watch 用于在进行事务操作的最后一步也就是在执行exec 之前对某个key进行监视
如果这个被监视的key被改动,那么事务就被取消,否则事务正常执行.
一般在MULTI 命令前就用watch命令对某个key进行监控.如果想让key取消被监控,可以用unwatch命令
被监视的key会被保存在两个地方
一个是:
typedef struct redisClient {
// 被监视的键
list *watched_keys; /* Keys WATCHED for MULTI/EXEC CAS */
} redisClient;
所有被监视的key 都保存在redisClient中的list * watched_keys这个结构体中
typedef struct watchedKey {
// 被监视的key
robj *key;
// 被监视的key所在的DB
redisDb *db;
} watchedKey;
另一个是
typedef struct redisDb {
// 被监视的key 所在的dict.被监视的key和所在的client组成一个dict
dict *watched_keys; /* WATCHED keys for MULTI/EXEC CAS */
} redisDb;
watch命令的实现如下:
void watchCommand(redisClient *c) {
int j;
// 可以看到watch命令必须要在事务执行之前执行,REDIS_MULTI 这个falgs是在multi命令中置位的
// 也就是watch函数必须在multi命令之前执行
if (c->flags & REDIS_MULTI) {
addReplyError(c,"WATCH inside MULTI is not allowed");
return;
}
// 前面我们之前被监视的key是保存在redisClient中的list * watched_keys这个结构体中
for (j = 1; j < c->argc; j++)
watchForKey(c,c->argv[j]);
addReply(c,shared.ok);
}
void watchForKey(redisClient *c, robj *key) {
list *clients = NULL;
listIter li;
listNode *ln;
watchedKey *wk;
//首先检测key是否已经存在redisClient中的watched_keys 这个list中
listRewind(c->watched_keys,&li);
while((ln = listNext(&li))) {
wk = listNodeValue(ln);
// 判断相等的标准是key的字符串相等,db的指针指向同一个db
if (wk->db == c->db && equalStringObjects(key,wk->key))
return; /* Key already watched */
}
clients = dictFetchValue(c->db->watched_keys,key);
//如果key对应的client 还不在的话,则通过listCreat新建一个clients指针,并
// 把key和client 作为dict 添加到watched_keys中
//clients 为null,说明这个client是第一次添加被监视的key
if (!clients) {
// 值为链表
clients = listCreate();
// 关联键值对到字典
dictAdd(c->db->watched_keys,key,clients);
incrRefCount(key);
}
// 将客户端添加到链表的末尾
listAddNodeTail(clients,c);
// 将新的watchedkey 田间道wacthe_keys链表的结尾
wk = zmalloc(sizeof(*wk));
wk->key = key;
wk->db = c->db;
// key的引用计数加1
incrRefCount(key);
listAddNodeTail(c->watched_keys,wk);
}
仅仅是这个robj对应对应的refcount 即引用计数加1
void incrRefCount(robj *o) {
o->refcount++;
}
与之相反的unwatch命令实现如下:
void unwatchAllKeys(redisClient *c) {
listIter li;
listNode *ln;
//如果key没有被监视,就不必取消了,直接返回
if (listLength(c->watched_keys) == 0) return;
// 遍历watched_keys 中所有被监视的key
listRewind(c->watched_keys,&li);
while((ln = listNext(&li))) {
list *clients;
watchedKey *wk;
/* Lookup the watched key -> clients list and remove the client
* from the list */
// 将client 从 被监视的ky中移除
wk = listNodeValue(ln);
// 取出客户端链表
clients = dictFetchValue(wk->db->watched_keys, wk->key);
// 断言client 不能为null
redisAssertWithInfo(c,NULL,clients != NULL);
// 删除client
listDelNode(clients,listSearchKey(clients,c));
/* Kill the entry at all if this was the only client */
// client为null,则说明没有这个key 没有对应的clint有监视了,因此删除这个key
if (listLength(clients) == 0)
dictDelete(wk->db->watched_keys, wk->key);
/* Remove this watched key from the client->watched list */
// 移除key这个节点
listDelNode(c->watched_keys,ln);
// keys的引用计数减1
decrRefCount(wk->key);
//释放wk 占用的内存
zfree(wk);
}
}
redis中的watch命令
最新推荐文章于 2023-11-22 17:19:27 发布