Redis
目录
Redis介绍与安装
redis 缓存数据库, 大部分时间做缓存,不仅仅可以做缓存。
非关系型数据库
C语言写的服务,用来存储数据,数据存在内存中,取值,存值速度都非常快。
redis快的原因
1.纯内存操作
2.网络模型使用的IO多路复用(可以处理的请求数更多)
- 6.x 版本之前,是单进程单线程架构,没有线程进程间的切换,消耗的资源更少。
官网下载
源码包安装,不支持windos系统
redis.io/download/
redis.cn/download.html #中文网站
安装
mac 源码编译安装
linux 源码编译安装
windows 因epoll模型无法安装,但微软基于源码改动编译成了安装包
windows下载地址
下载msi文件
# 最新5.x版本
https://github.com/tporadowski/redis/releases/
# 最新3.x版本
https://github.com/microsoftarchive/redis/releases
安装完成后会释放两个命令,执行完毕会自动加到服务里
redis-server # 类似 mysqld 服务端启动
redis-cli # 类似 mysql 客户端启动
安装目录需要注意的文件
redis-server
redis-cli
redis.windows-service.conf 配置文件
-bind 127.0.0.1 # 服务跑在的地址
-port 6379 #监听的端口
启动redis
1. 服务中点击启动
后台启动
2. 使用命令
redis-server 指定配置文件 不指定 默认
客户端连接redis
1 命令
redis-cli # 默认连接本地6379端口
2 远程连接方式
redis-cli -h 127.0.0.1 -p 端口
存储方式
key : value形式存储,没有表
图形化客户端操作
可以百度搜
下载
Redis Desktop Manager 原来免费,现在收费了
https://www.52pojie.cn/forum.php?mod=viewthread&tid=1699654
图形化工具连接redis服务器
进入可以看到一共有16个库,但是我们只用一个db0. 可以在配置文件中设置产生库的数量。
python连接redis
下载模块
pip install redis
导入模块
from redis import Redis
实例化得到对象
conn=Redis(host='127.0.0.1',port=6379)
参数:
db=0 # 指定库
socket_timeout = None # 超时时间
encoding='utf-8' # 编码
设置值
conn.set('name','lxj')
使用conn对象操作redis
# 获取值
res=conn.get('name') # 返回的数据时bytes格式
print(res)
关闭
conn.close()
代码
# 1.导入模块
from redis import Redis
# 2.实例化得到对象
conn = Redis(host='127.0.0.1',port=6379)
# 3.操作redis
res = conn.get('name')
print(res)
# 4 设置值
ret =conn.set('age',19)
# 5.关闭链接
conn.close()
链接池链接
多线程情况下链接是取链接池的链接与redis操作,目的为了不创建太多的链接数。
补充
django mysql 默认不带链接池一个请求就是一个mysql连接
如果使用链接池的话可能会有问题,并发数过高,导致mysql连接数过高,影响mysql性能
django连接池相关文档
https://blog.51cto.com/liangdongchang/5140039
不使用链接池链接redis
import redis
from redis import Redis
from threading import Thread
def task():
# 实例化得到对象
conn = Redis(host='127.0.0.1',port=6379)
print(conn.get('name'))
# 开100个线程
for i in range(100):
t=Thread(target=task) # 每次都是一个新链接,会导致redis的链接数过多
t.start()
使用链接池
import redis
from threading import Thread
# 使用redis链接池
"""
max_connections = # 池的大小
"""
# 创建10个链接
POLL = redis.ConnectionPool(max_connections=10,host='127.0.0.1',port=6379)
def task():
# 从池中拿一个链接
conn = redis.Redis(connection_pool=POLL) # 会报错,线程过多池里链接不够。有一个参数可以设置等待时间( 后面再说 )
print(conn.get('name'))
# 开10个线程
for i in range(10):
t=Thread(target=task)
t.start()
把POLL做成单例性能更高,否则有可能在每一个线程中创建一个池,那就是10个线程x10个池,性能更低。要保证只有一个线程池
单例模式多线程链接池链接
创建一个py文件创建链接池
import redis
# 创建10个链接
POLL = redis.ConnectionPool(max_connections=10,host='127.0.0.1',port=6379)
导入使用
import redis
from threading import Thread
# 导入单例
from pool import POLL
def task():
# 从池中拿一个链接
conn = redis.Redis(connection_pool=POLL)
print(conn.get('name'))
# 开10个线程
for i in range(10):
t=Thread(target=task)
t.start()
Redis数据类型(value类型)
1.字符串 常用来做缓存 ,计数
2.列表 简单的消息队列
3.字典 (hash) 缓存
4.集合 去重
5.有序集合 常用来做排行榜
Redis之字符串类型
redis 是key-value形式存储,数据放在内存中,如果断电那么数据会丢失。 redis有持久化方案。
- Redis提供了不同的持久性选项:
- RDB
- AOF
相关文档
https://www.cnblogs.com/wen-xin/p/11838603.html
字符串操作
set 改值
set(name, value, ex=None, px=None, nx=False, xx=False)
"""
ex = 过期时间(秒)
px = 过期时间(毫秒)
nx = True name存在set不会执行 nx=False 就会修改 不填则都会执行
xx = True hobby存在才会修改,不存在则不修改,xx=False hobby 不存在也会会新增
"""
# 存储一个爱好,3秒过期
conn.set('hobby','raper',ex=3)
# 3000毫秒过期
conn.set('hobby','raper',px=3000)
# name字段已存在 nx为True则不会修改内容
conn.set('name','lxj',nx=True)
# nx=False 就会修改
conn.set('name','lxj',nx=False)
# xx=Ture hobby存在才会修改,不存在则不修改
conn.set('hobby','篮球',xx=True)
# xx=False hobby 不存在也会会新增
conn.set('hobby','上网',xx=False)
conn.close()
get 取值
get(name)
# 取出来是bytes格式,以字节为单位
conn.get('name')
# 强转字符串
str(conn.get('name'),encoding='utf-8')
strlen 统计长度
strlen(name)
# 统计字节长度
conn.strlen('hobby')
incr 自增
incr(self,name,amount=1)
# 自增 不传默认1
conn.incr('age')
incrby 同样的功能
了解
1.setnx(name,value)
等同于 conn.set('name','lxj',nx=True)
# 字段存在不会修改
conn.setnx('name','刘亦菲')
2.setex(name,value,time)
等同于conn.set('wife','牛',ex=3)
# 3秒后过期,中间参数是时间
conn.setex('wife',3,'牛牛牛')
3.mset(*args,**kwargs)
# 传字典,批量设置值
conn.mset('wife':'牛牛牛','hobby':'小动物')
4.mget(keys,*args)
# 批量获取值,一次可以取多个拿出来是个列表
conn.mget('name','wife')
5.getset(name,value)
# 把第一个参数的值拿出来res接收的是这个值,并把redis库中的hobby值换成value
res=conn.getset('hobby','打篮球')
6.getrange(key,start,end)
# 范围内取值,以字节为单位的长度,字节不够拿一个字符则报错
conn.getrange('wife',0,2)
7.setrange(name,offset,value)
# 从第二个字节开始插入bbb
conn.setrange('wife',2,'bbb')
比特位操作
setbit(name,offset,value)
getbit(name,offset)
bitcount(key,start=None,end=None)
8 incrbyfloat(self,name,amount=1.0)
# 自增小数,数字精度不高
conn.incrbyfloat('age',1.2)
9 decr(self,name,amount=1)
decrby同样功能
# 自减1 不能带小数
conn.decrby('age')
10.append(key,value)
# 在原本hobby的值后面加上sb
conn.append('hobby','sb')
redis数据类型之列表(value)
列表操作
1 lpush记住
lpush(name,value)
# l list缩写, 从左侧插入 第二个会把第一个抵过去所以就是第一个位置
conn.lpush('girs','lyf','迪丽热巴')
2.rpush
rpush(name,value)
# 从右侧插 最后
conn.rpush('girs','小红')
3.lpushx
lpushx(name,value)
# 有boys才会存,没有不存 左侧插入
conn.lpushx('boys','hg')
4.rpushx
rpushx(name,value)
# # 有boys才会存,没有不存 右侧插入
conn.lpushx('boys','wg')
5.llen记住
llen(name)
# 统计数据个数
res = conn.llen('girls')
print(res)
6.linsert
linsert(name,where,refvlue,value)
# 在迪丽热巴前面插进古力娜扎
conn.linsert('girls','before','迪丽热巴','古力娜扎')
# 在小红后面插上小绿
conn.linsert('girls','after','小红','小绿')
"refvlue参照数据必须有值才能插入"
-
lset
lset(name,index,value)
#把索引位1的数据修改成小黑
conn.lset('girs',1,'小黑')
8 lrem
lrem(name,num,value)
# 从左侧 删除1个xxx 负数从右侧开始删 0全删除
conn.lrem('girls',1,'xxx')
9 lpop记住
lpop(name)
# 左侧弹出 rpop 右侧弹
res = conn.lpop('girls')
-
lindex
lindex(name,index)
# 拿出索引位1的数据
res =conn.lindex('girls',1)
12 lrange记住
lrange(name,start,end)
# 拿出从索引位0开始到索引位1的数据
res = conn.lrange('girls',0,1)
13 ltrim
ltrim(name,start,end)
# 只剩第二个位置到第四个位置,索引位
conn.ltrim('girls',2,4)
14 rpoplpush
rpoplpush(src,dst)
#从一个列表取出最右边的元素,同时将其添加至另一个列表的最左边
"""
src,要取数据的列表的name
dst,要添加数据的列表的name
"""
conn.rpoplpush('girls','boys')
15 blpop
blpop(key,timeout)
# 左侧有值弹出,没值阻塞,等待有值(链接的同一个redis数据库不分机器)插入就弹出
conn.blpop('boys')
"""简单的消息队列,程序崩了数据没处理完会丢失"""
-
brpop
与上个同样从右边开始
17. brpoplpush
# 从一个列表的右侧移除一个元素并将其添加到另一个列表的左侧
参数:
src,取出并要移除元素的列表对应的name
dst,要插入元素的列表对应的name
timeout,当src对应的列表中没有数据时,阻塞等待其有数据的超时时间(秒),0 表示永远阻塞
redis数据类型之字典(hash)(value值)
字典操作
hset记住
hset(name, key, value)
# 创建一个userinfo 里有name:lqz ,如果存在则修改值
conn.hset('userinfo','name','lqz')
conn.hset('userinfo',mapping={'name':'lxj','age':18})
hget记住
hget(name,key)
# 获取userinfo的name对应的值
res=conn.hget('userinfo','name')
print(res)
hmget记住
hmget(name, keys, *args)
# 可以一次获取多个k对应的值
res=conn.hmget('userinfo',['name','age'])
res=conn.hmget('userinfo','name','age')
print(res)
hgetall(慎用)
hgetall(name, keys, *args)
# 获取name对应hash的所有键值,数据量过大可能会撑爆内存
res=conn.hgetall('userinfo')
print(res)
hlen记住
hlen(name)
# 获取name对应的hash中键值对的个数
res=conn.hlen('userinfo')
print(res)
hkeys
hkeys(name)
# 获取name对应的hash中所有的key的值
res=conn.hkeys('userinfo')
print(res)
hvals
hvals(name)
# 获取name对应的hash中所有的value的值
res=conn.hvals('userinfo')
print(res)
hexists
hexists(name, key)
# 检查name对应的hash是否存在当前传入的key,存在返回True 否则False
res=conn.hexists('userinfo','name')
print(res)
hdel记住
hdel(name,\*keys)
# 将userinfo里的age键值对删掉,返回0或1
res=conn.hdel('userinfo','age')
print(res)
hincrby
hincrby(name, key, amount=1)
# 将userinfo里age对应的值自增1
res=conn.hincrby('userinfo','age',1)
print(res)
hincrbyfloat
hincrbyfloat(name, key, amount=1.0)
# 自增小数
hscan
hscan(name, cursor=0, match=None, count=None)
# 增量式迭代获取,对于数据大的数据非常有用,hscan可以实现分片的获取数据,并非一次性将数据全部获取完,从而放置内存被撑爆
# 参数:
# name,redis的name
# cursor,游标(基于游标分批取获取数据)
# match,匹配指定key,默认None 表示所有的key
# count,每次分片最少获取个数,默认None表示采用Redis的默认分片个数
# 如:
# 第一次:cursor1, data1 = r.hscan('xx', cursor=0, match=None, count=None)
# 第二次:cursor2, data1 = r.hscan('xx', cursor=cursor1, match=None, count=None)
# ...
# 直到返回值cursor的值为0时,表示数据已经通过分片获取完毕
hscan_iter记住
hscan_iter(name, match=None, count=None)
# 利用yield封装hscan创建生成器,实现分批去redis中获取数据
# count还不知道有啥用
res=conn.hscan_iter('userinfo',count=1)
for i in res:
print(i)
conn.close()
redis其他操作
通用操作
delete
delete(*names)
# 删除数据 删除userinfo
conn.delete('userinfo')
# 删除userinfo,userinfo2
conn.delete(*['userinfo','userinfo2'])
exists
exists(name)
# 判断是否存在 返回1或0
conn.exists('userinfo')
keys
keys(pattern='*')
# 拿出库中所有k
conn.keys('*')
# 含有t的
conn.keys('*t')
"*表示多个字符,?表示一个字符"
expire
expire(name,time)
# userinfo 3秒过期
conn.expire('userinfo',3)
rename
rename(src,dst)
# hobby改名成hobby111
conn.rename('hobby','hobby111')
move
move(name,db)
# 把hobby移到8库中
conn.move('hobby',8)
randomkey
randomkey()
# 随机弹出一个k
res=conn.randomkey()
type
type(name)
# 查看hobby的数据类型
conn.type('hobby')
redis数据类型操作详细博客
https://www.cnblogs.com/liuqingzheng/p/9833534.html
redis管道
redis单实例并基于管道才能支持事物.(单实例才能用管道)
测试代码
import redis
conn= redis.Redis()
# 张三金额减100
conn.decr('zhangsan_je',100)
raise Exception('程序崩溃了')
# 李四金额加一百
conn.incr('lisi_je',100)
conn.close()
"程序中间崩了,张三钱扣了,李四没加钱"
# 管道
import redis
conn= redis.Redis()
# pipeline管道,transaction=True开启事物
p= conn.pipeline(transaction=True)
p.multi()
# 张三金额减100
p.decr('zhangsan_je',100)
raise Exception('程序崩溃了')
# 李四金额加一百
p.incr('lisi_je',100)
p.execute()
conn.close()
"程序崩了会都不会有变化"