redis事物介紹與應用

研究reids與使用已經有幾個月的時間,中間自己總結了不少文檔以及相關資料,接下來時間,會陸續分享一些相關資料,並且介紹其在我們應用中的使用情況。

下面是redis事物的相關介紹(參考redis.io):

一.事物的使用

1.Redis事物通過MULTI命令開始。 這條命令總是返回OK。

2.然後用戶可以執行多條指令,redis不會馬上執行這些指令,還只是放入到隊列中。

3.當執行exec指令時,所有的指令執行。

4.調用discard指令,將會flush事物隊列,並且退出事物。

如下:

redis 127.0.0.1:6379> multi

OK

redis 127.0.0.1:6379> set foo 1

QUEUED

redis 127.0.0.1:6379> incr foo

QUEUED

redis 127.0.0.1:6379> incr foo

QUEUED

redis 127.0.0.1:6379> exec

1) OK

2) (integer) 2

3) (integer) 3

 

從以上會話中能看到multi命令返回的回覆是一個數組,每個元素即是事物中每條指令的回覆,並且跟指令發佈的順序一樣。當redis連接在multi請求下,所有的命令回覆都是queued,除非這條指令的語句法不正確。而一些指令語法正確,但執行階段出錯也是允許的。

如以下

redis 127.0.0.1:6379> multi

OK

redis 127.0.0.1:6379> set t 13

QUEUED

redis 127.0.0.1:6379> lpop t

QUEUED

redis 127.0.0.1:6379> exec

1) OK

2) (error) ERR Operation against a keyholding the wrong kind of value

對於這種err,需要客戶端給予合理的提示。

需要注意的是,所有在隊列中的指令都會被執行,redis不會終止指令的執行。

二.取消隊列指令

Discard爲取消命令隊列。可以終斷一個事物。不會有命令會被執行,並且連接的狀態是正常的。

如:

> SET foo 1

OK

> MULTI

OK

> INCR foo

QUEUED

> DISCARD

OK

> GET foo

"1"

三.Optimistic locking using check-and-set(樂觀鎖)

watch指令在redis事物中提供了CAS的行爲。爲了檢測被watch的keys在是否有多個clients改變時引起衝突,這些keys將會被監控。如果至少有一個watch的key在執行exec命令前被修改,整個事物將會被終止,並且執行exec會得到null的回覆。

例如:一個key自增長(假設redis不提供incr的功能)

val = GET mykey

val = val + 1

SET mykey $val

以上指令執行,如果是單一的client,整個操作是沒問題的。如果多個client在同一時間操作。如client A與 client B讀取了老的值,假如是10,這個值在兩個client將會被增長到11,最後set這個key值時,這個key最終是11還不是12.

watch能夠很好的處理這種問題:

WATCH mykey

val = GET mykey

val = val + 1

MULTI

SET mykey $val

EXEC

 

使用以上代碼,如果在執行watch與exec指令這段時間裏有其它客戶端修改此key值,此事物將執行失敗。以上形式的鎖被稱爲樂觀鎖。在大多數使用場合中,多併發將會處理不能的keys,因爲衝突不太可能。(通常沒有必要重複操作)

四. Watch 指令說明

watch指令是exec指令的執行條件:保證在執行redis事物操作時沒有任何client修改被watched的keys.否則事物不會執行。(注意:如果watch一個不穩定的key並且key過期,exec仍然會執行這條指令),當exec指令被調用,所有的keys將是unwatched,無論事物是否被終止。當client連接關閉後,所有的keys也會變成unwatched.爲了flush所有的watched keys,也可以使用unwatch指令.有時間使用樂觀鎖鎖住一些keys是很有用的,因爲可能需要選擇的keys需要事物操作,但是在執行讀取現有的keys的內容後發現不需要繼續執行,這時只要使用unwatch指令,使此連接能夠繼續被使用做其它新的事物操作。

 

ZPOP(取sorted set中socre最低的元素)指令就是通過watch指令實現的

WATCH zset

element = ZRANGE zset 0 0

MULTI

ZREM zset element

EXEC

五.總結

1.redis事物實現,multi開始,所有指令會被放入到隊列中。當調用exec後,隊列中所有指令會依次被執行。

2.multi-exec中指令執行時,所有指令只要語法合理都會被寫入隊列中。隊列執行時,指令有可能會執行失敗,但不影響其它指令執行。

3.redis事物提供了樂觀鎖,通過watch指令可以實現CAS操作。watch--multi--exec操作

在給key加上樂觀鎖後,當在執行exec指令前,有其它client修改此key,此事物將執行失敗。從而保證原子操作。

 

說明:對於redis事物的應用其實需要靈活使用,上面介紹的例子是從官網翻譯而來。其實在實際中可以通過watch一些標記位來保證多線程下緩存與數據庫數據庫的一致性。(我們的系統是分佈式緩存與數據庫的結合使用,緩存需要跟據數據庫的一致性很重要,下面舉例我們應用中的一個場景:)

如一個service方法,serviceA,執行DAO方法(1),然後更新緩存(2),兩個併發線程,線程一執行了方法1,此刻他需要把DAO相關的數據更新到緩存2中,多線程情況下,線程二在線程一執行1後,也同樣執行1,2相關的操作,並且比線程一優先完成,這樣將導致線程一在執行2時,將出現緩存數據與數據庫不一致的現象。(以上是針對單帳號的多併發操作,發生的概率還是存在),對於以上問題我們的解決方案是:

1.      帳號爲acc,爲每個acc在緩存中增加一個tag標識.

2.      當線程一執行方法1前,設置標記位tag.

3.      當執行方法2時,將會watch tag,並且比較tag是否發生了修改,如果一旦發生修改,則此次緩存操作不將更新,並清空此acc緩存。

4.      如果tag值達到預期,則提交緩存更新,在提交緩存這段時間,如果tag發生變換,則redisexec提交時,會返回null ,這樣,雖然緩存內容更新成功,但跟據返回結果,可以即時清除此acc的緩存,從而清空了緩存的髒數據。

5.      通過以上事物保證了緩存數據與數據庫數據不一致性的時間很短,甚至可以忽略,因爲基本上在MS級別上。

6.      (我們的應用在緩存數據不存在acc的情況下,會嘗試從數據庫讀取,而緩存的作用只是緩解我們系統數據庫的壓力,這樣實現,很好的達到了我們的預期效果).


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章