05. redis事务
redis 事务
redis操作时支持事务的。事务具有原子性atomic,包含在事务中的操作要么都执行成功,要么都执行失败。但是redis不支持回滚,但是可以在测试开发环节避免错误操作。可以说原子性上是半支持的,看后面原因。
很多时候我们需要进行事务操作。
翻译官档:
实际操作:python版使用参考
https://github.com/7edge/redis-demo/blob/master/redis_pipeline.py
事务
multi, exec, discard 和 watch 操作都是redis事务的基础操作。这些操作可以让一组操作看作一个操作原子执行。之所以能做到,是因为一下两个重要的保障:
1. 命令有序
在事务中的所有命令都是有序的,且执行是顺序执行的。当另一个客户端连接发生错误,是不可能影响到当前正在执行的事务的。这点就保证了命令执行时是相互隔离的操作。因为是单线程,且redisserver利用i/o多路复用来处理并发连接。
2. 始终原子
在事务中无论全部命令执行了,还是一个命令都没执行,redis的事务都是原子的。exec命令开启事务中所有命令的执行,如果在调用multi命令前没有,client到server端的连接断开,没有任何操作会被执行;如果exec命令被执行了, 所有的操作都会被执行。如果配置时使用了append-only file ,redis会使一个single write(2) syscall写入到aof文件中。然而,如果redis server crashes 或者被管理员强制kill掉,这时可能只有一部分操作被注册了,这种情况下,redis在重启的时候会发现这种情况,并且启动失败退出,报一个错误。 这时就要使用 redis-check-aof 工具,该工具将会修复aof文件,删除只有部分的事务,然后再重启redis即可。
开启使用事务
1. 执行multi # 开启事务, 返回ok或者其它 2. 执行多个命令 # 事务中的操作, 返回queued,表明加入事务命令队列 3. exec # 执行,返回每个事务中命令的返回结果列表 4. discard 如果在emulti之后exec之前要关闭事务,再exec之前执行即可flush所有事务命令队列中的命令并关闭事务
redis事务中出现错误
在事务中,有些操作可能或遇到执行出错,大致有两种命令执行错误:
1. exec前的错误
有一个错误在执行exec命令前,即命令没有成功的加入到队列中。如:执行的命令参数个数错误,命令名使用错误等。
对于这种错误,client是可以感知到的,因为执行命令时,其实是加入到命令队列中的,如果返回queued说明加入成功,其它返回都是错误。这种错误,大多数client将会中断事务并自动discard事务。(python得redis模块中,由于pipeline和multi/exec结合,redis.exceptions.responseerror异常,但是没有异常得命令还是会执行,就是由于与pipeline结合)
然而,在redis 2.6.5 后,server将会记录错误在累积命令到命令队列中时,这时执行exec,server将会拒绝事务的执行并返回一个错误,自动discard.
而在redis2.6.5之前,对于这种错误,server行为是,只会执行成功加入到队列中的命令,相当于命令集中的一个子集被执行一次,尽管有错误发生。redis 2.6.5 server没有了这种处理逻辑,而是将逻辑加入到新的命令中,就是pipeline管道命令,管道可以实现之前的行为,管道会返回命令的返回结果的列表。
2. exec后的错误
错误在exec执行后,如key冲突。对于这种错误,没有handle方法,所有的其它命令都会被执行,即使一新命令出错在事务期间。
为什么出错了不支持roll backs?
像关系型数据库,事务都是支持回滚的,而在redis事务中竟然支持错误的发生。redis之所以有这种行为是因为一下两点:
- redis 命令失败只会是语法错误或者 存在的key修改成了错误的数据类型。这些错误是非常容易在开发或者测试阶段被发现的。
- redis本应该是简单且快速的,不需要roll back能力。
redis事务所指的原子性仅仅只针对将命令加入执行队列的过程,redis事务不支持在命令执行过程中的错误回滚。
redis的乐观锁实现check-and-set
并发事务,都会有修改同一数据的时候,虽然redis事务因为读线程时隔离了的,但是有时候我们需要锁定数据,独占使用,就只有使用redis提供的乐观锁来锁定我们的数据,这个乐观锁只能配合事务使用,检查行为是在事务执行exec时,如果有变化,那么exec执行回失败。watch 被用于提供cas行为用于redis事务中。cas即是我们常说的乐观锁。
乐观锁的事务执行失败,必须检测结果,失败就要重试。
我们知道事务的4特性acid:
atomic原子性: 事务中操作集,要么都成功,要么都失败回滚。 redis事务所指的原子性仅仅只针对将命令加入执行队列的过程,redis事务不支持在命令执行过程中的错误回滚。 consistency 一致性: 事务前后,数据库是从一个一致性状态到另一个一致性状态。如转账前后总数是一致的。 isolation 隔离性: 并发事务时,事务之间都不会由于访问同一数据而被干扰。在这个隔离层面讲,就有多种隔离级别,因为隔离级别时影响并发事务效率和数据安全性的,要在效率和安全间平衡是关键。隔离级别太严格,并发效率低,数据安全;隔离级别太低,又怕数据不安全。 durability 持久性:事务一旦被提交,改变时永久的,即要持久化。很多都会将事务操作完后追加到操作日志文件中,数据者写入磁盘文件系统持久化。
watch 命令用于监控key的改变,如果被监控时的key发生了修改在执行exec之前,那么这个事务将aborts,并且返回一个null,表明事务失败。可用在要锁定数据的事务前。watch 配合multi和exec
使用unwatch可以flush掉所有watched keys。
小结
- redis事务所指的原子性仅仅只针对将命令加入执行队列的过程,redis事务不支持在命令执行过程中的错误回滚
- 利用watch可以乐观锁数据。乐观cas,悲观让别人等。
上一篇: 如何寻找sql注入漏洞?