redis基礎簡介(三) 事務

2021-07-25 19:00:39 字數 3940 閱讀 6509

和眾多其它資料庫一樣,redis作為nosql資料庫也同樣提供了事務機制。在redis中,multi/exec/discard/watch 這四個命令是我們實現事務的基石。相信對有關係型資料庫開發經驗的開發者而言這一概念並不陌生,即便如此,我們還是會簡要的列出redis中事務的實現特徵:

在事務中的所有命令都將會被序列化的順序執行,事務執行期間,redis不會再為其它客戶端的請求提供任何服務,從而保證了事物中的所有命令被原子的執行。

和關係型資料庫中的事務相比,在redis事務中如果有某一條命令a執行失敗,其後的命令仍然會被繼續執行(只有命令a執行失敗,其他命令均執行成功,這也是redis事務控制中應該急需解決的問題)。

我們可以通過multi命令開啟乙個事務,有關係型資料庫開發經驗的人可以將其理解為」begin transaction」語句。在該語句之後執行的命令都將被視為事務之內的操作,最後我們可以通過執行exec/discard命令來提交/回滾該事務內的所有操作。這兩個redis命令可被視為等同於關係型資料庫中的commit/rollback語句。

在事務開啟之前,如果客戶端與伺服器之間出現通訊故障並導致網路斷開,其後所有待執行的語句都將不會被伺服器執行。然而如果網路中斷事件是發生在客戶端執行exec命令之後,那麼該事務中的所有命令都會被伺服器執行。

事務正常執行

#在當前連線上啟動乙個新的事務。

redis 127.0

.0.1:6379> multi

ok#執行事務中的第一條命令,從該命令的返回結果可以看出,該命令並沒有立即執行,而是存於事務的命令佇列。

redis 127.0

.0.1:6379> incr t1

queued

#又執行乙個新的命令,從結果可以看出,該命令也被存於事務的命令佇列。

redis 127.0

.0.1:6379> incr t2

queued

#執行事務命令佇列中的所有命令,從結果可以看出,佇列中命令的結果得到返回。

redis 127.0

.0.1:6379> exec

1) (integer) 1

2) (integer) 1

事務中存在失敗的命令

#開啟乙個新的事務。

redis 127.0

.0.1:6379> multi

ok#設定鍵a的值為string型別的3。

redis 127.0

.0.1:6379> seta3

queued

#從鍵a所關聯的值的頭部彈出元素,由於該值是字串型別,而lpop命令僅能用於list型別,因此在執行exec命令時,該命令將會失敗。

redis 127.0

.0.1:6379> lpop a

queued

#再次設定鍵a的值為字串4。

redis 127.0

.0.1:6379> seta4

queued

#獲取鍵a的值,以便確認該值是否被事務中的第二個set命令設定成功。

redis 127.0

.0.1:6379> get

aqueued

#從結果中可以看出,事務中的第二條命令lpop執行失敗,而其後的set和get命令均執行成功,這一點是redis的事務與關係型資料庫中的事務之間最為重要的差別。

redis 127.0

.0.1:6379> exec

1) ok

2) (error) err operation against a key holding the wrong kind of

value

3) ok

4) "4"

回滾事務

#為鍵t2設定乙個事務執行前的值。

redis 127.0

.0.1:6379> set t2 tt

ok#開啟乙個事務。

redis 127.0

.0.1:6379> multi

ok#在事務內為該鍵設定乙個新值。

redis 127.0

.0.1:6379> set t2 ttnew

queued

#放棄事務。

redis 127.0

.0.1:6379> discard

ok#檢視鍵t2的值,從結果中可以看出該鍵的值仍為事務開始之前的值。

redis 127.0

.0.1:6379> get t2

"tt"

watch/unwatch命令和基於cas的樂觀鎖

在redis的事務中,watch命令可用於提供cas(check-and-set)功能。假設我們通過watch命令在事務執行之前監控了多個keys,倘若在watch之後有任何key的值發生了變化,exec命令執行的事務都將被放棄,同時返回null multi-bulk應答以通知呼叫者事務執行失敗。例如,我們再次假設redis中並未提供incr命令來完成鍵值的原子性遞增,如果要實現該功能,我們只能自行編寫相應的**。其偽碼如下:

val = get mykey

val = val + 1

set mykey $val

以上**只有在單連線的情況下才可以保證執行結果是正確的,因為如果在同一時刻有多個客戶端在同時執行該段**,那麼就會出現多執行緒程式中經常出現的一種錯誤場景–競態爭用(race condition)。比如,客戶端a和b都在同一時刻讀取了mykey的原有值,假設該值為10,此後兩個客戶端又均將該值加一後set回redis伺服器,這樣就會導致mykey的結果為11,而不是我們認為的12。為了解決類似的問題,我們需要借助watch命令的幫助,見如下**:

watch mykey

val = get mykey

val = val + 1

multi

set mykey $val

exec

和此前**不同的是,新**在獲取mykey的值之前先通過watch命令監控了該鍵,此後又將set命令包圍在事務中,這樣就可以有效的保證每個連線在執行exec之前,如果當前連線獲取的mykey的值被其它連線的客戶端修改,那麼當前連線的exec命令將執行失敗。這樣呼叫者在判斷返回值後就可以獲悉val是否被重新設定成功。

使用watch和multi實現簡單的事務控制示例:

# session 1

127.0

.0.1:6379> keys *

(empty list or set)

127.0

.0.1:6379> set age 10

ok127.0

.0.1:6379> watch age

ok127.0

.0.1:6379> multi

ok127.0

.0.1:6379> set age 11

queued

127.0

.0.1:6379>

# 這個時候先不要提交執行事務(exec)

# 假設在 session 1 事務還沒有開始提交執行的時候,這時候有另外乙個使用者(session 2)對age做了修改

# session 2

127.0

.0.1:6379> set age 22

ok127.0

.0.1:6379>

# 在 session 2 使用者對age修改為22後,session 1 使用者提交執行事務

127.0

.0.1:6379> exec

(nil)

127.0

.0.1:6379> get age

"22"

# 這個時候他發現他的事務並沒有執行成功,age的值是 session 2 使用者修改後的值

# 這就是redis樂觀鎖(watch)起到的作用,這有點類似於svn的版本控制。

Redis的事務分析與簡介

事務 redis 事務可以一次執行多個命令,並且帶有以下兩個重要的保證 事務是乙個單獨的隔離操作 事務中的所有命令都會序列化 按順序地執行。事務在執行的過程中,不會被其他客戶端傳送來的命令請求所打斷。事務是乙個原子操作 事務中的命令要麼全部被執行,要麼全部都不執行。乙個事務從開始到執行會經歷以下三個...

Redis 基礎 Redis 簡介及安裝

remote dictionary server redis 是乙個由salvatore sanfilippo寫的key value儲存系統。redis是乙個開源的使用ansi c語言編寫 遵守bsd協議 支援網路 可基於記憶體亦可持久化的日誌型 key value資料庫,並提供多種語言的api。它...

Redis基礎 五 Redis事務 樂觀鎖

redis 事務可以一次執行多個命令,並且帶有以下三個重要的保證 乙個事務從開始到執行會經歷以下三個階段 我們可以理解redis事物為乙個指令碼,指令碼中如果出現編譯時錯誤 有錯 整條指令碼都不會被執行,並且並拋棄。指令碼中如果出現執行時錯誤 1 0等邏輯錯誤 除了該條指令其他的語句依然會被執行。1...