我们都知道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就可以助你一臂之力。
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->db; 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->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[]中的命令,如下图中的两处信息:
好了,大概就这么说了,希望对你有帮助哈
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
RTX 5090要首发 性能要翻倍!三星展示GDDR7显存
三星在GTC上展示了专为下一代游戏GPU设计的GDDR7内存。
首次推出的GDDR7内存模块密度为16GB,每个模块容量为2GB。其速度预设为32 Gbps(PAM3),但也可以降至28 Gbps,以提高产量和初始阶段的整体性能和成本效益。
据三星表示,GDDR7内存的能效将提高20%,同时工作电压仅为1.1V,低于标准的1.2V。通过采用更新的封装材料和优化的电路设计,使得在高速运行时的发热量降低,GDDR7的热阻比GDDR6降低了70%。
更新日志
- 小骆驼-《草原狼2(蓝光CD)》[原抓WAV+CUE]
- 群星《欢迎来到我身边 电影原声专辑》[320K/MP3][105.02MB]
- 群星《欢迎来到我身边 电影原声专辑》[FLAC/分轨][480.9MB]
- 雷婷《梦里蓝天HQⅡ》 2023头版限量编号低速原抓[WAV+CUE][463M]
- 群星《2024好听新歌42》AI调整音效【WAV分轨】
- 王思雨-《思念陪着鸿雁飞》WAV
- 王思雨《喜马拉雅HQ》头版限量编号[WAV+CUE]
- 李健《无时无刻》[WAV+CUE][590M]
- 陈奕迅《酝酿》[WAV分轨][502M]
- 卓依婷《化蝶》2CD[WAV+CUE][1.1G]
- 群星《吉他王(黑胶CD)》[WAV+CUE]
- 齐秦《穿乐(穿越)》[WAV+CUE]
- 发烧珍品《数位CD音响测试-动向效果(九)》【WAV+CUE】
- 邝美云《邝美云精装歌集》[DSF][1.6G]
- 吕方《爱一回伤一回》[WAV+CUE][454M]