事务是逻辑上对数据的一组操作,这操作要么一次全部成功或者这操作全部失败,是不可分割的单位
四大特性
原子性,一致性,隔离性,持久性(ACID)
redis的事务
redis是弱事务型数据库,并不具备ACID的全部特性
redis具备隔离性:事务中的所有命令会被序列化,按顺序执行,在执行的过程中不会被其他客户端发送来的命令打断
不保证原子性:redis中的一个事务中如果存在命令执行失败,那么其他命令依然会被执行,没有回滚机制
事务命令
1,MULTI 开启事务 mysql begin
2,命令1 执行
3,命令2 .。。。。
4-1,EXEC 提交到数据库执行 mysql commit
4-2,DISCARD 取消事务 mysql 'rollback'
开启事务-命令入队列-提交到数据库执行
特殊事务情况-命令语法错误
命令入队失败,直接自动discard退出这个事务
这个命令在执行调用之前会发生错误。例如,这个命令可能有语法错误(错误的参数数量,错误的命令名)
处理方案:语法错误则自动执行discard
命令语法没错,但是类型操作有误,则事务执行调用之后失败,无法进行事务回滚
当我们执行了一个由于错误的value的key操作会出现该现象(例如对着string类型的 value施行list命令操作)
处理方案:发生在EXEC之后的是没有特殊方式去处理的:即使某些命令在事务中失败,其他命令都将会被执行。
PIPELINE流水线
批量执行redis命令,减少通信io
原理:效仿redis的事务,客户端将多个命令打包,一次通信发给redis,可明显降低redis服务的请求数
注意:
1,此为客户端技术
2,如果一组命令中,一个目录需要上一个命令执行结果才可以执行则无法使用该技术
创建redis连接池
import redis
pool = redis.ConnectionPool(host = '127.0.0.1',db=0,port=6379)
r = redis.Redis(connection_pool=pool)
pipe = r.pipeline()
pipe.set('fans',50)
pipe.incr('fans')
pipe.incrby('fans',100)
pipe.execute()
watch-乐观锁
事务过程中,可对指定key进行监听,命令提交时,若被监听key对应的值未被修改,事务方可提交成功,否则失败
解决资源竞争的一种方式
watch books
multi
incr books
exec
提交失败
watch之后,再开一个终端进入redis
incr books #修改book值
import redis
pool = redis.ConnectionPool(host = '127.0.0.1',db=0,port=6379)
r = redis.Redis(connection_pool=pool)
#python 操作事务需要依赖流水线技术
with r.pipeline(transaction=True) as pipe:
pipe.multi()
pipe.incr('books')
pipe.incr('books')
values = pipe.execute()
例子
import redis
pool = redis.ConnectionPool(host = '127.0.0.1',db=0,port=6379)
r = redis.Redis(connection_pool=pool)
def double_account(user_id):
key = f'account_{user_id}'
with r.pipeline(transaction=True) as pipe:
while True:
try:
pipe.watch(key)
value=int(r.get(key))
value*=2
pipe.multi()
pipe.set(key,value)
pipe.execute()
except redis.WatchError:
print('---key changed')
continue
return int(r.get(key))
if __name__ == '__main__':
print(double_account('xxxx'))