事务同命令一样,都是Redis的最小执行单位,一个事务中的命令要么都执行,要么都不执行。
一、Redis事务的实现
Redis通过multi、exec、watch等命令来实现事务功能,Redis中一个事务从开始到结束包含三个阶段:事务开始、命令入队、事务执行。其中multi命令标志着事务开始,exec命令标志着事务执行,期间所有命令都会存放到事务队列中。
在redis.h/redisClient结构中,可以看到每个Redis客户端都有自己的事务状态,保存在multiState属性中
typedef struct redisClient{
//...
// 事务状态
multiState mstate; /* MULTI/EXEC state */
//...
}
在redis.h/multiState结构可以看到其包含一个事务队列multiCmd以及一个已入队的命令计数器
/*
* 事务状态
*/
typedef struct multiState {
// 事务队列,FIFO 顺序
multiCmd *commands; /* Array of MULTI commands */
// 已入队命令计数
int count; /* Total number of MULTI commands */
int minreplicas; /* MINREPLICAS for synchronous replication */
time_t minreplicas_timeout; /* MINREPLICAS timeout as unixtime. */
} multiState;
在redis.h/multiCmd结构中可以看到已入队的命令的相关信息,包括指向命令实现函数的指针、命令参数及参数数量
/*
* 事务命令
*/
typedef struct multiCmd {
// 参数
robj **argv;
// 参数数量
int argc;
// 命令指针
struct redisCommand *cmd;
} multiCmd;
执行如下所示的事务命令
其事务状态如图所示
二、Redis事务的特性
与传统的数据库一样,Redis通过ACID性质来检验事务功能的可靠性和安全性。
1、原子性(Atomicity)
针对原子性,Redis不支持事务的回滚:
(1)事务要么都执行,要么都不执行;
(2)事务执行失败,后面的命令还会执行,错误之前的命令不回滚;
针对(1)给出如下图所示的示例,命令在放入事务队列的时候被检测到错误,所以事务中的所有命令都不会执行。在EXEC的时候发现事务错误,事务里的命令都不会执行。因此,谈不到回滚。
针对(2)给出如下图所示的示例,作者指出不支持事务的回滚是因为这种复杂的功能和Redis追求的简单高效的设计主旨不符合,这种错误通常是由编程错误造成,只会出现在开发环境,不会出现在生产环境中。因此,没有必要开发回滚功能。
2、一致性(Consistency)
针对一致性,通过妥善处理可能出错的地方,确保事务的一致性:
(1)入队错误:命令入队过程中出现命令不存在或者命令格式不正确的情况,则拒绝执行这个事务;
(2)执行错误:事务在执行过程中出错的命令由相应的错误处理,所以这些出错的命令不会对数据库做任何修改,也不会影响一致性;
(3)服务器停机:
a)无持久化的内存模式下,重启后空白,一致;
b)RDB模式下,事务中途停机,重启后可根据RDB文件还原数据库状态;
c)AOF模式下可根据AOF文件还原数据库状态;
3、隔离性(Isolation)
针对隔离性,Redis以单线程的方式来执行事务,并保证事务执行期间不会被中断。
4、持久性(Durability)
针对持久性,Redis的事务是用队列包裹起来的,因此事务的持久性由Redis使用的模式决定,如在AOF模式的事务具有持久性。但不管Redis在什么模式下,在事务的最后加下SAVE命令总可以保证事务的持久性。
参考资料
1、http://www.redis.net.cn/tutorial/3506.html
2、《Redis设计与实现》第二版---黄健宏
3、https://github.com/xingzhexiaozhu/redis-3.0-annotated
4、http://www.yiibai.com/redis/redis_strings.html