redis事务的示例分析

36次阅读
没有评论

共计 3772 个字符,预计需要花费 10 分钟才能阅读完成。

这篇文章将为大家详细讲解有关 redis 事务的示例分析,丸趣 TV 小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。

一:  事务实战

具体到事务是什么,要保证什么。。。这个我想没必要说了,先不管三七二十一,看一下 redis 手册,领略下它的魔力。

1. multi,exec

还记得 sqlserver 是怎么玩的吗?一般都是这样的三个步骤,生成事务,产生命令,执行事务,对吧,而对应 redis 呢??multi 就是生成事务,然后输入 redis 命令,最后用 exec 执行命令,就像下面这样:

可以看到,我 set 完命令之后,反馈信息是 QUEUED,最后我再执行 exec,这些命令才会真正的执行,就是这么的简单,一切执行的就是那么的顺利,一点都不拖泥带水,可能有些人说,其实事务中还有一个 rollback 操作,但好像在 redis 中没有看到,很遗憾是 redis 中没有 rollback 操作,比如下面这样。

在图中我故意用 lpush 命令去执行 string,可想而知自然不会执行成功,但从结果中,你看到什么了呢?两个 OK,一个 Error,这就是违反了事务的原子性,但是我该怎么反驳呢??? reids 仅仅是个数据结构服务器,多简单的一件事情,退一万步说,很明显的错误命令它会直接返回的,比如我故意把 lpush 写成 lpush2:

2. watch

不知道你看完 multi 后面的三条 set 命令之后,有没有一种心虚的感觉,怎么说呢,就是只要命令是正确的,redis 保证会一并执行,誓死完成任务,虽然说命令是一起执行的,但是谁可以保证我在执行命令的过程中,其他 client 不会修改这些值呢???如果修改了这些值,那我的 exec 还有什么意义呢???没关系,这种烂大街的需求,redis 怎可能袖手旁观???这里的 watch 就可以助你一臂之力。

WATCH
WATCH key [key ...]

监视一个 (或多个) key ,如果在事务执行之前这个 (或这些) key  被其他命令所改动,那么事务将被打断。

上面就是 redis 手册中关于 watch 的解释,使用起来貌似很简单,就是我在 multi 之前,用 watch 去监视我要修改的 key,如果说我在 exec 之前,multi 之后的这段时间,key 被其他 client 修改,那么 exec 就会执行失败,返回(nil),就这么简单,我还是来举个例子:

 

二:原理探索

关于事务操作的源代码,大多都在 redis 源码中的 multi.c  文件中,接下来我会一个一个的简单剖析一下:

1. multi

在 redis 的源代码中,它大概是这么写的:

void multiCommand(redisClient *c) { if (c- flags   REDIS_MULTI) {
 addReplyError(c, MULTI calls can not be nested 
 return;
 }
 c- flags |= REDIS_MULTI;
 addReply(c,shared.ok);

从这段代码中,你可以看到 multi 只是简单的把 redisClient 的 REDIS_MULTI 状态打开,告诉这个 redis 客户端已经进入事务模式了。

2.  生成命令

在 redisClient 中,里面有一个 multiState 命令:

typedef struct redisClient {
 。。。 multiState mstate; /* MULTI/EXEC state */
 。。。} redisClient;

从注释中你大概也看到了这个命令和 multi/exec 肯定有关系,接下来我很好奇的看看 multiState 的定义:

typedef struct multiState {
 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;

从 multiState 这个枚举中,你可以看到下面有一个 *command 命令,从注释中可以看到它其实指向的是一个数组,它就是你的若干条命令啦。。。下面还有一个 count,可以看到是实际的 commands 的总数。 

3. watch

为了方便说到后面的 exec,这里想说一下 watch 大概是怎么实现的,在 multi.c 源代码中是这样写的。

typedef struct watchedKey {
 robj *key;
 redisDb *db;
 } watchedKey;
 
 void watchCommand(redisClient *c) {
 int j;
 
 if (c- flags   REDIS_MULTI) {
 addReplyError(c, WATCH inside MULTI is not allowed 
 return;
 }
 for (j = 1; j   c- argc; j++)
 watchForKey(c,c- argv[j]);
 addReply(c,shared.ok);
 }
 
 /* Watch for the specified key */
 void watchForKey(redisClient *c, robj *key) {
 list *clients = NULL;
 listIter li;
 listNode *ln;
 watchedKey *wk;
 
 /* Check if we are already watching for this key */
 listRewind(c- watched_keys, li);
 while((ln = listNext( li))) { wk = listNodeValue(ln);
 if (wk- db == c- db   equalStringObjects(key,wk- key))
 return; /* Key already watched */
 }
 /* This key is not already watched in this DB. Let s add it */
 clients = dictFetchValue(c- db- watched_keys,key);
 if (!clients) { clients = listCreate();
 dictAdd(c- db- watched_keys,key,clients);
 incrRefCount(key);
 }
 listAddNodeTail(clients,c);
 /* Add the new key to the list of keys watched by this client */
 wk = zmalloc(sizeof(*wk));
 wk- key = key;
 wk- db = c- 
 incrRefCount(key);
 listAddNodeTail(c- watched_keys,wk);
 }

这段代码中大概最核心的一点就是:

 /* This key is not already watched in this DB. Let s add it */
 clients = dictFetchValue(c- db- watched_keys,key);

就是通过 dicFetchValue 这个字典方法,从 watched_keys 中找到指定 key 的 value,而这个 value 是一个 clients 的链表,说明人家其实是想找到关于这个 key 的所有 client,最后还会将本次 key 塞入到 redisclient 的 watched_keys 字典中,如下代码:

 /* Add the new key to the list of keys watched by this client */
 wk = zmalloc(sizeof(*wk));
 wk- key = key;
 wk- db = c- 
 incrRefCount(key);
 listAddNodeTail(c- watched_keys,wk);

如果非要画图,大概就是这样:

其中 watched_key 是个字典结构,字典的键为上面的 key1,key2。。。,value 为 client 的链表,这样的话,我就非常清楚某个 key 中是被哪些 client 监视着的。

4.exec

这个命令里面大概做了两件事情:

1 :  判断 c - flags=REDIS_DIRTY_EXEC  打开与否,如果是的话,取消事务 discardTransaction(c),也就是说这个 key 已经被别的 client 修改了。

2 :  如果没有修改,那么就 for 循环执行 comannd[] 中的命令,如下图中的两处信息:

关于“redis 事务的示例分析”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-08-04发表,共计3772字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)