python redis缓存快速上手教程
Redis
[TOC]
终极策略:使用Redis的String类型做的事,都可以用Memcached替换,以此换取更好的性能提升; 除此以外,优先考虑Redis
Redis安装和启动
安装
Linux的安装:apt install redis-server
Windows安装:windows解压包
直接运行
.zip
包内的redis-cli.exe
文件连接到本地服务器
启动
Windows下启动Redis服务器:
- 如果是通过
.msi
安装的会自动的通过服务启动Redis
服务 - 如果是通过
.zip
解压的直接手动运行redis-server.exe
启动服务
linux下启动Redis服务器:
# 启动Redis服务器
systemctl restart redis
# 查看redis是否开启
ps -ef | grep redis
# 启动Redis客户端
redis-cli
# 手动重启服务
redis-cli shutdown
redis-server /etc/redis/redis.conf
配置
配置密码
#>>> vim /etc/redis.conf
# 去掉前面的注释,并修改后面的密码
requirepass passwd123
- 使用密码登陆redis:
redis-cli -h 127.0.0.1 -p 6379 -a passwd123
不添加-a参数虽然也可以登录成功,但是没有任何操作权限。需要使用
auth myPassword
登陆。
登陆后查看密码:
config get requirepass
在命令行临时修改密码:
config set requirepass newPassword
在Redis集群中使用认证密码
如果Redis服务器,使用了集群。除了在
master
中配置密码外,也需要在slave
中进行相应配置。在slave
的配置文件中找到如下行,去掉注释并修改与master
相同的密码即可:# masterauth master-password
Redis数据类型
- 字符串 String
- 散列/哈希 Hash
- 列表 List
- 集合 Set
- 可排序集合 ZSet
- 位图 Bitmap
命令
常用命令
# 查看所有的变量
keys *
# 查看某个变量类型
type username
# 清空所有数据
flushall
字符串命令
# 设置键/值
set username "xionglilong"
# 得到某个键的值
get username
# 得到某个键的子串
getrange username 2 5
# 得到键长度
strlen username
set count "3"
# 给键加1
incr count
# 给键减1
decr count
# 给字符串附加其他字符串
append count "xiong"
哈希命令
# 在username_dict字典里,设置键为'xiong',值为'xionglilong'
hset username_dict xiong "xionglilong"
# 得到username_dict字典中xiong的值
hset username_dict xiong
# 得到username_dict字典中所有键值对
hgetall username_dict
# 判断username_dict字典中xiong键是否存在
hexists username_dict xiong
# 删除username_dict字典中xiong这个键
hdel username_dict xiong
# 获取username_dict字典中所有键
hkeys username_dict
# 获取username_dict字典中所有值
hvals username_dict
列表命令
# 向username_list列表左边添加"xiong"这个值
lpush username_list "xiong"
# 向username_list列表右边添加"zhang"这个值
rpush username_list "zhang"
# 获取0-10的位置的值
lrange username_list 0 10
# 从列表左侧弹出一个值,超时时间为3秒,b=block(阻塞),brpop为右侧
blpop username_list 3
# 从列表右侧弹出一个值(非阻塞,没有就返回)
rpop username_list
# 列表的长度
llen username_list
# 取出username_list列表的第一元素
lindex username_list 0
集合命令
#在username_set集合内添加一个元素(不可重复)
sadd username_set "xiong"
# 获取元素总数
scard username_set
# 前面的set减去后面的set
sdiff username_set username2_set
# 两个set求交集
sinter username_set username2_set
# 随机弹出一个值
spop username_set
# 随机获取三个值
srandmember username_set 3
# # 获取集合所有元素
smembers username_set
可排序集合
# 向username_set集合添加值并设置值的分数
zadd username_set 0 "xiong" 5 "zhang" 10 "huang"
# 获取username_set集合中0-5分的值
zrangebyscore username_set 0 5
# 获取username_set集合0-5分的值的总数
zcount username_set 0 5
python调用
pip install redis
连接
import redis
# 第一种方式连接:(会自动创建连接池,推荐使用)
r = redis.Redis(host='127.0.0.1', port=6379, password='ps123', db=0, decode_responses=True)
# 第二种方式连接: (手动创建连接池)
redis_pool = redis.ConnectionPool(host='127.0.0.1', port=6379, password='ps123', db=0, decode_response=true) # 建立redis连接池
r = redis.Redis(connection_pool=pool)
当程序创建数据源实例时,系统会一次性创建多个数据库连接,并把这些数据库连接保存在连接池中,当程序需要进行数据库访问时,无需重新新建数据库连接,而是从连接池中取出一个空闲的数据库连接。实现多个Redis实例共享一个连接池。 目前新版中初始化默认就定义了连接池,可以不自己定义 **注意:**默认情况下,设置的值或取得的值都为bytes类型,如果想改为str类型,需要在连接时添加上decode_responses=True db=0: redis有0-15 共计16个数据库 默认是0 可以根据不同类型数据存放到不同的数据库下
python redis基本操作
import redis
r = redis.Redis(host='localhost', port=6379, password='pass', decode_responses=True)
r.set('name', 'xionglilong') # ex是过期时间,3秒后'name'键的值就变成None
r.exists('name') # 判断值是否存在
r.delete('name') # 删除值
r.keys(pattern='') # 模糊匹配
r.expire('name', time=3) # 设置超时时间
r.rename('name', 'username') # 重命名
r.type(name) # 获取类型
r.randomkey() # 随机获取一个值`
r.dbsize() # 查看总数
r.save() # 将数据写回磁盘。保存时阻塞
r.flushdb() # 清空
r.set('name', 'xionglilong', ex=3) # ex是过期时间,3秒后'name'键的值就变成None
r.set('name', 'xionglilong', nx=True) # ex是仅新建,如果不存在该键才执行
r.set('name', 'xionglilong', xx=True) # xx是仅修改,如果键存在才执行
r.mset({'username': 'xiong', 'password': '123'}) # 一次设置多个键值(不能设置过期时间)
r.msetnx({'username': 'xiong', 'password': '123'}) # 一次设置多个键值(不能设置过期时间),即使只有一个键已经存在,也不会执行任何操作
r.mget(['username', 'password']) # 一次查看多个键值
r.mget('username', 'password') # 一次查看多个键值
r.exists('username') # 判断是否存在
r.strlen('username') # 查看对应的字节长度(一个汉字3个字节)
r.getset('name', 'xiong2') # 先获取该值,再设置一个新的值
r.incr("count", amount=1) # 自增,仅能在整型字符串上设置。amount为自增的数量,默认为1。如果键不存在则设置为amount
r.decr("count", amount=1) # 自减,同上
r.incrbyfloat("count", amount=0.1) # 自增,同上,区别是这个是浮点
r.append('password', '123') # 在键对应的值后面增加内容
type(r.get('name')) # 查看类型
# ------------ 增加 ---------------
r.lpush('list1', *['23','25','27']) # 列表左边逐个添加
r.rpush('list2', *['77','23','68']) # 列表右边逐个添加
r.linsert(name='list1', where='before', refvalue='25', value='03') # 在list1列表中找到第一个'25',在它前面插入一个'03'
r.lset(name='list1', index=2, value='77') # 在list1列表的指定索引的值修改为77
r.rpoplpush("list1", "list2") # 弹出第一个列表最右边的值,推送到第二个列表最左边
r.brpoplpush("list1", "list2", timeout=2) # 同上。timeout为当第一个列表没有数据时,阻塞等待多少秒,0表示永远阻塞
# ---------- 查看 -------
r.lpop('list1') # 列表左弹出
r.rpop('list1') # 列表右弹出
r.blpop(['list1', 'list2', 'list3'], timeout=2) # 将多个列表排列好,左弹出。timeout是阻塞超时时间
r.brpop(['list1', 'list2', 'list3'], timeout=2) # 同上,右弹出
r.lindex('list1', 2) # 列表根据索引查看
r.lrange('list1', 0, 4) # 列表切片查看
r.lrange('list1', 0, -1) # 查看列表中所有值
r.llen('list1') # 查看列表长度
# 循环取出列表值
def list_iter(name):
for index in range(r.llen(name)):
yield r.lindex(name, index)
for item in list_iter('list1'):
print(item)
# ----- 删除 ------
r.lrem('list1', '27', num=2) # 删除指定值,并删除几次
r.ltrim('list1', 2, 5) # 删除切片之外的值
# ---- 增加 ------
r.hset('userinfo', 'zhangsan', '18')
r.hset('userinfo', 'lisi', '23')
r.hset('userinfo', mapping={'wangwu': '33', 'zhangfei':'22'}) # 批量设置
r.hsetnx('userinfo', 'lisi', '27') # 仅能新建
r.hinciby('userinfo', 'zhangsan', amount=1) # 自增整数
r.hincibyfloat('userinfo', 'zhangsan', amount=0.1) # 自增浮点
# ---- 查看 ------
r.hget('userinfo', 'zhangsan') # 取单个值
r.hmget('userinfo', 'zhangsan', 'lisi', ['wangwu', 'zhangfei']) # 取多个值
r.hmget('userinfo', ['wangwu', 'zhangfei']) # 取多个值
r.hkeys('userinfo') # 取出字典所有键名,返回键名的列表
r.hvals('userinfo') # 取出字典所有值,返回值得列表
r.hgetall('userinfo') # 取出字典所有键值对,返回字典
r.hexists('userinfo', 'wanger') # 判断键是否存在
r.hlen('userinfo') # 查看字典长度
# 分片读取
cursor1, data1 = r.hscan('userinfo', cursor=0, match=None, count=None)
cursor2, data1 = r.hscan('userinfo', cursor=cursor1, match=None, count=None)
# 循环读取
for item in r.hscan_iter('userinfo'):
print(item)
# ---- 删除 ------
r.hdel('userinfo', 'zhangsan', 'lisi') # 删除字典中的键值对
r.sadd('set1', *['23', '73', '88']) # 重复值只会保留一个
r.scard('set1') # 获取个数
r.smembers('set1') # 获取所有值
r.sismember('set1', '35') # 判断是否存在
r.spop('set1') # 随机弹出
r.srem('set1', '23') # 删除指定值
# 循环读取
for i in r.sscan_iter('set1'):
print(i)
r.sdiff('set1', 'set2') # 差集
r.inter('set1', 'set2') # 交集
r.sunion('set1', 'set2') # 并集
https://www.runoob.com/w3cnote/python-redis-intro.html
r.zadd('zset1', mapping={'zhangsan':0, 'lisi': 44}) # 增加
r.zcard('zset1') # 获取长度
r.zcount('zset1', 3, 7) # 统计在分数范围的值个数
r.zrange('zset1', 0, -1) # 获取所有值
r.zrange('zset1', 0, -1, withscores=True, desc=False) # 获取所有值和排序分数
r.zrank('zset1', 'zhangsan') # 获取某个值的索引号
r.zscore('zset1', 'zhangsan') # 获取某个值的分数
r.zrangebyscore('zset1', 3, 7) # 在分数范围中取出符合条件的值
r.zrangebyscore('zset1', 3, 7, withscores=True) # 在分数范围中取出符合条件的值和分数
r.zincrby('zset1', 1, 'zhangsan') # 将某个值得分数自增一个
r.zrem('zset1', 'zhangsan') # 删除值
r.zremrangebyrank('zset1' ,0 ,10) # 根据索引范围删除
# 循环取出
for i in r.zscan_iter('zset1'):
print(i)
# 切片循环取出(注意如果取完了会从头开始取,通过cursor==0判断是否取完)
cursor = "0"
while cursor!=0:
cursor, data = r.zscan('ip_set', cursor=cursor, count=100)
提示
常见参数:
- nx: 仅新建,如果不存在该键才执行
- xx: 仅修改,如果键存在才执行
管道
日常使用中一般不会用到管道
一般情况下,执行一条命令后必须等待结果才能输入下一次命令,管道用于在一次请求中执行多个命令。
redis-py默认在执行每次请求都会创建(连接池申请连接)和断开(归还连接池)一次连接操作。
如果想要在一次请求中指定多个命令,则可以使用pipline实现一次请求指定多个命令,并且默认一次pipline 是原子性操作。
transaction:指示是否所有的命令应该以原子方式执行,要使用命令缓冲,但禁止pipeline 的原子操作属性,关掉 transaction(transaction=False
)
import redis
pool = redis.ConnectionPool(host='192.168.0.110', port=6379)
r = redis.Redis(connection_pool=pool)
pipe = r.pipeline(transaction=True)
r.set('name', 'zhangsan')
r.set('name', 'lisi')
pipe.execute()
发布和订阅
首先定义一个RedisHelper类,连接Redis,定义频道为monitor,定义发布(publish)及订阅(subscribe)方法。
import redis
class RedisHelper(object):
def __init__(self):
self.__conn = redis.Redis(host='192.168.0.110',port=6379)#连接Redis
self.channel = 'monitor' #定义名称
def publish(self,msg):#定义发布方法
self.__conn.publish(self.channel,msg)
return True
def subscribe(self):#定义订阅方法
pub = self.__conn.pubsub()
pub.subscribe(self.channel)
pub.parse_response()
return pub
发布者:
#发布
from RedisHelper import RedisHelper
obj = RedisHelper()
obj.publish('hello')#发布
订阅者:
from RedisHelper import RedisHelper
obj = RedisHelper()
redis_sub = obj.subscribe()#调用订阅方法
while True:
msg= redis_sub.parse_response()
print (msg)
发布和订阅2
发布方:
import redis
r=redis.Redis(host="localhost",port=6379,decode_responses=True)
#发布使用publish(self, channel, message):Publish ``message`` on ``channel``.
Flag=True
while Flag:
msg=input("主播请讲话>>:")
if len(msg)==0:
continue
elif msg=='quit':
break
else:
r.publish('cctv0',msg)
订阅方:
import redis
r=redis.Redis(host="localhost",port=6379,decode_responses=True)
#发布使用publish(self, channel, message):Publish ``message`` on ``channel``.
Flag=True
chan=r.pubsub()#返回一个发布/订阅对象
msg_reciver=chan.subscribe('cctv0')#订阅
msg=chan.parse_response()#第一次会返回订阅确认信息
print(msg)
print("订阅成功,开始接收------")
while Flag:
msg=chan.parse_response()#接收消息
print(">>:",msg[2])#此处的信息格式['消息类型', '频道', '消息'],所以使用[2]来获取
事务
python中可以使用管道来代替事务:
import redis,time
import redis.exceptions
r=redis.Redis(host='localhost',port=6379,decode_responses=True)
pipe=r.pipeline()
print(r.get('a'))
try:
# pipe.watch('a')
pipe.multi()
pipe.set('here', 'there')
pipe.set('here1', 'there1')
pipe.set('here2', 'there2')
time.sleep(5)
pipe.execute()
except redis.exceptions.WatchError as e:
print("Error")