博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
15天玩转redis —— 第八篇 你不得不会的事务玩法
阅读量:6670 次
发布时间:2019-06-25

本文共 4396 字,大约阅读时间需要 14 分钟。

   我们都知道redis追求的是简单,快速,高效,在这种情况下也就拒绝了支持window平台,学sqlserver的时候,我们知道事务还算是个比较复杂的东西,

所以这吊毛要是照搬到redis中去,理所当然redis就不是那么简单纯碎的东西了,但是呢,事务是我们写程序无法逃避的场景,所以redis作者折衷的写了个简

化版的事务机制,下面我来扯一下它的蛋蛋。

 

一: 事务实战

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

1. multi,exec

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

输入redis命令,最后用exec执行命令,就像下面这样:

可以看到,我set完命令之后,反馈信息是QUEUED,最后我再执行exec,这些命令才会真正的执行,就是这么的简单,一切执行的就是那么的顺利,

一点都不拖泥带水,牛逼的不要不要的,可能有些人说,其实事务中还有一个rollback操作,但好像在redis中没有看到,哈哈,牛逼哈,很遗憾是

redis中没有rollback操作,比如下面这样。

 

在图中我故意用lpush命令去执行string,可想而知自然不会执行成功,但从结果中,你看到什么了呢?两个OK,一个Error,这就是违反了事务

的原子性,对吧,但是我该怎么反驳呢??? 我会说,错你妹啊。。。连个基本的命令都写错了,你搞个毛啊。。。还写个吊毛代码,reids仅仅

是个数据结构服务器,多简单的一件事情,退一万步说,很明显的错误命令它会直接返回的,比如我故意把lpush写成lpush1:

 

2. watch

  不知道你看完multi后面的三条set命令之后,有没有一种心虚的感觉,怎么说呢,就是只要命令是正确的,redis保证会一并执行,誓死完成

任务,虽然说命令是一起执行的,但是谁可以保证我在执行命令的过程中,其他client不会修改这些值呢???如果修改了这些值,那我的exec

还有什么意义呢???没关系,这种烂大街的需求,redis怎可能袖手旁观???这里的watch就可以助你一臂之力。

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

 上面就是redis手册中关于watch的解释,使用起来貌似很简单,就是我在multi之前,用watch去监视我要修改的key,如果说我在exec之前,

multi之后的这段时间,key被其他client修改,那么exec就会执行失败,返回(nil),就这么简单,我还是来举个例子:

 

 

二:原理探索

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

1. multi

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

1 void multiCommand(redisClient *c) {2     if (c->flags & REDIS_MULTI) {3         addReplyError(c,"MULTI calls can not be nested");4         return;5     }6     c->flags |= REDIS_MULTI;7     addReply(c,shared.ok);8 }

从这段代码中,你可以看到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源代码中是这样写的。

1 typedef struct watchedKey { 2     robj *key; 3     redisDb *db; 4 } watchedKey; 5  6 void watchCommand(redisClient *c) { 7     int j; 8  9     if (c->flags & REDIS_MULTI) {10         addReplyError(c,"WATCH inside MULTI is not allowed");11         return;12     }13     for (j = 1; j < c->argc; j++)14         watchForKey(c,c->argv[j]);15     addReply(c,shared.ok);16 }17 18 /* Watch for the specified key */19 void watchForKey(redisClient *c, robj *key) {20     list *clients = NULL;21     listIter li;22     listNode *ln;23     watchedKey *wk;24 25     /* Check if we are already watching for this key */26     listRewind(c->watched_keys,&li);27     while((ln = listNext(&li))) {28         wk = listNodeValue(ln);29         if (wk->db == c->db && equalStringObjects(key,wk->key))30             return; /* Key already watched */31     }32     /* This key is not already watched in this DB. Let's add it */33     clients = dictFetchValue(c->db->watched_keys,key);34     if (!clients) {35         clients = listCreate();36         dictAdd(c->db->watched_keys,key,clients);37         incrRefCount(key);38     }39     listAddNodeTail(clients,c);40     /* Add the new key to the list of keys watched by this client */41     wk = zmalloc(sizeof(*wk));42     wk->key = key;43     wk->db = c->db;44     incrRefCount(key);45     listAddNodeTail(c->watched_keys,wk);46 }

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

/* 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->db;    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[]中的命令,如下图中的两处信息:

  

 

好了,大概就这么说了,希望对你有帮助哈~~~

 

转载地址:http://gaoxo.baihongyu.com/

你可能感兴趣的文章
HDOJ_ACM_Piggy-Bank
查看>>
【ZJOI2012】灾难
查看>>
如何通过使用Xmanager的图形化界面修改系统
查看>>
数据库MongoDB查询语句--持续更新
查看>>
ios 应用内支付(In-App Purchase,沙盒测试,后台验证)iap
查看>>
noi 1996 登山
查看>>
HDU 3625 第一类斯特林数
查看>>
jQuery--checkbox全选
查看>>
Host '192.168.1.21' is not allowed to connect to this MySQL server
查看>>
【前端攻略】:玩转图片Base64编码
查看>>
Oracle 12c安装报错Installation failed to access the temporary location(无法访问临时位置)...
查看>>
iOS-常见的几种UI布局技巧
查看>>
约瑟夫环之递归算法
查看>>
linux进程的休眠(等待队列)【转】
查看>>
javascript基础知识
查看>>
LeetCode OJ:Integer to Roman(转换整数到罗马字符)
查看>>
redis在Linux上的安装
查看>>
msf
查看>>
灵活就业协议影响应届身份吗,档案应该如何处理
查看>>
校招开篇第一次面试——打响校招的第一枪(二)
查看>>