redis的watch命令?

前言

        面试了一家上市公司,他们问我redis的watch命令是什么意思,当场就蒙了,redis命令数据类型,get,set这些常见的命令知道,但是watch这个命令一时忘了是什么意思?好尴尬,好吧,就是菜。

正文


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);
    }
}

最后

谢谢大家的阅读,原创不易,喜欢就点个赞,这将是我最强的写作动力。如果你觉得文章对你有所帮助,也蛮有趣的,就关注一下我的博客,谢谢。

发布了83 篇原创文章 · 获赞 54 · 访问量 6112

猜你喜欢

转载自blog.csdn.net/qq_43162613/article/details/104186215