天天看點

Redis之事務事務基本用法redis事務沒有原子性discard (放棄執行)使用管道優化watch指令

事務

關系型資料庫通常會通過事務保證ACID,即原子性、隔離性、持久性、一緻性,Redis雖然也有事務,但是沒法完全支援事務的這幾種特性,

Redis是單線程的,是以符合事務的隔離性,同時Redis具備備份功能,也符合持久性,但是一緻性和原子性是沒有的。

基本用法

關系型資料庫的事務指令一般是begin(開始)、commit(送出)、rollback(復原)三者。

begin();
try {
    command1();
    command2();
    commit();
} catch (Exception e) {
    rollback();
}           

redis也有三個與之對應,multi(開始)、exec(執行)、discard(丢棄)。

127.0.0.1:6379> multi

OK

127.0.0.1:6379(TX)> incr test

QUEUED

127.0.0.1:6379(TX)> incr test

QUEUED

127.0.0.1:6379(TX)> exec

1) (integer) 1

2) (integer) 2
           

當事務multi開始時,之後的所有指令都将被存在服務端的一個事務隊列中,最終服務端收到exec指令時,才開始執行整個事務隊列,執行完畢後一次性傳回所有的運作結果,

因為redis的單線程特性,不用擔心事務指令隊列執行時被其他指令打攪,是以redis事務是可以保證隔離性的。

下圖是事務執行的完成過程,multi開啟一個事務,服務端傳回OK,後續的操作服務端都傳回QUEUE,這意味着指令都被放入到隊列中,等到exec時,服務端開始執行并傳回結果。

Redis之事務事務基本用法redis事務沒有原子性discard (放棄執行)使用管道優化watch指令

redis事務沒有原子性

事務的原子性是指事務要麼完全成功,要麼全部失敗,而Redis是不支援事務原子性的可以用下面的demo示範,

通過結果可以看到,事務中最後一個指令執行失敗了,因為data的值并不是一個數值,是以無法自增,根據原子性此時應該失敗,但是下面的将data設定為hi依然執行了,并且事務結束後我們擷取到的也是hi,

redis事務執行時出現執行失敗後續的指令依然會得到執行,是以說redis事務是沒有原子性的。

127.0.0.1:6379> get data

"nihao"

127.0.0.1:6379> multi

OK

127.0.0.1:6379(TX)> set data hello

QUEUED

127.0.0.1:6379(TX)> incr data

QUEUED

127.0.0.1:6379(TX)> set data hi

QUEUED

127.0.0.1:6379(TX)> exec

1) OK

2) (error) ERR value is not an integer or out of range

3) OK

127.0.0.1:6379> get data

"hi"
           

discard (放棄執行)

discard指令用于丢棄事務緩存的指令隊列裡的所有指令,當然要在exec指令前執行,可以看到下面的demo,discard後,meta依然是nil,并沒有發生指令執行的情況。

127.0.0.1:6379> get meta

(nil)

127.0.0.1:6379> multi

OK 

127.0.0.1:6379(TX)> incr meta

QUEUED

127.0.0.1:6379(TX)> incr meta

QUEUED

127.0.0.1:6379(TX)> discard

OK

127.0.0.1:6379> get meta

(nil)
           

使用管道優化

Redis事務每次發送指令也會造成一次網絡讀寫,當事務的指令較多,也會造成網絡耗時的開銷,是以一般事務可以搭配管道技術一起使用,節約網絡開銷,使程式更快。

watch指令

watch指令用在事務開始之前,監控一個變量,當事務開始執行時,發現該變量的值和watch時的值不一樣,将會終止執行,并傳回一個null給用戶端。

一般業務場景中可以用做樂觀鎖,例如需要讀取redis中的某個值在記憶體中做計算,最後把計算的值放回去,因為這個過程不是原子性的,很有可能讀寫過程中有别的線程把這個值改了并存到了redis中,這會造成混亂,

使用redis鎖可以防止這個問題,但是分布式鎖是悲觀鎖,如果并沒有人在這期間修改該值,也造成了擷取鎖和釋放鎖的資源浪費,根據上面的描述watch比較适合做這個事情,

在事務開始前watch住該值,如果事務exec時發現該值沒有改變,則正常執行,否則傳回null告訴用戶端執行失敗,用戶端做重試或其他業務邏輯處理。

需要注意的是redis進制在multi和exec執行watch,否則會出錯

ERR WATCH inside MULTI is not allowed

在下方demo中,使用了watch來對redis中的一個key進行自增操作,最終的值正确為10,watch在其中配合事務做出了樂觀鎖的效果,頗有CAS的味道。

Redis之事務事務基本用法redis事務沒有原子性discard (放棄執行)使用管道優化watch指令
Redis之事務事務基本用法redis事務沒有原子性discard (放棄執行)使用管道優化watch指令

代碼位址:

https://github.com/qiaomengnan16/redis-demo/tree/main/redis-watch

繼續閱讀