1、Redis事务(ACID)
1、原子性:不支持
不会回滚并且继续执行
2、隔离性:支持
事务中命令顺序执行, 并且不会被其他客户端打断 (先EXEC的先执行)
单机redis读写操作使用单进程单线程
3、持久性:支持, 但相比Mysql, redis数据易丢失
4、一致性:不支持
基本语法
MULTI
开启事务, 后续的命令会被加入到同一个事务中
事务中的操作会发给服务端, 但是不会立即执行, 而是放到了该事务的对应的一个队列中, 服务端返回QUEUED
EXEC
执行EXEC后, 事务中的命令才会被执行
事务中的命令出现错误时, 不会回滚也不会停止事务, 而是继续执行
DISCARD
取消事务, 事务队列会清空, 客户端退出事务状态
2、Redis乐观锁
应用场景:避免并发引起的资源抢夺问题
基本语法:
watch
redis实现的乐观锁
机制:
事务开启前, 设置对数据的监听, EXEC时, 如果发现数据发生过修改, 事务会自动取消(DISCARD)
事务EXEC后, 无论成败, 监听会被移除
WATCH mykey # 监视mykey的值
MULTI # 开启事务
SET mykey 10
EXEC # 如果mykey的值在执行exec之前发生过改变, 则该事务会取消(客户端可以在发生碰撞后不断重试)
代码实现:
# 需求: 使用redis实现秒杀功能 (防止超卖)
from redis import StrictRedis, WatchError
# 1.创建客户端
redis_client = StrictRedis(host='192.168.234.128', port=6381, decode_responses=True)
# 2.创建管道对象
pipe = redis_client.pipeline()
while True:
try:
# 3.监听数据 如果开启监听, 则不会开启默认的事务, 后续的pipe操作会立即执行
pipe.watch('count')
# 4.读取库存数量
count = pipe.get('count')
# 判断库存数量
if int(count) > 0: # 有库存, 库存-1
# 一旦使用watch函数事务不会自动开启
# 手动开启事务
pipe.multi()
# 库存减一
pipe.decr('count')
# 提交事务
pipe.execute()
print('下单成功')
else: # 无库存
print('商品已售罄')
# 将监听移除
pipe.reset()
break
except WatchError: # 捕获到该异常, 说明监听的数据被其他客户端修改, 此时应该重试/取消操作
print('数据被修改, 重试')
continue
3、Redis悲观锁
基本语法
SETNX命令
键不存在才会设置成功
多个客户端抢夺, 只有一个可以设置成功(获取锁, 获取操作数据的权限)
from redis import StrictRedis
# 1.创建redis客户端对象
redis_cli = StrictRedis(decode_responses=True)
# 2.构建锁的键
key = "order:lock"
while True:
# 3.争夺锁资源
# 争夺到锁资源返回True 否则返回False,参数1是任意值
lock = redis_cli.setnx(key, 1)
# 防止忘记删除锁资源,给锁资源添加过期时长 --防止出现死锁
redis_cli.expire(key, 5)
# 4.获取库存
count = redis_cli.get("count")
# 5.争夺到锁资源的允许操作redis客户端对象进行数据库增删改查
if lock:
if int(count) > 0:
# 减库存
redis_cli.decr("count")
print("下单成功")
else:
print("库存不足")
break
# 6.删除锁--防止死锁
redis_cli.delete(key)
4、Redis非事务管道
from redis import StrictRedis
# 1.创建redis、客户端对象
redis_cli = StrictRedis(decode_responses=True)
# 2.创建redis管道对象【非事务型管道对象】
# 好处:当需要执行的命令特别多的情况下,使用非事务管道,能够提高执行效率
pipeline = redis_cli.pipeline(transaction=False)
# 3.通过管道对象操作数据库[增删改查]
a = pipeline.set("name3", "小明")
b = pipeline.get("name3")
# 4.执行管道中的命令
c = pipeline.execute()
print(a, b, c)