Redis事务原理

本章主要Redis的事务相关特性

一、与Redis事务相关的基本概念

  • MULTI:用于标记事务的开始,其后执行的命令都将被存入命令队列,直到执行EXEC时,这些命令才会被原子执行。
  • EXEC: 执行事务,内部包装执行了WATCH命令,只有当WATCH所监控的keys没有被修改的前提下,EXEC命令才能执行事务队列中的所有命令,否则EXEC将放弃当前事务中的所有命令。(有点相似于CAS)
  • DISCARD:回滚事务队列中的所有命令,同时再将当前连接的状态恢复为正常状态,即非事务状态。如 果WATCH命令被使用,该命令将UNWATCH所有的keys。
  • WATCH:在MULTI命令之前,可以指定监控的keys,提供监控(乐观锁)的功能,如果key的值发生了变化,就会放弃事务的执行。
  • UNWATCH: 取消当前事务中指定监控的keys,如果执行了EXEC或DISCARD命令,事务中所有的keys都将自动取消WATCH。

二、底层实现

2.1 事务队列

每个客户端都有自己的事务状态,保存在客户端的mstate属性中。事务状态包含一个事务队列,结构有:

typedef struct redisClient {
    //事务状态
    multiState mstate;
} redisClient;

type struct multiState {
    //事务队列,FIFO
    multiCmd *commands;
    //已入队命令计数器
    int count;
} multiState;

 事务队列是一个multiCmd类型的数组,数组中每个multiCmd结构都保存了一个已入队命令的相关信息,包括指向命令实现函数的指针、命令的参数,以及参数数量:

typedef struct multiCmd {
    //参数
    robj **argv;
    //参数数量
    int argc;
    //命令指针
    struct redisCommand *cmd;
} multiCmd;

Redis事务原理_第1张图片

2.2 执行事务

当客户端发送exec命令时候,服务器会遍历客户端的事务队列,执行队列中所有的命令,并把执行结果一次性全部返回给客户端。

2.3 放弃事务

执行discard命令后,会清空事务队列,并且客户端会从事务状态总退出。

2.4 WATCH的乐观锁实现

WATCH 命令可以为 Redis 事务提供 check-and-set (CAS)行为。被WATCH的键会被监控,如果在 WATCH 执行之后, EXEC 执行之前, 有其他客户端修改了键的值, 那么当前客户端的事务就会失败。

三、Redis事务的理解

事务的ACID特性,在Redis中,需要特别理解:

A:原子性。Redis的原子性是通过事务队列执行的,EXEC 命令负责触发并执行事务中的所有命令。当时用AOF持久化时,Redis会使用单个的write命令将事务写入磁盘中,如果Redis服务因故障只有部分事务写入磁盘,启动时重载AOF文件会报错,使用redis-check-aof程序可以修复这一问题:它会移除 AOF 文件中不完整事务的信息,确保服务器可以顺利启动。

另一方面,如果开启了事务MULTI,但是未执行EXEC,事务队列中的命令不会被执行。

C:一致性。在Redis中,一致性不能很好的保证,不支持回滚。

I:隔离性。因为Redis单线程串行化命令的特点,可以保证事务的隔离性

D:持久性。Redis有RDB和AOF两种模式做持久化处理。

 

Redis的一致性问题:事务在执行 EXEC 之前,入队的命令可能会出错。比如说,命令可能会产生语法错误(参数数量错误,参数名错误,等等),或者其他更严重的错误,比如内存不足,

  • 在Redis2.6.5之前,客户端可以根据入队命令返回QUEUED,判断是否入队成功
  • 在Redis2.6.5之后,服务端会对入队的命令做记录,如果入队失败,执行事务的时候会自动抛弃这些命令

在执行事务的过程中,如果命令出现了错误,事务中的其他命令将会继续执行,对于错误的命令,返回结果是nil。

比如

set a 10;
set b lala

multi

incr a
incr b

exec

// 返回结果有:
11
error

从上面的列子中看出,Redis是不支持回滚的。

四、Redis为什么不支持回滚

Redis 在事务失败时不进行回滚,而是继续执行余下的命令。主要的原因是:

  • Redis 命令只会因为错误的语法而失败(并且这些问题不能在入队时发现),或是命令用在了错误类型的键上面:这属于编程错误,生产环境中不应该出现。
  • 不支持回滚,可以保持Redis内部简单快捷

参考文献:

https://blog.csdn.net/weixin_33704234/article/details/89543128

https://www.jianshu.com/p/ae4c52af3390

 

 

 

你可能感兴趣的:(redis)