在使用關係型數據庫的時候,爲了保證數據的ACID(Atomicity:原子性,Consistency:一致性,Isolation:隔離性,Durability:持久性)我們經常會使用事務,要麼全部提交成功,要麼失敗全部失敗,不會存在中間狀態。那麼我們Redis也會有事務,只不過它不能保證原子性,Redis已經在系統內部進行功能簡化,這樣可以確保更快的運行速度,因爲Redis不需要事務回滾的能力。
redis爲我們的事務提供了四個指令:multi(開啓事務),discard(丟棄),exec(執行),watch(監視)
基本使用
> multi #開啓事務
OK
> set a a
QUEUED
> get a
QUEUED
> exec #執行事務
1) OK
2) "a"
> get a
"a"
執行原理如下
-
當執行multi時,事務開啓
-
事務裏的執行指令都存到一個隊列裏面,注意這裏還是沒有執行,可以相當於一個延遲執行
-
直到遇見exec指令,我們從隊列裏面獲取指令,然後全部執行
關於事務操作失敗的問題
當我們執行事務的時候,隊列中有一個執行失敗,沒執行完的命令會執行嗎?答案是會執行的,看下面的結果
> multi
OK
> set name mango
QUEUED
> incr name
QUEUED
> set name zhangsan
QUEUED
> exec
1) OK
2) (error) ERR value is not an integer or out of range
3) OK
> get name
"zhangsan"
丟棄(discard)
> multi
OK
> set a a
QUEUED
> get a
QUEUED
> discard
OK
> exec
(error) ERR EXEC without MULTI
> get a
(nil)
discard命令用於取消一個事務,它清空客戶端的整個事務隊列,然後將客戶端從事務狀態調整回非事務狀態,最後返回字符串OK給客戶端,說明事務已被取消。
> multi
OK
> multi
(error) ERR MULTI calls can not be nested
Redis 的事務是不可嵌套的,當客戶端已經處於事務狀態,而客戶端又再向服務器發送multi時,服務器只是簡單地向客戶端發送一個錯誤,然後繼續等待其他命令的入隊。multi命令的發送不會造成整個事務失敗,也不會修改事務隊列中已有的數據。
監視(watch)
watch命令用於在事務開始之前監視任意數量的鍵,當調用EXEC命令執行事務時,如果任意一個被監視的鍵已經被其他客戶端修改了,那麼整個事務不再執行,直接返回失敗。
那麼我們來一個失敗的情況,具體操作請看圖片,我們就不貼代碼了
在每個代表數據庫的redis.h/redisDb結構類型中,都保存了一個watched_keys字典,字典的鍵是這個數據庫被監視的鍵,而字典的值則是一個鏈表,鏈表中保存了所有監視這個鍵的客戶端。
watch只能在客戶端進入事務狀態之前執行,在事務狀態下發送watch命令會引發一個錯誤,但它不會造成整個事務失敗,也不會修改事務隊列中已有的數據。
如果說之前說的分佈式鎖是悲觀鎖,那麼watch一定是樂觀鎖,當watch監視的key發生了變化時,我們的事務就不會執行任何操作,當key沒有發生任何變化時,我們就執行這個事務。
什麼情況下使用事務?
當我們需要一次性完成很多個指令或者我們需要在某次操作中不受其他指令影響,我們可以考慮使用事務,redis事務正如作者所說,我們在使用redis的時候出現異常,那麼很多情況是程序設計出現了bug,不要過分依賴redis來做邏輯處理,redis本質是爲了提高性能,回滾只會帶來更多的問題嚴重影響實用性和性能。
但是如果需要滿足ACID強事務類型,那麼我們可以考慮使用lua腳本,但是我們同時需要考慮性能、使用成本等一些方面
一名正在搶救的coder
筆名:mangolove
CSDN地址:https://blog.csdn.net/mango_love
GitHub地址:https://github.com/mangoloveYu