Redis作为一个数据库使用时,它本身也提供了事务机制的支持。事务执行期间,Redis服务器不会去中断事务而执行其他客户端的命令请求,它会将事务中所有的命令都执行完毕之后,才去处理其他客户端的命令请求。Redis事务的实现主要通过MULTI、EXEC和WATCH三个命令实现,其中MULTI用于开启事务,EXEC用于提交事务、WATCH用于监视任意数量的key。

    Redis事务实现的一个核心结构是事务队列,当服务器以事务状态运行时,针对于接收到的不同命令会有不同的操作:

    • 如果是MULTI、EXEC、WATCH和DISCARD其中的任意一个,服务器立刻执行
    • 如果不是上述的四个命令,那么服务器就会将其放入到一个事务队列中,然后向服务器返回QUEUED恢复,表示命令已经入队,等待执行

    其中,每个RedisClient通过mstate字段来标识自己的事务状态,而事务状态又包含一个事务队列和一个计数器,如下所示:

    1. typedef struct redsiClient{
    2. // ...
    3. multiState mstate; // 事务状态
    4. // ...
    5. }
    6. typedef struct multiState{
    7. // ...
    8. multiCmd *commands; // 事务队列
    9. int count; // 入队命令计数器
    10. // ...
    11. }
    12. typedeef struct multiCmd{
    13. // ...
    14. robj **args; // 参数
    15. int argc; // 参数数量
    16. struct redisCommand *cmd; // 命令指针
    17. // ...
    18. }

    假设执行如下的Redis命令:

    1. MULTI
    2. set "name" "Kobe"
    3. set "sport" "basktball"
    4. get "name"
    5. EXEC

    当服务器使用MULTI开启事务后,后续所有除了MULTI、EXEC、WATCH和DISCARD之外的命令都会进入到事务队列中。当执行EXEC时,服务器会遍历事务队列,执行队列中的所有命令,最后将命令执行的结果回复给客户端。
    image.png
    以事务形式执行上述的命令,如下所示:

    1. 127.0.0.1:6379> MULTI
    2. OK
    3. 127.0.0.1:6379> set name "Kobe"
    4. QUEUED
    5. 127.0.0.1:6379> set sport "basketball"
    6. QUEUED
    7. 127.0.0.1:6379> get name
    8. QUEUED
    9. 127.0.0.1:6379> EXEC
    10. 1) OK
    11. 2) OK
    12. 3) "Kobe"
    13. 127.0.0.1:6379>

    下面看一下WATCH这个命令是如何参与事务控制的。WATCH命令是一种乐观锁的实现方式,它在EXEC命令执行之前监视任意数量的键,并在EXEC执行期间检查监视的键是否至少有一个已经被修改。如果是,服务器拒绝执行事务,并向客户端返回代表事务执行失败的空回复。如果一个键都没有被修改,则成功执行事务。

    WATCH命令监视的键会保存在数据库的watched_keys字典中,key为监视的键,value为监视该键的所有客户端,如下所示:
    image.png

    其中,所有监视键的客户端都会维护一个REDIS_DIRTY_CAS标识,只要监视的键发生了改变,那么REDIS_DIRTY_CAS就会被打开。Redis服务器在执行EXEC时,只需要查看所有监视的键的客户端的REDIS_DIRTY_CAS标识是否有至少一个被打开,只要有一个被打开,那么说明键被修改过,拒绝执行事务。

    Redis事务的ACID
    **
    Redis所支持的事务同样满足事务的ACID,具体表现来说:

    • 原子性:事务中的命令要么全部执行,要么一个都不执行。但Redis不支持事务的回滚,即使事务中包含出错的命令,也不影响其他正确命令的执行
    • 一致性:Redis在各种可能出错的情况下都可以满足事务的一致性要求
    • 隔离性:Redis事务总是串行执行,并且事务总是满足隔离性要求
    • 持久性:Redis事务的持久性只有在支持AOF持久化,并且appendsync选项被配置为always时才能保证

    总结
    **
    最后总结一下和事务相关的命令,如下所示:

    命令 描述
    MULTI 开启事务
    EXEC 执行事务
    WATCH 监视任意数量的key
    DISCARD 取消事务,放弃执行事务块中的所有命令
    UNWATCH 取消WATCH对所有key的监视