為了保證多條命令組合的原子性,Redis提供了簡單的事務功能以及集成Lua腳本來解決這個問題,本文介紹Redis事務,Lua在下一篇文章介紹
一、事務概述
簡單地說,事務表示一組動作,要么全部執行,要么全部不執行。例如在社交網站上用戶A關注了用戶B,那么需要在用戶A的關注表中加入用戶B,并且在用戶B的粉絲表中添加用戶A,這兩個行為要么全部執行,要么全部不執行,否則會出現數據不一致的情況
Redis只提供了四個命令管理事務:
MULTI:用來開啟一個事務。開啟一個事務之后,輸入的命令不會被立即執行,而是進入事務隊列中(入隊),所以可以看見輸入命令的結果顯示為“QUEUED”
WATCH:是一個樂觀鎖。它可以在EXEC命令執行之前,監視任意數量的數據庫鍵,并在EXEC命令執行時,檢查被監視的鍵是否至少有一個已經被修改過了,如果是的話,服務器將拒絕執行事務,并向客戶端返回代表事務執行失敗的空回復
DISCARD:用于取消本次事務,放棄執行事務塊內的所有命令。如果使用了WATCH,DISCARD將取消監視連接監視的所有鍵
EXEC:提交一個事務
所以Redis的事務比較簡單,主要是因為它不支持事務中的回滾特性,同時無法實現命令之間的邏輯關系計算,當然也體現了Redis的“keep it simple”的特性
二、事務演示案例
MULTI+EXEC
下面使用MULTI開啟一個事務,并且執行相關操作,最后使用EXEC提交執行事務內的操作
備注:可以看到事務開啟之后,每次執行的命令結果都會顯示QUEUED,表示命令入隊,但是沒有被執行
MULTI+DISCARD
下面使用MULTI開啟一個事務,并且執行相關操作,最后使用DISCARD終止本次事務,并且事務內的操作全部放棄執行
備注:因為discard已經結束事務了,所以再次輸入exec會顯示沒有匹配的multi
MULTI+WATCH+EXEC
客戶端1:先設置一個字符串,鍵名為key,然后使用watch監聽該鍵。然后開啟事務
客戶端2:在客戶端1事務還未結束的時候,修改key
客戶端1:操作key,并提交事務。因為key被其他客戶端修改,所以EXEC返回nil,事務沒有被執行。然后獲取key,key沒有被改變
三、事務錯誤的處理
如果事務中出現錯誤,那么Reiis的處理機制也不盡相同
①命令錯誤
如果一個事務在入隊命令的過程中,出現了命令不存在,或者命令的格式不正確等情況,那么Redis將拒絕執行這個事務
例如:下面操作錯將set寫成了sett,屬于語法錯誤,會造成整個事務無法執行,key和counter的值未發生變化:
????? ?根據文檔記錄,在Redis 2.6.5以前的版本,即使有命令在入隊過程中發生了錯誤, 事務一樣可以執行,不過被執行的命令只包括那些正確入隊的命令,以下這段代碼是 在Redis 2.6.4版本上測試的,可以看到事務可以正常執行,但只有成功入隊的SET命令 和GET命令被執行了,而錯誤的YAH000O則被忽略了:
redis> MULTI
OK
redis> SET msg "hello"
QUEUED
redis> YAH000O
(error) ERR unknown command ' YAH000O'
redis> GET msg
QUEUED
redis> EXEC
11 OK
2) "hello"
②運行時錯誤(執行錯誤)
有些事務輸入的命令沒有錯誤,但是語法或邏輯有錯誤,這類錯誤不會被立即檢測出來,只有當事務提交時才會被檢測出來
即使在事務的執行過程中發生了錯誤,服務器也不會中斷事務的執行,它會繼續執行事務中余下的其他命令,并且已執行的命令(包括執行命令所產生的結果)不會被出錯的命令影響
因為在事務執行的過程中,出錯的命令會被服務器識別出來,并進行相應的錯誤處理, 所以這些出錯命令不會對數據庫做任何修改,也不會對事務的一致性產生任何影響
③服務器停機
如果Redis服務器在執行事務的過程中停機,那么根據服務器所使用的持久化模式,可能有以下情況出現:
如果服務器運行在無持久化的內存模式下,那么重啟之后的數據庫將是空白的,因此數據總是一致的
如果服務器運行在RDB模式下,那么在事務中途停機不會導致不一致性,因為服務器可以根據現有的RDB文件來恢復數據,從而將數據庫還原到一個一致的狀態。如果找不到可供使用的RDB文件,那么重啟之后的數據庫將是空白的,而空白數據庫總是一致的
如果服務器運行在AOF模式下,那么在事務中途停機不會導致不一致性,因為服務器可以根據現有的AOF文件來恢復數據,從而將數據庫還原到一個一致的狀態。如果找不到可供使用的AOF文件,那么重啟之后的數據庫將是空白的,而空白數據庫總是一致的
綜上所述,無論Redis服務器運行在哪種持久化模式下,事務執行中途發生的停機都不會影響數據庫的一致性