Redis在Python中的使用
一 Redis 介绍
Redis 是一个高性能的 key-value 数据格式的内存缓存,NoSQL 数据库。NOSQL:not only sql,泛指非关系型数据库.关系型数据库:
序号 | 关系数据库(mysql, oracle, sql server, sqlite) | 非关系数据库(Redis) |
---|---|---|
1 | 数据存放在表中,表之间有关系。 | 没有数据表的概念,不同的nosql数据库存放数据位置不同。 |
2 | 通用的SQL操作语言。 | nosql数据库没有通用的操作语言。 |
3 | 大部分支持事务。 | 基本不支持事务. redis支持简单事务 |
Redis 优点
- 异常快速 : Redis 是非常快的,每秒可以执行大约 110000 设置操作,81000 个 / 每秒的读取操作。
- 支持丰富的数据类型 : Redis 支持最大多数开发人员已经知道如列表,集合,可排序集合,哈希等数据类型。
- 原子 – Redis 的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过 MULTI 和 EXEC 指令包起来。
- Redis 是一个多功能实用工具,可以在很多如,消息传递队列中使用(Redis 原生支持发布 / 订阅)
- 单线程特性,秒杀系统,基于 redis 是单线程特征,防止出现数据库 “爆破”
redis 的典型应用
(一)性能
Redis 中缓存热点数据,能够保护数据库,提高查询效率。如下图所示,我们在碰到需要执行耗时特别久,且结果不频繁变动的 SQL,就特别适合将运行结果放入缓存。这样,后面的请求就去缓存中读取,使得请求能够迅速响应。
(二)并发
还是如上图所示,在大并发的情况下,所有的请求直接访问数据库,数据库会出现连接异常。这个时候,就需要使用 redis 做一个缓冲操作,让请求先访问到 redis,而不是直接访问数据库。
二 Python 操作 Redis
2.1连接方式
1,操作模式
import redis
# 方式1
r = redis.Redis(host='192.168.211.133', port=6379, password='foobared')
# 方式二 StrictRedis是Redis的子类 用于向后兼容旧版本的 redis-py
# r = redis.StrictRedis(host='192.168.211.133', port=6379, password='foobared')
r.set('foo', 'Bar')
print(r.get('foo'))
2,连接池
import redis
#避免每次建立、释放连接的开销
pool = redis.ConnectionPool(host='192.168.211.133', port=6379, password='foobared')
r = redis.Redis(connection_pool=pool)
print(r.get("age")) # b'99'
2.2操作
1. string类型:
字符串类型是 Redis 中最为基础的数据存储类型,它在 Redis 中是二进制安全的,也就是byte类型
最大容量是512M。
2. hash类型:
hash用于存储对象,对象的结构为属性、值,值的类型为string。
key:{
域:值[这里的值只能是字符串],
域:值,
域:值,
域:值,
...
}
3. list类型:
列表的元素类型为string。
key:[ 值1,值2,值3..... ]
4. set类型:
无序集合,元素为string类型,元素唯一不重复,没有修改操作。
{值1,值4,值3,值5}
5. zset类型:
有序集合,元素为string类型,元素唯一不重复,没有修改操作。
2.2.1 String 操作
字符串类型是 Redis 中最为基础的数据存储类型,它在 Redis 中是二进制安全的,也就是byte类型,最大容量是512M
redis 中的 String 在在内存中按照一个name对应一个 value 来存储.如图:
set(name, value, ex=None, px=None, nx=False, xx=False)
#在Redis中设置值,默认,不存在则创建,存在则修改
#参数:
#ex,过期时间(秒)
#px,过期时间(毫秒)
#nx,如果设置为True,则只有name不存在时,当前set操作才执行
#xx,如果设置为True,则只有name存在时,岗前set操作才执行
r.set("name","alex",5)
setnx(name, value)
# 设置值,只有name不存在时,执行设置操作(添加)
setex(name, value, time)
# 过期时间(秒)
psetex(name, time_ms, value)
# px,过期时间(毫秒)
mset(*args, **kwargs)
# 批量设置值
mset(k1='v1', k2='v2')
mget({'k1': 'v1', 'k2': 'v2'})
get(name)
# 获取值
mget(keys, *args)
# 批量获取
mget('ylr', 'wupeiqi')
r.mget(['ylr', 'wupeiqi'])
getset(name, value)
# 设置新值并获取原来的值
getrange(key, start, end)
# 获取子序列(根据字节获取,非字符)
r.getrange("name",0,2)
setrange(name, offset, value)
# 修改字符串内容,从指定字符串索引开始向后替换(新值太长时,则向后添加)
# 参数:
# offset,字符串的索引,字节(一个汉字三个字节)
# value,要设置的值
r.setrange("name",3,"!!!")
strlen(name)
# 返回name对应值的字节长度(一个汉字3个字节)
r.strlen("name")
incr(self, name, amount=1)
# 自增 name对应的值,当name不存在时,则创建name=amount,否则,则自增自增数(必须是整数)
# 参数:
# name,Redis的name
# amount,自增数(必须是整数)
# 注:同incrby
incrbyfloat(self, name, amount=1.0)
# 自增 name对应的值,当name不存在时,则创建name=amount,否则,则自增。
# 参数:
# name,Redis的name
# amount,自增数(浮点型)
decr(self, name, amount=1)
# 自减 name对应的值,当name不存在时,则创建name=amount,否则,则自减。
# 参数:
# name,Redis的name
# amount,自减数(整数)
append(key, value)
# 在redis name对应的值后面追加内容
2.2.2 Hash 操作
什么是哈希表
哈希表也叫散列表,是存储Key-Value映射的集合.对于某一个Key,哈希表可以在接近O(1)的时间内进行读写操作,哈希表通过哈希函数实现Key和数组下标的转换,通过开放寻址法和链表法来解决哈希冲突
hash 表现形式上有些像pyhton 中的dict, 可以存储一组关联性较强的数据,redis 中 Hash 在内存中的存储格式如下图:
hset(name, key, value)
#name对应的hash中设置一个键值对不存在,则创建;否则,修改
hsetnx(name, key, value)
#当name对应的hash中不存在当前key时则创建(相当于添加)
hmset(name, mapping)
# 在name对应的hash中批量设置键值对
r.hmset('xx', {'k1':'v1', 'k2': 'v2'})
hget(name,key)
# 在name对应的hash中获取根据key获取value
r.hget("infos","name")
hmget(name, keys, *args)
#在name对应的hash中获取多个key的值
r.hmget("infos",["name","age"]
hgetall(name)
获取name对应hash的所有键值
r.hgetall("infos")
hlen(name)
# 获取name对应的hash中键值对的个数
r.hlen("infos")
hkeys(name)
#获取name对应的hash中所有的key的值
r.hkeys("infos") # [b'name', b'age', b'gender']
hvals(name)
# 获取name对应的hash中所有的value的值
r.hvals("infos") # [b'alex', b'1000', b'male']
hexists(name, key)
# 检查name对应的hash是否存在当前传入的key
r.hexists("infos","names")
hdel(name,*keys)
# 将name对应的hash中指定key的键值对删除
r.hdel("infos","gender")
hincrby(name, key, amount=1)
# 自增name对应的hash中的指定key的值,不存在则创建key=amount
r.hincrby("infos","age",2)
hincrbyfloat(name, key, amount=1.0)
# 自增name对应的hash中的指定key的值,不存在则创建key=amount amount,自增数(浮点数)
r.hincrby("infos","age",2.0)
hscan_iter(name, match=None, count=None)
# 利用yield封装hscan创建生成器,实现分批去redis中获取数据
# 参数:
# match,匹配指定key,默认None 表示所有的key
# count,每次分片最少获取个数,默认None表示采用Redis的默认分片个数
r.hscan_iter("infos")
for i in r.hscan_iter("infos"):
print(i)
2.2.3 List 操作
List 操作,redis 中的 List 在在内存中按照一个name 对应一个 List 来存储。如图:
lpush(name,values)
# 在name对应的list中添加元素,每个新的元素都添加到列表的最左边
r.lpush('oo', 11,22,33) # 保存顺序为: 33,22,11
# rpush(name, values) 表示从右向左操作
lpushx(name,value)
# 在name对应的list中添加元素,只有name已经存在时,值添加到列表的最左边
r.lpushx("scores",100)
# rpushx(name, value) 表示从右向左操作
llen(name)
# name对应的list元素的个数
r.llen("scores")
linsert(name, where, refvalue, value))
# 在name对应的列表的某一个值前或后插入一个新值
# 参数:
# name,redis的name
# where,BEFORE或AFTER
# refvalue,标杆值,即:在它前后插入数据
# value,要插入的数据
r.linsert("scores","AFTER","34","44")
r.lset(name, index, value)
# 对name对应的list中的某一个索引位置重新赋值
# 参数:
# name,redis的name
# index,list的索引位置
# value,要设置的值
r.lset("scores",1,98)
r.lrem(name, value, num)
# 在name对应的list中删除指定的值
# 参数:
# name,redis的name
# value,要删除的值
# num
# num=0,删除列表中所有的指定值
# num=2,从前到后,删除2个;
# num=-2,从后向前,删除2个
r.lrem("scores",count=0,value=98)
lpop(name)
# 在name对应的列表的左侧获取第一个元素并在列表中移除,返回值则是第一个元素
# 更多
# rpop(name) 表示从右向左操作
r.lpop("scores")
lindex(name, index)
# 在name对应的列表中根据索引获取列表元素
r.lindex("scores",3)
lrange(name, start, end)
# 在name对应的列表分片获取数据
# 参数:
# name,redis的name
# start,索引的起始位置
# end,索引结束位置
r.lrange("scores",0,-1)
ltrim(name, start, end)
# 在name对应的列表中移除没有在start-end索引之间的值
# 参数:
# name,redis的name
# start,索引的起始位置
# end,索引结束位置
r.ltrim("scores",1,3)
2.2.4 Set 操作
Set 操作,Set 无序集合,元素为string类型,元素唯一不重复,没有修改操作,{值1,值4,值3,值5}
sadd(name,values)
# name对应的集合中添加元素
r.sadd("score_set01",1,2,3,4,5,6,6)
scard(name)
# 获取name对应的集合中元素个数
r.scard("score_set01")
sdiff(keys, *args)
# 在第一个name对应的集合中且不在其他name对应的集合的元素集合
sinter(keys, *args)
# 获取多一个name对应集合的交集
r.sinter("score_set01","score_set02")
# 并集
r.sunion("score_set01","score_set02")
# 差集
r.sdiff("score_set01","score_set02")
sismember(name, value)
# 检查value是否是name对应的集合的成员
r.sismember("score_set01",6)
smembers(name)
# 获取name对应的集合的所有成员
r.smembers("score_set01")
spop(name)
# 从集合中随机移除一个成员,并将其返回
r.spop("score_set01")
srandmember(name, numbers)
# 从name对应的集合中随机获取numbers个元素
r.srandmember("score_set01",3)
srem(name, values)
# 在name对应的集合中删除某些值
r.srem("score_set01",5)
sunion(keys, *args)
# 获取多一个name对应的集合的并集
r.sunion("score_set01","score_set02")
sscan_iter(name, match=None, count=None)
# 同字符串的操作,用于增量迭代分批获取元素,避免内存消耗太大
for i in r.sscan_iter("score_set01"):
print(i)
2.2.5 Sort Set 操作
有序集合,在集合的基础上,为每元素排序,元素的排序需要根据另外一个值来进行比较,所以,对于有序集合,每一个元素有两个值,即:值和分数,分数专门用来做排序。
zadd(name, *args, **kwargs)
# 在name对应的有序集合中添加元素
r.zadd("zz",{"n1":1,"n2":2,"n3":3,"n4":4})
# 查看
r.zscan("zz")
zcard(name)
# 获取name对应的有序集合元素的数量
r.zscan("z")
zcount(name, min, max)
# 获取name对应的有序集合中分数在[min,max]之间的个数
r.zcount("z",1,3)
zincrby(name, value, amount)
#自增name对应的有序集合的name对应的分数
r.zincrby("z",2,"n3")
zrange(name, start, end, desc=False, withscores=False, score_cast_func=float)
# 按照索引范围获取name对应的有序集合的元素
'''参数:
name redis的name
start 有序集合索引起始位置
end 有序集合索引结束位置
desc 排序规则,默认按照分数从小到大排序
withscores 是否获取元素的分数,默认只获取元素的值
score_cast_func 对分数进行数据转换的函数
'''
r.zrange("zset_name",0,1,desc=False,withscores=True,score_cast_func=int)
r.zrange("z",0,2)
zscore(name, value)
#获取name对应有序集合中value对应的分数
r.zscore("z","n4")
zrank(name, value)
# 获取某个值在name对应的有序集合中的排行(从0开始)
r.zrank("z","n4")
# 更多:
# zrevrank(name, value),从大到小排序
zrem(name, values)
# 删除name对应的有序集合中值是values的成员
zrem('zz', ['s1', 's2'])
zremrangebyrank(name, min, max)
# 根据排行范围删除
r.zremrangebyrank("z",0,1)
zremrangebyscore(name, min, max)
# 根据分数范围删除
r.zremrangebyscore("z",1,7)
zinterstore(dest, keys, aggregate=None)
# 获取两个有序集合的交集,如果遇到相同值不同分数,则按照aggregate进行操作
# aggregate的值为: SUM MIN MAX
zunionstore(dest, keys, aggregate=None)
# 获取两个有序集合的并集,如果遇到相同值不同分数,则按照aggregate进行操作
# aggregate的值为: SUM MIN MAX
2.2.6 其他常用操作
delete(*names)
# 根据删除redis中的任意数据类型
r.delete("naem")
exists(name)
# 检测redis的name是否存在
r.exists("name")
keys(pattern=’*’)
# 根据模型获取redis的name
r.keys(pattern="k*")
# 更多:
# KEYS * 匹配数据库中所有 key 。
# KEYS h?llo 匹配 hello , hallo 和 hxllo 等。
# KEYS h*llo 匹配 hllo 和 heeeeello 等。
# KEYS h[ae]llo 匹配 hello 和 hallo ,但不匹配 hillo
expire(name ,time)
# 为某个redis的某个name设置超时时间
r.expire("name",10)
rename(src, dst)
# 对redis的name重命名为
randomkey()
# 随机获取一个redis的name(不删除)
r.randomkey()
type(name)
# 获取name对应值的类型
r.type("infos")
scan_iter(match=None, count=None)
# 同字符串操作,用于增量迭代获取key
for i in r.scan_iter(match="k*"):
print(i)
2.2.7 使用场景
针对各种数据类型使用场景如下:
类型 | 作用 |
---|---|
String | 最常规的 set/get 操作,value 可以是 String 也可以是数字。一般做一些复杂的计数功能的缓存,比如减少库存 |
hash | 这里 value 存放的是结构化的对象,比较方便的就是操作其中的某个字段。博主在做单点登录的时候,就是用这种数据结构存储用户信息,以 cookieId 作为 key,设置 30 分钟为缓存过期时间,能很好的模拟出类似 session 的效果。 |
list | 使用 List 的数据结构,可以做简单的消息队列的功能。另外还有一个就是,可以利用 lrange 命令,做基于 redis 的分页功能,性能极佳,用户体验好.本人还用一个场景,很合适 — 取行情信息.就也是个生产者和消费者的场景.LIST 可以很好的完成排队,先进先出的原则 |
set | 因为 set 堆放的是一堆不重复值的集合。所以可以做全局去重的功能。另外,就是利用交集、并集、差集等操作,可以计算共同喜好,全部的喜好,自己独有的喜好等功能。 |
sorted set | sorted set 多了一个权重参数 score, 集合中的元素能够按 score 进行排列。可以做排行榜应用,取 TOP N 操作 |
2.3 管道
redis-py 默认在执行每次请求都会创建(连接池申请连接)和断开(归还连接池)一次连接操作,如果想要在一次请求中指定多个命令,则可以使用 pipline 实现一次请求指定多个命令,并且默认情况下一次 pipline 是原子性操作。
import redis
pool = redis.ConnectionPool(host='10.211.55.4', port=6379)
r = redis.Redis(connection_pool=pool)
# pipe = r.pipeline(transaction=False)
pipe = r.pipeline(transaction=True)
pipe.set('name', 'alex')
pipe.set('role', 'sb')
pipe.execute()
2.4 发布订阅
订阅者:
import redis
r=redis.Redis(host='127.0.0.1')
pub=r.pubsub()
pub.subscribe("fm104.5")
pub.parse_response()
while 1:
msg = pub.parse_response()
print(msg)
发布者:
import redis
r=redis.Redis(host='127.0.0.1')
r.publish("fm104.5", "Hi,yuan!")
发布订阅的特性用来做一个简单的实时聊天系统再适合不过了,当然这样的东西开发中很少涉及到。
再比如在分布式架构中,常常会遇到读写分离的场景,在写入的过程中,就可以使用 redis 发布订阅,使得写入值及时发布到各个读的程序中,就保证数据的完整一致性.再比如,在一个博客网站中,有 100 个粉丝订阅了你,当你发布新文章,就可以推送消息给粉丝们拉。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix