事务

事务:提供一种将多个命令请求打包,然后一次性、按顺序地执行多个命令的机制,并且在事务执行期间,服务器不会中断事务而改去执行其他客户端的命令请求,它将事务中的所有命令都执行完毕,然后才会处理其他客户端的命令请求。

1. 事务的实现

三个阶段:

1.1 MULTI命令:事务的开始

执行该命令的客户端从非事务状态切换至事务状态,在客户端状态的flags属性中打开REDIS_MULTI标识完成.

127.0.0.1:6379> MULTI
OK

1.2 命令入队(事务队列)

127.0.0.1:6379> SET name "xiaoming"
QUEUED
127.0.0.1:6379> GET name
QUEUED
127.0.0.1:6379> SET author "peter"
QUEUED
127.0.0.1:6379> GET author
QUEUED

这里写图片描述

typedef struct redisClient{
    //...

    //事务状态
    multiState mstate;

    //...
};
typedef struct mltiState{
    //事务队列,FIFO顺序
    multiCmd *commands;  //数组,存放命令
    //已入队命令计数
    int count;
};
typedef struct multiCmd{
    //参数
    robj **argv;

    //参数数量
    int argc;

    //命令指针
    struct redisCommand *cmd;
};

事务队列的实现:

这里写图片描述

1.3 执行事务

EXEC命令:当一个处于事务状态的客户端向服务器发送EXEC命令时,这个EXEC命令将立即被服务器执行。服务器遍历这个客户端的事务队列,执行队列中保存的所有命令,最后将执行命令得到的结果全部返回个给客户端。

127.0.0.1:6379> EXEC
1) OK
2) "xiaoming"
3) OK
4) "peter"
127.0.0.1:6379> 

2. WATCH 命令的实现

WATCH命令是一个乐观锁,在EXEC命令执行前,监视热议数量的数据库键,并在EXEC命令执行时,检查被监视的键是否至少有一个已经被修改过了,如果是的话,服务器将拒绝执行事务,并向客户端代表事务执行失败的空回复。

2.1 使用WATCH命令监视数据库键

每个数据库都保存有一个watched_keys字典,键是某个被WATCH命令监视的数据库键,值是一个链表,记录了所有监视相应数据库键的客户端。

typedef struct redisDb{

    //...

    //正在被WATCH命令监视的键
    dict *watched_keys;

    //...

}redisDb;

这里写图片描述

//监视name键和age键
redis> WATCH "name" "age"
OK

2.2 监视机制的触发

所有对数据库进行修改的命令(SET/LPUSH/SADD/ZREM/DEL/FLUSHDB等),在执行后都会调用 multi.c/touchWatchedKey函数对watched_keys字典进行检查,如果有客户端监视,会将监视被修改的客户端的REDIS_DIRTY_CAS标识打开,表示该客户端的事务安全性被破坏。

/* "Touch" a key, so that if this key is being WATCHed by some client the
 * next EXEC will fail. 
 *
 * “触碰”一个键,如果这个键正在被某个/某些客户端监视着,
 * 那么这个/这些客户端在执行 EXEC 时事务将失败。
 */
void touchWatchedKey(redisDb *db, robj *key) {
    list *clients;
    listIter li;
    listNode *ln;

    // 字典为空,没有任何键被监视
    if (dictSize(db->watched_keys) == 0) return;

    // 获取所有监视这个键的客户端
    clients = dictFetchValue(db->watched_keys, key);
    if (!clients) return;

    /* Mark all the clients watching this key as REDIS_DIRTY_CAS */
    /* Check if we are already watching for this key */
    // 遍历所有客户端,打开他们的 REDIS_DIRTY_CAS 标识
    listRewind(clients,&li);
    while((ln = listNext(&li))) {
        redisClient *c = listNodeValue(ln);

        c->flags |= REDIS_DIRTY_CAS;
    }
}

2.3 判断失误是否安全

这里写图片描述

2.4 一个完整的WATCH事务执行过程

(1)监视

c10086> WATCH "name"  //将客户端c10086添加到db[i]->watched_keys[name]的链表中。
OK

(2)创建事务

c10086> MULTI
OK

c10086> SET "name" "peter"
QUEUED

(3)这时另一个客户端修改了”name“键

c999> SET "name" "john"
OK

(4)当c10086再执行EXEC时,已经是事务安全性破坏。服务器拒绝它提交的事务

c10086> EXEC
(nil)

3. 事务的ACID性质

事务总是具有四个性质:

  • 原子性(Atomicity)
  • 一致性(Consistency)
  • 隔离性(Isolation)
  • 耐久性(Durability)

3.1 原子性

事务的原子性:事务中的多个操作当作一个整体来执行,服务器要么执行事务中的所有操作,要么一个操作也不执行。

3.2 一致性

数据库在执行事务之前是一致的,那么在执行事务后,无论事务是否执行成功,数据库也仍是一致的。
”一致“指的是数据符合数据库本身的定义和要求,没有包含非法或者无效的错误数据。

3.3 隔离性

事务的隔离性:即使数据库中有多个事务并发地执行,各个事务之间也不会互相影响,并且在并发状态下执行的事务和串行状态下执行的事务产生的结果完全相同。(有点类似于并发安全性)

3.4 耐久性

事务的耐久性:当事务执行完毕时,结果已经保存到永久性存储介质了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值