Redis事务
redis事务简介
redis事务的本质:
- 一组命令的集合,一个事务中的所有命令都会被序列化,在事务执行过程中,都会被顺序执行
- redis事务特性:一次性,顺序性,排他性(事务在执行期间不会被主动中断,执行完事务中的所有命令之后才会继续处理其他客户端的其他命令)
- redis事务中没有隔离性
- 事务中的命令只有在exec后才开始执行
redis单条命令是保证原子性的,但是redis的事务是不保证原子性的
示例:
// 开启事务
multi
// 开启事务后,所有命令依次入队
set k v
set k1 v2
// 执行事务,命令队列中的命令才会依次执行
exec
// 放弃事务,事务队列中的命令都被清空
discard
如果事务中存在编译时异常,比如我在入队时添加了redis根本没有的命令,那么直接就会抛出异常不会等到exec,事务中的其他命令不会被执行
如果事务队列中某些命令存在运行时异常,那么在exec时,其他命令可以正常执行,错误命令抛出异常,比如我对字符串的value进行自增,那么该命令就会报错,但是其他命令正常执行
事务原理
当开启事务时,即执行multi,redis会由非事务状态转为事务状态,那redis服务器在接收到命令时就会将命令放在事务队列中,等待exec,并不是所有的命令都会被放进事务队列, 其中的例外就是 EXEC 、 DISCARD 、 MULTI 和 WATCH 这四个命令 —— 当这四个命令从客户端发送到服务器时, 它们会像客户端处于非事务状态一样, 直接被服务器执行:
如果客户端正处于事务状态, 那么当 EXEC 命令执行时, 服务器根据客户端所保存的事务队列, 以先进先出(FIFO)的方式执行事务队列中的命令: 最先入队的命令最先执行, 而最后入队的命令最后执行。
Redis 的事务是不可嵌套的, 当客户端已经处于事务状态, 而客户端又再向服务器发送 MULTI 时, 服务器只是简单地向客户端发送一个错误, 然后继续等待其他命令的入队。 MULTI 命令的发送不会造成整个事务失败, 也不会修改事务队列中已有的数据。(保证排他性)
redis watch机制
watch命令用来在事务开始之前监控任意数量的key,当调用exec时,如果被监控的key被其他redis客户端修改,那么整个事务不会被执行,直接返回失败
实现原理
在redis每个数据库中都有一个watch_keys的字典,字典的键就是这个数据库被监视的键,而字典的值就是一个链表, 链表中保存了所有监视这个键的客户端
执行watch命令就是将客户端实例添加到watch_keys的指定链表中
如果程序想检查某个键是否被监视, 那么它只要检查字典中是否存在这个键即可; 如果程序要获取监视某个键的所有客户端, 那么只要取出键的值(一个链表), 然后对链表进行遍历即可
watch触发
在所有对数据库的key修改命令执行后, multi.c/touchWatchedKey
函数都会被调用 —— 它检查数据库的 watched_keys
字典, 看是否有客户端在监视已经被命令修改的键, 如果有的话, 程序将所有监视这个/这些被修改键的客户端的 REDIS_DIRTY_CAS
选项打开,当客户端发送exec命令触发事务执行,服务器会对该客户端的状态进行检查,如果客户端的 REDIS_DIRTY_CAS
选项已经被打开,那么说明被客户端监视的键至少有一个已经被修改了,事务的安全性已经被破坏,则事务失败