【Redis-11】Redis事务实现原理

 Redis通过MULTI、EXEC、WATCH等命令来实现事务的功能,事务提供了一种将多个命令请求打包,然后一次性,顺序性的执行多个命令的机制。在事务执行期间,服务器不会中断事务去执行其他客户端的命令,他会讲事务中所有命令执行完成后,才会去处理其他客户端的命令请求。

1. 事务的实现

 一个事务从开始到结束通常会经历三个阶段:事务开始、命令入队、事务执行。接下来我们就针对这三个阶段看一下事务的整个执行过程。

  • 事务开始:Redis事务的开始是通过MULTI这个命令开始的,此命令就标志着客户端从非事务的状态切换到事务的状态,具体表现在客户端状态标记的打开,即:client.flags |= REDIS_MULTI
  • 命令入队:当客户端处于事务状态时,服务器除了会执行EXEC、DISCARD、WATCH、MULTI命令之外,其余的命令都会放在一个事务队列中,然后向客户端返回QUEUED
  • 事务队列:每个客户端都有自己的事务状态,保存在client.mstate属性中,事务状态属性包含一个事务队列,以及一个入队的命令计数器(事务队列的长度),事务队列以先进先出的方式保存入队命令。
typedef struct client {
    ...
    // 客户端的事务状态
    multiState mstate;      /* MULTI/EXEC state */
    ...
} client;
typedef struct multiState {
	// 队列,保存客户端命令
    multiCmd *commands;     /* Array of MULTI commands */
    // 入队命令的个数
    int count;              /* Total number of MULTI commands */
    ...
} multiState;
  • 执行事务:当客户端发送EXEC命令时,服务器会遍历客户端的事务队列,一次性不间断的执行完队列中所有的命令,并将结果返回给客户端。

2. WATCH命令的实现

WATCH命令是一个乐观锁,可以在执行EXEC命令之前,监视任意数量的数据库键值对,并且在 EXEC命令执行时,检查监视的键是否有修改过,如果是,服务器会拒绝执行,并向客户端返回事务执行失败的回复。
 首先看一下WATCH命令的实现原理:在代表数据库的结构redisDB中,保存着代表被WATCH监视的数据库键,这个属性是dict,字典的键是被监视的键,字典的值是一个链表,链表每个节点是WATCH对应键值对的客户端。

typedef struct redisDb {
	// 代表WATCH监视的键值对及对应客户端
    dict *watched_keys;         /* WATCHED keys for MULTI/EXEC CAS */
	...
} redisDb;

 所以通过这个字典,其实可以知道哪些客户端在监听哪些数据库的键值。比如有这么一个场景,客户端c1、c2执行了WATCH name,客户端c3执行了WATCH age,客户端c4、c5、c6执行了WATCH gender,通过一个图示可以展现如下:
【Redis-11】Redis事务实现原理_第1张图片
 当数据库执行键值对的写操作时,会对watched_keys进行检查,查看是否有客户端正在监视对应的key。如果有的话,会调用相关函数,对所有的客户端进行标志的修改:client.flags |= REDIS_DIRTY_CAS,表示该客户端的事务安全性已经遭到了破坏。
 当服务器接收到EXEC命令准备执行时,会检查client.flags是否打开了REDIS_DIRTY_CAS的标识。如果标识被打开,说明事务已经不再安全,服务器拒绝执行,否则服务器会正常执行客户端的事务。

3. 事务的ACID特性

 这里是要去对比关系型数据库原子性、一致性、隔离性、持久性的四个特性。

  • 原子性: 原子性指的是,数据库会将事务中的多个操作当成一个整体来执行,要么执行全部操作,要么一个也不执行。Redis事务与传统关系型数据库原子性区别在于,首先Redis事务不支持回滚,其次Redis只会在命令书写错误或者格式错误的时候会拒绝执行事务,而在命令执行期间的错误(比如参数类型错误等),redis会跳过继续执行余下的所有命令。
  • 一致性: 一致性指的是数据库在执行事务之前和之后的状态应该是一直的,无论事务是否执行成功,这里的一致性指的符合数据库本身的定义和要求,不包含非法字符或者无效的错误数据。这一点体现在,redis无论是通过检查命令的合法性而拒绝执行事务,或者事务执行期间命令参数错误也继续余下命令时,都不会被数据库做出任何错误的修改,所以符合一致性。
  • 隔离性: 隔离性是指即使数据库中有多个事务并发的执行,各个事务之间也不会互相影响,并且在并发状态下执行的事务和串行状态下执行事务的结果完全相同。因为Redis使用单线程的方式来执行事务,并且服务器在执行事务期间不会对事务中断,所以Redis这种串行的方式可以保证事务的隔离性。
  • **持久性:**持久性是指,当事务执行完成时,事务的结果已经被永久性的保存到储存介质中,即使服务器发生故障,也不会导致事务失效。Redis事务执行时,会依次执行队列中的所有命令,所以事务持久性与服务器配置的持久化方式有关,如果配置了RDB或者AOF的持久化,且命令执行后被持久化到磁盘,也是具备持久的特性。

 综上,就是事务执行的部分场景,如有问题,可以给我留言。

你可能感兴趣的:(redis,redis,数据库,java)