Python实现操作Redis的高级用法分享
作者:CodeDevMaster 发布时间:2022-07-29 04:26:29
redis-py
redis-py是Python操作Redis的第三方库,它提供了与Redis服务器交互的API。
GitHub地址:https://github.com/redis/redis-py
安装redis-py
pip install redis
基本使用
import redis
# 连接到本地Redis服务器,默认端口为6379
r = redis.Redis(host='localhost', port=6379, db=0)
# 设置键值对
r.set('hello', 'world')
# 获取键对应的值
value = r.get('hello')
# 输出 b'world'
print(value)
# 批量设置键值对
r.mset({'foo': '1', 'bar': '2'})
# 批量获取键对应的值
values = r.mget(['foo', 'bar'])
# 输出 [b'1', b'2']
print(values)
也可以使用StrictRedis对象连接redis服务器,StrictRedis类基于Redis类实现。
import redis
# 连接到本地Redis服务器,默认端口为6379
r = redis.StrictRedis(host='localhost', port=6379, db=0)
# 设置键值对
r.set('hello', 'world')
# 获取键对应的值
value = r.get('hello')
print(value) # 输出 b'world'
增删改查
以操作String数据类型的增删改查为例。
# 引⼊模块
import redis
if __name__ == '__main__':
try:
# 创建Redis对象
r = redis.Redis(host='localhost', port=6379, db=0)
# 新增,添加成功则返回True,如果添加失败则返回False
result = r.set('name', 'test')
print('是否新增成功:', result)
# 获取,如果键存在则返回对应的值,如果键不存在则返回None
name = r.get('name')
print('查询结果:', name)
# 修改,如果键已经存在则进⾏修改,如果键不存在则进⾏添加
result = r.set('name', 'redis')
print('是否修改成功:', result)
name = r.get('name')
print('查询结果:', name)
# 获取所有的键
result = r.keys()
print('获取所有的键', result)
# 删除,删除键及对应的值,如果删除成功则返回受影响的键数,否则则返 回0
result = r.delete('name')
print('删除key的数量:', result)
except Exception as e:
print(e)
字符串操作
import redis
# 连接到本地Redis服务器,默认端口为6379
r = redis.StrictRedis(host='localhost', port=6379, db=0)
# 设置键值对,键为'foo',值为'bar'
r.set('foo', 'bar')
# 获取键对应的值
value = r.get('foo')
print(value) # 输出 b'bar'
# 批量设置键值对
r.mset({'apple': 'red', 'banana': 'yellow'})
# 批量获取键对应的值
values = r.mget(['apple', 'banana'])
print(values) # 输出 [b'red', b'yellow']
# 获取部分值
part_value = r.getrange('foo', 0, 1)
print(part_value) # 输出 b'ba'
# 追加字符串
r.append('foo', 'baz')
append_value = r.get('foo')
print(append_value) # 输出 b'barbaz'
# 自增计数器
r.incr('counter')
# 获取计数器的值
value = r.get('counter')
print(value) # 输出 b'1'
# 在自增计数器的基础上再加上5
r.incrby('counter', 5)
# 获取计数器的值
value = r.get('counter')
print(value) # 输出 b'6'
# 减少计数器
r.decr('counter')
# 获取计数器的值
value = r.get('counter')
print(value) # 输出 b'5'
哈希操作
import redis
# 连接到本地Redis服务器,默认端口为6379
r = redis.StrictRedis(host='localhost', port=6379, db=0)
# 设置哈希表键值对
r.hset('user1', 'name', 'Alice')
r.hset('user1', 'age', 20)
r.hset('user1', 'gender', 'female')
# 获取整个哈希表
hash_table = r.hgetall('user1')
print(hash_table) # 输出 {b'name': b'Alice', b'age': b'20', b'gender': b'female'}
# 获取特定键对应的值
value = r.hget('user1', 'name')
print(value) # 输出 b'Alice'
# 删除哈希表的一个键值对
r.hdel('user1', 'gender')
# 获取所有键名
keys = r.hkeys('user1')
print(keys) # 输出 [b'name', b'age']
# 获取所有键名对应的值
values = r.hvals('user1')
print(values) # 输出 [b'Alice', b'20']
# 批量设置哈希表键值对
r.hmset('user2', {'name': 'Bob', 'age': 25})
# 批量获取哈希表键名对应的值
values = r.hmget('user2', ['name', 'age'])
print(values) # 输出 [b'Bob', b'25']
列表操作
import redis
# 连接到本地Redis服务器,默认端口为6379
r = redis.StrictRedis(host='localhost', port=6379, db=0)
# 从左侧插入元素
r.lpush('mylist1', 'foo')
r.lpush('mylist1', 'bar')
r.lpush('mylist1', 'baz')
# 从右侧删除元素
r.rpop('mylist1')
# 获取列表长度
length = r.llen('mylist1')
print(length) # 输出 2
# 获取整个列表
mylist = r.lrange('mylist1', 0, -1)
print(mylist) # 输出 [b'baz', b'bar']
# 从左侧插入元素
r.lpush('mylist2', 'one')
r.lpush('mylist2', 'two')
r.lpush('mylist2', 'three')
# 弹出列表头部元素
value1 = r.lpop('mylist2')
print(value1) # 输出 b'three'
# 弹出列表尾部元素
value2 = r.rpop('mylist2')
print(value2) # 输出 b'one'
# 在指定元素前或后插入新元素
r.linsert('mylist2', 'BEFORE', 'two', 'new')
mylist = r.lrange('mylist2', 0, -1)
print(mylist) # 输出 [b'new', b'two']
# 裁剪列表
r.ltrim('mylist2', 0, 0)
mylist = r.lrange('mylist2', 0, -1)
print(mylist) # 输出 [b'new']
集合操作
import redis
# 连接到本地Redis服务器,默认端口为6379
r = redis.StrictRedis(host='localhost', port=6379, db=0)
# 向集合中添加元素
r.sadd('set1', 'foo')
r.sadd('set1', 'bar')
# 获取集合中的所有元素
members = r.smembers('set1')
print(members) # 输出 {b'foo', b'bar'}
# 获取集合中的元素数量
count = r.scard('set1')
print(count) # 输出 2
# 判断一个元素是否在集合中
result = r.sismember('set1', 'foo')
print(result) # 输出 True
# 删除集合中的一个元素
r.srem('set1', 'bar')
# 获取多个集合的交集
r.sadd('set2', 'foo')
r.sadd('set2', 'baz')
intersection = r.sinter(['set1', 'set2'])
print(intersection) # 输出 {b'foo'}
# 获取多个集合的并集
union = r.sunion(['set1', 'set2'])
print(union) # 输出 {b'foo', b'baz'}
# 获取一个集合与多个集合的差集
difference = r.sdiff('set2', ['set1'])
print(difference) # 输出 {b'baz'}
有序集合操作
import redis
# 连接到本地Redis服务器,默认端口为6379
r = redis.StrictRedis(host='localhost', port=6379, db=0)
# 添加有序集合成员和分值
r.zadd('zset1', {'foo': 1.0, 'bar': 2.0, 'baz': 4.0})
# 获取有序集合的成员数
count = r.zcard('zset1')
print(count) # 输出 3
# 获取有序集合指定范围内的成员
members = r.zrange('zset1', 0, -1)
print(members) # 输出 [b'foo', b'bar', b'baz']
# 获取有序集合指定成员的分值
score = r.zscore('zset1', 'bar')
print(score) # 输出 2.0
# 获取有序集合指定范围内成员的数量
count = r.zcount('zset1', 1.5, 3.5)
print(count) # 输出 1
# 删除有序集合中一个成员
r.zrem('zset1', 'bar')
# 获取有序集合中指定范围内的成员和分值
with_scores = r.zrangebyscore('zset1', 0, 5, withscores=True)
print(with_scores) # 输出 [(b'foo', 1.0), (b'baz', 4.0)]
高级用法
Redis管道pipeline
在Redis中,管道(pipeline)是指可以将多个Redis命令依次发送给Redis,让Redis 一次性执行这些命令并返回结果的机制。使用管道可以大大减少客户端与Redis的网络通信次数,提高Redis的处理效率,是优化Redis性能的重要手段之一。
在redis-py库中,可以使用pipeline()方法创建一个管道对象,并对该对象连续调用多个 Redis 命令并提交到 Redis 进行执行。提交执行后,每个命令都会获取到这些命令的执行结果,并按照请求的顺序返回给客户端。
特点:
1.可以一次性发送多条命令并在执行完后一次性将结果返回
2.pipeline通过减少客户端与Redis的通信次数来实现降低往返延时时间
实现原理:
管道pipeline实现的原理是队列,队列是先进先出,这样就保证数据的顺序性
Client可以将三个命令放到一个tcp报文一起发送
Server则可以将三条命令的处理结果放到一个tcp报文返回
基本使用
1.使用 pipeline() 方法创建一个新的 Pipeline 对象,并向该管道对象连续调用了三个不同的 SET 命令,分别设置了三个不同的键名和对应的键值
2.通过 execute() 方法提交管道内所有的命令。Redis 服务器一次性执行管道内所有的命令,并将结果返回给客户端
3.最后输出Redis 管道执行的结果到控制台,其中包含了每个 SET 命令的执行结果
import redis
# 连接到本地 Redis 服务器,默认端口为 6379
r = redis.StrictRedis(host='localhost', port=6379, db=0)
# 使用 Redis 管道
pipe = r.pipeline()
# 连续设置多个键名和键值
pipe.set('name', 'John')
pipe.set('age', 30)
pipe.set('city', 'New York')
# 执行 Redis 管道中所有命令,并获取所有命令的执行结果
result = pipe.execute()
# 输出 Redis 管道执行结果
print(result)
Redis事务
事务 Redis 通过 MULTI 和 EXEC 来实现事务,MULTI 开启一个事务,EXEC 提交多个命令到 Redis 执行,可以保证单位时间内只有当前请求在访问 Redis 服务器,其他读写操作会等待这个事务结束后才能进行,从而保证了数据一致性。
基本使用
1.创建一个 redis.StrictRedis 实例,并且设置 transaction 参数为 True,表示开启 Redis 事务
2.使用 pipeline() 方法创建一个新的 Pipeline 对象,并将其 transaction 参数设置为 True,表示这个 Pipeline 是用于 Redis 事务的
3.调用 multi() 方法开启Redis 事务,之后向两个不同的键名 foo 和 bar 分别设置了不同的字符串值
4.最后通过 execute() 方法提交事务,Redis 将一次性执行整个事务并返回每个命令的执行结果
import redis
# 连接到本地 Redis 服务器,默认端口为 6379
r = redis.StrictRedis(host='localhost', port=6379, db=0)
# 使用 Redis 事务
trans = r.pipeline(transaction=True)
# 开启事务
trans.multi()
# 向两个键名分别设置不同的值
trans.set('foo', 'hello')
trans.set('bar', 'world')
# 在 Redis 事务中执行以上命令,并获取执行结果
result = trans.execute()
# 输出 Redis 事务执行结果
print(result)
事务与管道的区别
在Redis中,事务(transaction)和管道(pipeline)都是用于批量执行命令的方式,但二者有本质上的不同:
1.调用方式不同
使用事务时,需要先通过MULTI命令将客户端设置为事务模式,然后按照一定的顺序添加执行的多个命令,最后通过EXEC命令将操作提交到服务器执行。
使用管道时,则是对同一个连接对象上连续调用多个Redis命令并且在最后统一执行这些命令。
2.发送机制不同
Redis事务的逻辑单元可以确保所有被包含的命令“原子性”地执行,即要么全部执行成功完成,要么全部回滚;而Redis>运用管道的方法仅仅是优化传输,将多个命令打包发送到Redis服务节点,并在结果关闭时进行收集处理,以达到多个请求一次通信的目的。
3.回滚能力不同
Redis事务提交的过程中如果某个命令执行失败了,后面的命令则都不会再执行,已经执行过的命令不会回滚。当然在EXEC之前可以通过DISCARD命令清空已经放入到事务队列里面的命令;而管道机制暂时没有回滚的能力。
因此:
Redis管道是解决高性能I/O操作的手段,主要目的在于将多个命令打包,一次发出去避免了每次发送都占有一个网络通道
Redis事务适用于数据的批量修改,并期望原子性action。两种方案各有利弊,需要按照实际业务场景选择使用哪一种方式。
分布式锁
Redis通过 SETNX 和 EXPIRE 等命令实现分布式锁,可防止多个客户端同时修改同一资源。具体实现时,检查一个键是否存在,若不存在则对该键进行设置并获得锁;若已存在则等待。
import time
import uuid
import redis
# 连接到本地 Redis 服务器,默认端口为 6379
r = redis.StrictRedis(host='localhost', port=6379, db=0)
def acquire_lock(conn, lockname, acquire_timeout=10, lock_timeout=10):
"""
尝试获取锁,获取成功返回锁id;获取失败或者异常返回None
:param conn: Redis连接对象
:param lockname: 锁的名称
:param acquire_timeout: 最大尝试获取锁的时间(seconds)
:param lock_timeout: 锁的超时时间(seconds)
:return 是否获取权益:锁的ID/None
"""
identifier = str(uuid.uuid4())
end_time = time.time() + acquire_timeout
while time.time() < end_time:
# 获取锁
if conn.setnx(lockname, identifier):
conn.expire(lockname, lock_timeout)
return identifier
# 防止死锁
elif not conn.ttl(lockname):
conn.expire(lockname, lock_timeout)
time.sleep(0.001)
return None
def release_lock(conn, lockname, identifier):
"""
根据锁id释放锁,若锁不存在或者已经被其他持有者所释放则返回False;成功释放返回True
:param conn: Redis连接对象
:param lockname: 锁的名称
:param identifier:锁的ID
:return 是否释放:True/False
"""
pipe = conn.pipeline(True)
while True:
try:
# 开启事务
pipe.watch(lockname)
# 检查对应键是否还是要当前程序设置的值,以avoid误删别的客户端的锁
if pipe.get(lockname).decode('utf-8') == identifier:
# 删除锁
pipe.multi()
pipe.delete(lockname)
pipe.execute()
return True
pipe.unwatch()
break
except redis.exceptions.WatchError:
pass
return False
if __name__ == '__main__':
lockname = " lock"
id = acquire_lock(r, lockname)
print(id)
tag = release_lock(r, lockname, id)
print(tag)
订阅和发布
Redis 通过 SUBSCRIBE 和 PUBLISH 命令实现类似消息队列的功能,可以实现多个进程(客户端)针对一个频道进行消息的监听和广播。
import redis
# 连接到本地 Redis 服务器,默认端口为 6379
r = redis.StrictRedis(host='localhost', port=6379, db=0)
# 在 “频道” 上发布 “消息”
pub = r.publish('channel', 'Hello World')
# 返回发布/订阅对象
sub = r.pubsub()
# 使用此对象,可以订阅频道并收听发布的消息
sub.subscribe('channel')
for message in sub.listen():
print(message)
print(message['data'])
GeoHash
Redis通过GeoHash 实现了地理位置排序和搜索等功能。
import redis
# 连接到本地Redis服务器,默认端口为6379
r = redis.StrictRedis(host='localhost', port=6379, db=0)
# 添加地理位置坐标
r.geoadd("locations", 126.6507114792, 45.7603340486, "Harbin")
r.geoadd("locations", 117.2028132333, 39.0879960175, "Beijing")
r.geoadd("locations", 121.5670484719, 38.9544883509, "Tianjin")
# 获取指定两点间的距离(以 km 为单位)
distance = r.geodist("locations", "Harbin", "Beijing", unit="km")
print(distance) # 1072.1429
# 搜索指定范围内的地理位置(以 km 为单位),并按照距离从近到远排序
locations = r.georadiusbymember("locations", "Tianjin", 500, unit="km", withdist=True, sort="ASC")
print(locations) # [[b'Tianjin', 0.0], [b'Beijing', 377.3833]]
redis-py-cluster
redis-py-cluster是Python中用于连接Redis集群的模块,支持对Redis集群中的所有节点进行Hash槽分配操作,提供了与redis-py相同的API接口,使用方法类似。
GitHub地址:https://github.com/Grokzen/redis-py-cluster
安装
pip install redis-py-cluster
基本使用
from rediscluster import RedisCluster
# Redis节点
startup_nodes = [
{"host": "127.0.0.1", "port": "6379"},
{"host": "127.0.0.1", "port": "7001"},
{"host": "127.0.0.1", "port": "7002"}
]
# 创建RedisCluster对象
rc = RedisCluster(startup_nodes=startup_nodes, decode_responses=True)
# 与Redis的基本交互
rc.set("foo", "bar")
value = rc.get("foo")
print(value) # bar
# 哈希操作
rc.hset("user", "name", "Alice")
rc.hset("user", "age", 20)
hash_table = rc.hgetall("user")
print(hash_table) # {'name': 'Alice', 'age': '20'}
# 列表操作
rc.lpush("mylist", "foo")
rc.lpush("mylist", "bar")
rc.lpush("mylist", "baz")
mylist = rc.lrange("mylist", 0, -1)
print(mylist) # ['baz', 'bar', 'foo']
# 删除键
rc.delete("foo")
来源:https://juejin.cn/post/7232253274057441317


猜你喜欢
- 相信大家在日常学习或者是阅读英文文章的过程中,难免会出现几个不认识的单词,或者想快速翻译某段英文的意思。今天,利用Python爬虫等知识,教
- 认证登录django.contrib.auth中提供了许多方法,这里主要介绍其中的三个:1 authenticate(**cre
- Fuse.js是什么最近在项目里用到了Fuse.js做模糊查询,便对这个算法起了点好奇心,翻了翻源码。Fuse.js 是一个 JavaScr
- 手里有个老项目是基于element admin框架下的,之前写的时候没考虑到要打包成桌面端,后期需要打包成客户端,然后就开始了一些列版本操作
- 1、标识符与关键字在了解数据类型之前,先了解一下go的标识符和关键字1.1 标识符在编程语言中标识符就是定义的具有某种意义的词,比如变量名、
- 伴随着自然语言技术和机器学习技术的发展,越来越多的有意思的自然语言小项目呈现在大家的眼前,聊天机器人就是其中最典型的应用,今天小编就带领大家
- 我们平日办公时用得最多的软件是Execl、Word或WPS Office等,你的计算机中一定储存着大量的XLS、DOC、WPS文件吧!网页制
- 前言今天给大家带来的是Vue 3 中的极致防抖/节流(含常见方式防抖/节流)这篇文章,文章中不仅会讲述原来使用的防抖或节流方式,还会带来新的
- 1. 定义用一行构建代码例题# 构建一个1-100的列表l1 = [i for i in range(1,101)]print(l1)# 输
- 以下均在本人虚拟机上进行1.安装pip3sudo apt install python3-pip2.安装虚拟环境sudo apt insta
- 很多朋友对FrontPage2003中增加的网页布局功能很感兴趣,现在我们一起来深入了解这一实用功能。用FrontPage2003的“布局表
- 在实际工作或面试中,我们经常会遇到“数组去重”问题,接下来就是使用js实现的数组去重的多种方法:1.将数组的每一个元素依次与其他元素做比较,
- 也不一定,以前从来没有深入的研究过sql查询,最近买了一本T-SQL查询的书,把以前忽视的问题都记录一下 以前一直模模糊糊的把sqlserv
- 1.配置环境安装python3安装python3-pip通过pip安装Django**如果需要使用Jinja模板,需要通过pip安装djan
- 简介这两天更新完Xcode8之后发现Xcode对图标的要求又有了变化,之前用的一个小应用“IconKit”还没赶上节奏,已经不能满足Xcod
- 每个JavaScript函数都有prototype属性(javascript对象没有这个属性),这个属性引用了一个对象,这个对象就是原型对象
- 亲身实践安装mysql,用时居然花费了三个小时,在有那么多教程的情况下,依然在不该花费时间的路上浪费了太多时间。希望这篇文章能够帮助大家少走
- 人生苦短,快学Python!最近有位读者朋友遇到了一个小问题,私聊找小五答疑。感觉也会有其他同学会遇到,所以干脆分享出来。如下图所示,在本地
- 1.创建mysql存储过程,这是个复杂查询加上了判断,比较复杂CREATE PROCEDURE searchAllList (IN trad
- Python读取配置文件-ConfigParser二次封装直接上上代码test.conf[database]connect = mysqls