redis事務(wù)_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
我們都知道redis追求的是簡(jiǎn)單,快速,高效,在這種情況下也就拒絕了支持window平臺(tái),學(xué)sqlserver的時(shí)候,我們知道事務(wù)還算是個(gè)比較復(fù)雜的東西,所以這要是照搬到redis中去,理所當(dāng)然redis就不是那么簡(jiǎn)單純碎的東西了,但是呢,事務(wù)是我們寫程序無法逃避的場(chǎng)景,所以redis作者折衷的寫了個(gè)簡(jiǎn)化版的事務(wù)機(jī)制。
一: 事務(wù)實(shí)戰(zhàn)
具體到事務(wù)是什么,要保證什么。。。這個(gè)我想沒必要說了,先不管三七二十一,看一下redis手冊(cè),領(lǐng)略下它的魔力。
1. multi,exec
還記得sqlserver是怎么玩的嗎?一般都是這樣的三個(gè)步驟,生成事務(wù),產(chǎn)生命令,執(zhí)行事務(wù),對(duì)吧,而對(duì)應(yīng)redis呢??multi就是生成事務(wù),然后輸入redis命令,最后用exec執(zhí)行命令,就像下面這樣:
可以看到,我set完命令之后,反饋信息是QUEUED,最后我再執(zhí)行exec,這些命令才會(huì)真正的執(zhí)行,就是這么的簡(jiǎn)單,一切執(zhí)行的就是那么的順利,一點(diǎn)都不拖泥帶水,可能有些人說,其實(shí)事務(wù)中還有一個(gè)rollback操作,但好像在redis中沒有看到,很遺憾是redis中沒有rollback操作,比如下面這樣。
在圖中我故意用lpush命令去執(zhí)行string,可想而知自然不會(huì)執(zhí)行成功,但從結(jié)果中,你看到什么了呢??jī)蓚€(gè)OK,一個(gè)Error,這就是違反了事務(wù)的原子性,但是我該怎么反駁呢??? reids僅僅是個(gè)數(shù)據(jù)結(jié)構(gòu)服務(wù)器,多簡(jiǎn)單的一件事情,退一萬(wàn)步說,很明顯的錯(cuò)誤命令它會(huì)直接返回的,比如我故意把lpush寫成lpush1:
2. watch
不知道你看完multi后面的三條set命令之后,有沒有一種心虛的感覺,怎么說呢,就是只要命令是正確的,redis保證會(huì)一并執(zhí)行,誓死完成任務(wù),雖然說命令是一起執(zhí)行的,但是誰(shuí)可以保證我在執(zhí)行命令的過程中,其他client不會(huì)修改這些值呢???如果修改了這些值,那我的exec還有什么意義呢???沒關(guān)系,這種爛大街的需求,redis怎可能袖手旁觀???這里的watch就可以助你一臂之力。
WATCH WATCH key [key ...]
監(jiān)視一個(gè)(或多個(gè)) key ,如果在事務(wù)執(zhí)行之前這個(gè)(或這些) key 被其他命令所改動(dòng),那么事務(wù)將被打斷。
上面就是redis手冊(cè)中關(guān)于watch的解釋,使用起來貌似很簡(jiǎn)單,就是我在multi之前,用watch去監(jiān)視我要修改的key,如果說我在exec之前,multi之后的這段時(shí)間,key被其他client修改,那么exec就會(huì)執(zhí)行失敗,返回(nil),就這么簡(jiǎn)單,我還是來舉個(gè)例子:
二:原理探索
關(guān)于事務(wù)操作的源代碼,大多都在redis源碼中的multi.c 文件中,接下來我會(huì)一個(gè)一個(gè)的簡(jiǎn)單剖析一下:
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只是簡(jiǎn)單的把redisClient的REDIS_MULTI狀態(tài)打開,告訴這個(gè)redis客戶端已經(jīng)進(jìn)入事務(wù)模式了。
2. 生成命令
在redisClient中,里面有一個(gè)multiState命令:
typedef struct redisClient { 。。。 multiState mstate; /* MULTI/EXEC state */ 。。。 } redisClient;
從注釋中你大概也看到了這個(gè)命令和multi/exec肯定有關(guān)系,接下來我很好奇的看看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這個(gè)枚舉中,你可以看到下面有一個(gè)*command命令,從注釋中可以看到它其實(shí)指向的是一個(gè)數(shù)組,它就是你的若干條命令啦。。。下面還有一個(gè)count,可以看到是實(shí)際的commands的總數(shù)。
3. watch
為了方便說到后面的exec,這里想說一下watch大概是怎么實(shí)現(xiàn)的,在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); }
這段代碼中大概最核心的一點(diǎn)就是:
/* This key is not already watched in this DB. Let's add it */ clients = dictFetchValue(c->db->watched_keys,key);
就是通過dicFetchValue這個(gè)字典方法,從watched_keys中找到指定key的value,而這個(gè)value是一個(gè)clients的鏈表,說明人家其實(shí)是想找到關(guān)于這個(gè)key的所有client,最后還會(huì)將本次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是個(gè)字典結(jié)構(gòu),字典的鍵為上面的key1,key2。。。,value為client的鏈表,這樣的話,我就非常清楚某個(gè)key中是被哪些client監(jiān)視著的。
4.exec
這個(gè)命令里面大概做了兩件事情:
<1>: 判斷c->flags=REDIS_DIRTY_EXEC 打開與否,如果是的話,取消事務(wù)discardTransaction(c),也就是說這個(gè)key已經(jīng)被別的client修改了。
<2>: 如果沒有修改,那么就for循環(huán)執(zhí)行comannd[]中的命令,如下圖中的兩處信息:
好了,大概就這么說了,希望對(duì)你有幫助哈
- PHP+Redis事務(wù)解決高并發(fā)下商品超賣問題(推薦)
- redis中的事務(wù)操作案例分析
- redis事務(wù)常用操作詳解
- Redis事務(wù)涉及的watch、multi等命令詳解
- python實(shí)現(xiàn)redis三種cas事務(wù)操作
- redis中事務(wù)機(jī)制及樂觀鎖的實(shí)現(xiàn)
- Redis 基礎(chǔ)教程之事務(wù)的使用方法
- Redis 事務(wù)與過期時(shí)間詳細(xì)介紹
- 【Redis緩存機(jī)制】詳解Java連接Redis_Jedis_事務(wù)
- Redis教程(八):事務(wù)詳解
- Redis 事務(wù)知識(shí)點(diǎn)相關(guān)總結(jié)
相關(guān)文章
Redis高效率原因及數(shù)據(jù)結(jié)構(gòu)分析
這篇文章主要為大家詳細(xì)的介紹了Redis高效的原因以及分析了Redis高效的數(shù)據(jù)結(jié)構(gòu),有需要的朋友可以借鑒參考下,希望能夠有所幫助2021-09-09redis和rabbitmq實(shí)現(xiàn)延時(shí)隊(duì)列的示例代碼
在高并發(fā)場(chǎng)景下,延遲隊(duì)列顯得尤為重要,本文主要介紹了兩種方式,redis和rabbitmq實(shí)現(xiàn)延時(shí)隊(duì)列,具有一定的參考價(jià)值,感興趣的可以了解一下2024-03-03CentOS系統(tǒng)中Redis數(shù)據(jù)庫(kù)的安裝配置指南
Redis是一個(gè)基于主存存儲(chǔ)的數(shù)據(jù)庫(kù),性能很強(qiáng),這里我們就來看一下CentOS系統(tǒng)中Redis數(shù)據(jù)庫(kù)的安裝配置指南,包括將Redis作為系統(tǒng)服務(wù)運(yùn)行的技巧等,需要的朋友可以參考下2016-06-06淺談Redis?中的過期刪除策略和內(nèi)存淘汰機(jī)制
本文主要介紹了Redis?中的過期刪除策略和內(nèi)存淘汰機(jī)制,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-04-04利用redis lua腳本實(shí)現(xiàn)時(shí)間窗分布式限流
Lua是一種輕量小巧的腳本語(yǔ)言,Redis是高性能的key-value內(nèi)存數(shù)據(jù)庫(kù),在部分場(chǎng)景下,是對(duì)關(guān)系數(shù)據(jù)庫(kù)的良好補(bǔ)充,本文給大家介紹了如何利用redis lua腳本實(shí)現(xiàn)時(shí)間窗分布式限流,需要的朋友可以參考下2024-03-03Redis+AOP+自定義注解實(shí)現(xiàn)限流
這篇文章主要為大家詳細(xì)介紹了如何利用Redis+AOP+自定義注解實(shí)現(xiàn)個(gè)小功能:自定義攔截器限制訪問次數(shù),也就是限流,感興趣的可以了解一下2022-06-06