redis真是一個分布式應用場景下的好東西,對於我們的應用設計,功勞大大的!
今天要研究的是基於redis的事務機制以及watch指令(CAS)實現樂觀鎖的過程。
所謂樂觀鎖,就是利用版本號比較機制,只是在讀數據的時候,將讀到的數據的版本號一起讀出來,當對數據的操作結束后,准備寫數據的時候,再進行一次數據版本號的比較,若版本號沒有變化,即認為數據是一致的,沒有更改,可以直接寫入,若版本號有變化,則認為數據被更新,不能寫入,防止臟寫。
下面,看看如何基於redis實現樂觀鎖。
首先,看看redis的事務,涉及到的指令,主要有multi,exec,discard。而實現樂觀鎖的指令,在事務基礎上,主要是watch指令,以及unwatch指令,unwatch通常可以不用!
案例1:redis的純事務
下面是ssh窗口1里面的操作:
127.0.0.1:6379> set hello 1 OK 127.0.0.1:6379> get hello "1" 127.0.0.1:6379> multi OK 127.0.0.1:6379> incr hello QUEUED 127.0.0.1:6379> incr hello #這一步執行完畢后,去另外一個窗口(ssh窗口2),對hello這個key做incr操作,將hello對應的值變成2。完成后,繼續后面的exec指令 QUEUED 127.0.0.1:6379> exec 1) (integer) 3 #注意,這時hello的值是3了,前面執行get hello指令時,值是1喲,說明這個值在其他地方被修改過,這里的其他地方,就是指前面提到的,在另外一個連接窗口里面執行的。 2) (integer) 4 127.0.0.1:6379>
這個情景下,multi和exec之間的指令,依然是可以執行的。
下面的操作,就是在ssh窗口2里面的操作:
127.0.0.1:6379> 127.0.0.1:6379> get hello "1" 127.0.0.1:6379> incr hello (integer) 2 127.0.0.1:6379>
案例2: 利用watch指令,基於CAS機制,簡單的樂觀鎖
下面是ssh窗口1里面的操作:
127.0.0.1:6379> watch hello OK 127.0.0.1:6379> get hello "4" 127.0.0.1:6379> multi OK 127.0.0.1:6379> incr hello QUEUED 127.0.0.1:6379> incr hello #這一步執行完畢后,去另外一個窗口(ssh窗口2),對hello這個key做incr操作,將其值變成5。完成后,繼續后面的exec指令 QUEUED 127.0.0.1:6379> exec (nil) #注意,這是exec執行后返回的是nil,表示事務提交執行失敗 127.0.0.1:6379> 127.0.0.1:6379> get hello #這個時候,查看hello對應的值,就是在另外一個窗口(ssh窗口2)執行incr后的值 "5"
下面是ssh窗口2里面的操作:
127.0.0.1:6379> incr hello (integer) 5 127.0.0.1:6379>
案例3:watch指令在一次事務執行完畢后,即結束其生命周期
下面是ssh窗口1里面的操作:
127.0.0.1:6379> multi #接着上面案例2后,不再輸入watch hello這個指令,直接啟動事務 OK 127.0.0.1:6379> incr hello QUEUED 127.0.0.1:6379> incr hello #這一步執行完畢后,就在另外一個窗口(ssh窗口2),執行incr hello,將hello的值變成6。 QUEUED 127.0.0.1:6379> exec #另外一個窗口(ssh窗口2)里面的操作結束后,繼續來這個窗口執行該指令,依然完成了上面的兩個incr hello的操作。 1) (integer) 7 2) (integer) 8 127.0.0.1:6379>
下面是ssh窗口2里面的操作:
127.0.0.1:6379> incr hello (integer) 6 127.0.0.1:6379>
上述3個案例的操作,指令其實非常的少,兩個窗口的指令全集,截圖如下:
在另外一個窗口(ssh窗口2)中的操作:
通過這個簡單的例子,基於redis的樂觀鎖,可以得出一個結論:
1. 樂觀鎖的實現,必須基於WATCH,然后利用redis的事務。
2. WATCH生命周期,只是和事務關聯的,一個事務執行完畢,相應的watch的生命周期即結束。