我們都知道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就可以助你一臂之力。
上面就是redis手冊中關于watch的解釋,使用起來貌似很簡單,就是我在multi之前,用watch去監視我要修改的key,如果說我在exec之前,
multi之後的這段時間,key被其他client修改,那麼exec就會執行失敗,傳回(nil),就這麼簡單,我還是來舉個例子:
二:原理探索
關于事務操作的源代碼,大多都在redis源碼中的multi.c 檔案中,接下來我會一個一個的簡單剖析一下:
1. multi
在redis的源代碼中,它大概是這麼寫的:
從這段代碼中,你可以看到multi隻是簡單的把redisclient的redis_multi狀态打開,告訴這個redis用戶端已經進入事務模式了,對吧。
2. 生成指令
在redisclient中,裡面有一個multistate指令:
從注釋中你大概也看到了這個指令和multi/exec肯定有關系,接下來我很好奇的看看multistate的定義:
從multistate這個枚舉中,你可以看到下面有一個*command指令,從注釋中可以看到它其實指向的是一個數組,這個數組我想你閉着眼睛都
能想得到吧。。。它就是你的若幹條指令啦。。。下面還有一個count,可以看到是實際的commands的總數。
3. watch
為了友善說到後面的exec,這裡想說一下watch大概是怎麼實作的,在multi.c源代碼中是這樣寫的。
這段代碼中大概最核心的一點就是:
就是通過dicfetchvalue這個字典方法,從watched_keys中找到指定key的value,而這個value是一個clients的連結清單,說明人家其實是想找到
關于這個key的所有client,對吧,最後還會将本次key塞入到redisclient的watched_keys字典中,如下代碼:
如果非要畫圖,大概就是這樣:
其中watched_key是個字典結構,字典的鍵為上面的key1,key2。。。,value為client的連結清單,這樣的話,我就非常清楚某個key
中是被哪些client監視着的,對吧。
4.exec
這個指令裡面大概做了兩件事情:
<1>: 判斷c->flags=redis_dirty_exec 打開與否,如果是的話,取消事務discardtransaction(c),也就是說這個key已經
被别的client修改了。
<2>: 如果沒有修改,那麼就for循環執行comannd[]中的指令,如下圖中的兩處資訊:
好了,大概就這麼說了,希望對你有幫助哈~~~