Redis 笔记(08)— 事务(一次执行多条命令、命令 watch/multi/exec/discard、错误处理)
1. 事務概念
Redis 中的事務 (transaction)是一組命令的集合。事務同命令一樣是 Redis 的最小執行單位,一個事務中的命令要么都執行,要么都不執行。事務的原理是先將屬于一個事務的命令發送給 Redis,然后再讓 Redis 依次執行這些命令。
2. 事務命令
| 命令 | 說明 |
|---|---|
| watch key[key…] | 鎖定key,直到執行了multi/exec命令 |
| multi | 標記一個事務塊開始 |
| exec | 執行所有multi之后發的命令 |
| discard | 丟棄所有multi之后發的命令 |
3. 事務使用
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set a 1
QUEUED
127.0.0.1:6379> set b 2
QUEUED
127.0.0.1:6379> set c 3
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
3) OK
127.0.0.1:6379>
multi 標記一個事務的開始,表示之后發送的命令都屬于同一個事務,而 exec 命令則告訴 Redis 將等待執行的事務隊列中的所有命令(即剛才返回 QUEUED 的命令)按照發送順序依次執行。exec 命令的返回值是事務命令的返回值組成的列表,返回值順序和命令的順序相同。
Redis 會保證一個事務中的命令要么都執行,要么都不執行。 如果在執行 exec 命令之前客戶端斷線了那么 Redis 會自動清空事務隊列,事務中的所有命令都不會執行;而如果客戶端執行了 exec 命令后斷線也沒有關系,Redis 已經記錄了所有要執行的命令。
所有的指令在 exec 之前不執行,而是緩存在服務器的一個事務隊列中,服務器一旦收到 exec 指令,才開執行整個事務隊列,執行完畢后一次性返回所有指令的運行結果。因為 Redis 的單線程特性,它不用擔心自己在執行隊列的時候被其它指令打攪,可以保證他們能得到的「原子性」執行。
Redis 為事務提供了一個 discard 指令,用于丟棄事務緩存隊列中的所有指令,在 exec 執行之前。
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set x 1
QUEUED
127.0.0.1:6379(TX)> set y 2
QUEUED
127.0.0.1:6379(TX)> set z 3
QUEUED
127.0.0.1:6379(TX)> discard # 丟棄 multi 之后發送的命令
OK
127.0.0.1:6379> get x
(nil)
127.0.0.1:6379> mget x y z
1) (nil)
2) (nil)
3) (nil)
127.0.0.1:6379>
4. 錯誤處理
如果 Redis 事務中一個命令發生錯誤,那么其它的命令還會執行嗎?我們主要看兩種錯誤:
-
語法錯誤
只要事務中有一個命令的語法發生錯誤,那么整個事務中的命令都不會執行。
-
運行錯誤
運行錯誤是指在執行命令時出現的錯誤,這種錯誤在實際執行之前是無法發現的,當事務中出現這種運行錯誤時,其它命令仍然會繼續執行的。
> multi
OK
> set books python
QUEUED
> incr books
QUEUED
> set phone huawei
QUEUED
> exec
1) OK
2) (error) ERR value is not an integer or out of range
3) OK
> get books
"python"
> get phone
"huawei"
上面的例子是事務執行到中間遇到失敗了,因為我們不能對一個字符串進行數學運算,事務在遇到指令執行失敗后,后面的指令還繼續執行,所以 phone 的值能繼續得到設置。
到這里,你應該明白 Redis 的事務根本不能算「原子性」,而僅僅是滿足了事務的「隔離性」,隔離性中的串行化——當前執行的事務有著不被其它事務打斷的權利。
Redis 事務不支持關系型數據庫事務提供的回滾功能。
5. watch 命令
watch 命令可以監控一個或者多個鍵,一旦其中有一個鍵被修改或刪除,之后的事務就不會執行。監控一直持續到 exec 命令 (事務中的命令是在 exec 命令之后執行的,所以在 multi 命令之后可以修改 watch 監控的鍵值)。
watch 會在事務開始之前盯住 1 個或多個關鍵變量,當事務執行時,也就是服務器收到了 exec 指令要順序執行緩存的事務隊列時,Redis 會檢查關鍵變量自 watch 之后,是否被修改了 (包括當前事務所在的客戶端)。如果關鍵變量被人動過了,exec 指令就會返回 null 回復告知客戶端事務執行失敗,這個時候客戶端一般會選擇重試。
127.0.0.1:6379> set a 1
OK
127.0.0.1:6379> watch a # 監控 a 是否變化
OK
127.0.0.1:6379> set a 2 # 鍵被修改,則后面的 multi 不會被執行
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set a 3
QUEUED
127.0.0.1:6379> exec # 變量被修改,服務器回復 nil
(nil)
127.0.0.1:6379> get a # 被修改之后的值,multi 命令之后的值沒有被執行
"2"
127.0.0.1:6379>
也可以用 unwatch 命令來取消監控。
Redis事務在執行時是單線程運行的。但是在執行前有可能別的客戶端已經修改了事務里執行的 key 。所以在 multi 事務開始之前用 watch 檢測這個 key 避免被其他客戶端改變的。如果這個 key 被改變 了 exec 的時候就會報錯不執行這個事務。
Redis 禁止在 multi 和 exec 之間執行 watch 指令,而必須在 multi 之前做好盯住關鍵變量,否則會出錯。
6. 事務結合管道使用
Redis 事務在發送每個指令到事務緩存隊列時都要經過一次網絡讀寫,當一個事務內部的指令較多時,需要的網絡 IO 時間也會線性增長。所以通常 Redis 的客戶端在執行事務時都會結合 pipeline 一起使用,這樣可以將多次 IO 操作壓縮為單次 IO 操作。
比如我們在使用 Python 的 Redis 客戶端時執行事務時是要強制使用 pipeline 的。
pipe = redis.pipeline(transaction=true)
pipe.multi()
pipe.incr("books")
pipe.incr("books")
values = pipe.execute()
總結
以上是生活随笔為你收集整理的Redis 笔记(08)— 事务(一次执行多条命令、命令 watch/multi/exec/discard、错误处理)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2022-2028年中国防臭袜行业投资分
- 下一篇: 2022-2028中国快时尚服装市场竞争