1. 高层视角解读
redis的事务实现是比较简单的,支持CAS操作,watch命令可以锁定某个key,在事务执行时如果检测到watch的key被修改,事务失败。事务成功执行后,会unwatch掉所有观察的keys。
可以参考《Redis设计与实现》里的
事务一章。
事务的实现
2. 底层代码选读
和事务相关的函数都集中在了multi.c文件中
2.1 数据结构
首先回忆一下,一个redis有很多数据库,每个数据库里有一个哈希表存储所有记录,一个哈希表存储所有记录的过期时间,还有一个哈希表存储记录的监控信息。
typedef struct redisDb {
dict *dict; /* The keyspace for this DB */
dict *expires; /* Timeout of keys with a timeout set */
dict *watched_keys; /* WATCHED keys for MULTI/EXEC CAS */
} redisDb;
而客户端存储正在使用的数据库,待执行的事务的命令队列,还有监控信息。
typedef struct redisClient {
redisDb *db; // 当前正在使用的数据库
multiState mstate; /* MULTI/EXEC state */
list *watched_keys; /* Keys WATCHED for MULTI/EXEC CAS */
} redisClient;
注意redisDb和redisClient结构体里各有一个watched_keys,但是它们的类型是不同的
如果用java泛型来表示的话代码如下
class redisDb {
Map<robj, redisClient> watched_keys;
}
class redisClient {
List<watchedKey> watched_keys;
}
另外一些结构,watchedKey 里存着监控信息,multiCmd 存着待执行的命令,multiState 则存着一个命令的数组。
typedef struct watchedKey {
// 被监视的键
robj *key;
// 键所在的数据库
redisDb *db;
} watchedKey;
typedef struct multiCmd {
robj **argv;
int argc;
struct redisCommand *cmd;
} multiCmd;
typedef struct multiState {
multiCmd *commands; /* Array of MULTI commands */
int count; /* Total number of MULTI commands */
} multiState;
2.2 watch和unwatch的代码
void watchForKey(redisClient *c, robj *key) {
list *clients = NULL;
listIter li;
listNode *ln;
watchedKey *wk;
// 检查 key 是否存在于数据库的 watched_keys 字典中
clients = dictFetchValue(c->db->watched_keys,key);
// 如果不存在的话,添加它
if (!clients) {
// 值为链表
clients = listCreate();
// 关联键值对到字典
dictAdd(c->db->watched_keys,key,clients);
}
// 1.添加服务端的某个数据库的watched_keys链表中的key节点
listAddNodeTail(clients,c); //服务端的watched_keys里元素类型是redisClient
wk = zmalloc(sizeof(*wk));
wk->key = key;
wk->db = c->db;
// 2.将客户端添加到链表的末尾
listAddNodeTail(c->watched_keys,wk); //客户端的watched_keys里元素类型是watchedKey
}
void unwatchAllKeys(redisClient *c) {
listIter li;
listNode *ln;
// 遍历链表中所有被客户端监视的键
listRewind(c->watched_keys,&li);
while((ln = listNext(&li))) {
list *clients;
watchedKey *wk;
// 从数据库的 watched_keys 字典的 key 键中
// 删除链表里包含的客户端节点
wk = listNodeValue(ln);
// 取出客户端链表
clients = dictFetchValue(wk->db->watched_keys, wk->key);
// 1.删除服务端的某个数据库的watched_keys链表中的key节点
listDelNode(clients,listSearchKey(clients,c));
// 2.删除客户端的watched_keys链表中的key节点
listDelNode(c->watched_keys,ln);
}
}
代码的各个struct之间你引用我,我引用你,有点绕。但也正是用了这个办法,才能够将struct关联起来,然后通过c->db->watched_keys这样的形式来设置一个值。
可以看到watch和unwatch要同时操作服务端和客户端的两个watched_keys。两个watched_keys类型是不一样的,所以代码有点绕。
由于C语言里没有泛型,所以代码看起来不直观,需要深层分析才能看清楚。一不小心编码错了编译没问题,要等运行时才会出错。
而C++则引入了模板,java和c#有泛型来使得代码看起来更直观,也更安全。
分享到:
相关推荐
NULL 博文链接:https://xpenxpen.iteye.com/blog/2090620
redis源码阅读中文分析注释
NULL 博文链接:https://xpenxpen.iteye.com/blog/2093786
NULL 博文链接:https://xpenxpen.iteye.com/blog/2088678
NULL 博文链接:https://xpenxpen.iteye.com/blog/2091126
scala连接redis哨兵模式 demo 使用scala的redis库(csdn)————程序
redis安装遇到的问题——linux centos7.5,包括未安装gcc,make不能编译等所有问题
Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。 附件里面包括redis源码,phpredis源码,redis指令及文档
Redis,即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。...本文适合Redis初学者和进阶者阅读,是一份全面而实用的学习笔记。
redis源码级别的分析。需要具备c语言基础的开发学习,十分详细,带你全程深入了解redis。
redis的可视化工具,方便进行测试
Redis全套学习笔记 完整版pdf.rar set:添加键值对 get:获取值 apend:追价值 strlen:获取值的长度 setnx:key不存在时,设置key的值 incr:原子递增1 decr:原子递减1 incrby/decrby:递增或者递减指定的数字 ...
Redis全套学习笔记-带章节目录
springBoot集成Redis源码,springBoot集成Redis源码,springBoot集成Redis源码
redis的学习笔记
Redis源码阅读参考资料1
Redis学习笔记.pdf 含目录 #资源达人分享计划#
Redis的Windows版本源码
学习狂神说-Redis视频笔记,笔记完整并且加入了个人的理解和认知,笔记工整清晰,而且适合记忆学习。
代码redis-study.rar