Redis 事務及其應用

參考:css

http://www.runoob.com/redis/redis-transactions.htmlhtml

https://www.cnblogs.com/qlshine/p/5958504.htmlmysql

 

Redis 事務能夠一次執行多個命令, 而且帶有如下兩個重要的保證:redis

  • 批量操做在發送 EXEC 命令前被放入隊列緩存。
  • 收到 EXEC 命令後進入事務執行,事務中任意命令執行失敗,其他的命令依然被執行。
  • 在事務執行過程,其餘客戶端提交的命令請求不會插入到事務執行命令序列中。

一個事務從開始到執行會經歷如下三個階段:sql

  • 開始事務。
  • 命令入隊。
  • 執行事務。

實例

如下是一個事務的例子, 它先以 MULTI 開始一個事務, 而後將多個命令入隊到事務中, 最後由 EXEC 命令觸發事務, 一併執行事務中的全部命令:數組

redis 127.0.0.1:6379> MULTI OK redis 127.0.0.1:6379> SET book-name "Mastering C++ in 21 days" QUEUED redis 127.0.0.1:6379> GET book-name QUEUED redis 127.0.0.1:6379> SADD tag "C++" "Programming" "Mastering Series" QUEUED redis 127.0.0.1:6379> SMEMBERS tag QUEUED redis 127.0.0.1:6379> EXEC 1) OK 2) "Mastering C++ in 21 days" 3) (integer) 3 4) 1) "Mastering Series" 2) "C++" 3) "Programming"

單個 Redis 命令的執行是原子性的,但 Redis 沒有在事務上增長任何維持原子性的機制,因此 Redis 事務的執行並非原子性的。緩存

事務能夠理解爲一個打包的批量執行腳本,但批量指令並不是原子化的操做,中間某條指令的失敗不會致使前面已作指令的回滾,也不會形成後續的指令不作。ui

 

好比:spa

redis 127.0.0.1:7000> multi OK redis 127.0.0.1:7000> set a aaa QUEUED redis 127.0.0.1:7000> set b bbb QUEUED redis 127.0.0.1:7000> set c ccc QUEUED redis 127.0.0.1:7000> exec 1) OK 2) OK 3) OK

若是在 set b bbb 處失敗,set a 已成功不會回滾,set c 還會繼續執行。code

 

應用

redis 與 mysql 事務的對比:

  mysql redis
開啓 start transaction multi
語句 普通的sql 普通命令
失敗 rollback回滾 discard取消
成功 commit exec

 

: rollback 與 discard 的區別
若是已經成功執行了2條語句, 第3條語句出錯
rollback後, 前兩條語句影響消失
discard只是結束本次事務, 前兩條語句形成的影響依然存在
:
在multi後面的語句中, 語句出錯可能有2種狀況

    1. 語法自己有問題, 這種錯誤exec時報錯, 全部語句得不到執行
    2. 語法自己沒錯誤, 但適用對象有問題, 好比zadd操做list對象, exec後會執行正確的語句, 並跳過有不適當的語句

使用redis模擬銀行轉帳操做:

  • 正常狀況

    127.0.0.1:6379> set wang 200 #wang有200 OK 127.0.0.1:6379> set zhao 700 #zhao有700 OK 127.0.0.1:6379> 127.0.0.1:6379> multi #開啓事務 OK 127.0.0.1:6379> decrby zhao 100 #zhao減100 QUEUED 127.0.0.1:6379> incrby wang 100 #wang加100 QUEUED #以上兩個QUEUED表示兩條語句被放在隊列裏面, exec前並無執行 127.0.0.1:6379> exec #執行完畢 1) (integer) 600 2) (integer) 300 127.0.0.1:6379> 
  • 意外狀況

    127.0.0.1:6379> multi 開啓事務 OK 127.0.0.1:6379> 127.0.0.1:6379> decrby zhao 100 #zhao減100 QUEUED 127.0.0.1:6379> das #輸入一個錯誤的命令 (error) ERR unknown command 'das' 127.0.0.1:6379> exec #執行, 提示被忽略 (error) EXECABORT Transaction discarded because of previous errors. 127.0.0.1:6379> 127.0.0.1:6379> mget zhao wang #zhao 和 wang的值不變 1) "600" 2) "300" 127.0.0.1:6379> 
  • 另外一種報錯狀況

    127.0.0.1:6379> multi OK 127.0.0.1:6379> 127.0.0.1:6379> decrby zhao 100 #zhao減100 QUEUED 127.0.0.1:6379> sadd wang pig #故意把wang當作數組加入一個key, 發現並無報錯, #由於這條語句被存放在隊列裏, 並無被執行 QUEUED 127.0.0.1:6379> exec #此時語句才被執行, 因此報錯, 可是zhao依然減了100, 說明執行了正確的語句, 跳過了不正取的語句, 影響還在 1) (integer) 500 2) (error) WRONGTYPE Operation against a key holding the wrong kind of value 127.0.0.1:6379> 127.0.0.1:6379> mget zhao wang 1) "500" 2) "300" 127.0.0.1:6379> 
  • discard取消

    127.0.0.1:6379> mget wang zhao 1) "400" 2) "400" 127.0.0.1:6379> multi OK 127.0.0.1:6379> decrby zhao 100 QUEUED 127.0.0.1:6379> incrby wang 100 QUEUED 127.0.0.1:6379> 127.0.0.1:6379> discard #取消後值沒變 OK 127.0.0.1:6379> 127.0.0.1:6379> mget wang zhao 1) "400" 2) "400" 127.0.0.1:6379> exec (error) ERR EXEC without MULTI 127.0.0.1:6379> 
127.0.0.1:6379> mget zhao wang 1) "400" 2) "400" 127.0.0.1:6379> 127.0.0.1:6379> multi OK 127.0.0.1:6379> decrby zhao 100 QUEUED 127.0.0.1:6379> sadd wang pig QUEUED 127.0.0.1:6379> 127.0.0.1:6379> exec 1) (integer) 300 2) (error) WRONGTYPE Operation against a key holding the wrong kind of value 127.0.0.1:6379> 127.0.0.1:6379> discard #不能discard (error) ERR DISCARD without MULTI 127.0.0.1:6379> 127.0.0.1:6379> mget zhao wang 1) "300" 2) "400" 127.0.0.1:6379> 

watch key1 key2 ... keyN

做用: 監聽 key1 key2 keyN有沒有變化, 若是有變化, 則事務取消

unwatch(不加key): 取消全部 watch 監聽

場景: 一我的正在買票, ticket-1, money-100, 而票只有一張, 若是在我multi以後, exec以前票被別人買走, 即ticket變爲0了, 怎麼辦?

127.0.0.1:6379> set ticket 1 #加入只有1張票 OK 127.0.0.1:6379> set lisi 300 #lisi有300 OK 127.0.0.1:6379> set wang 300 #wang有300 OK 127.0.0.1:6379> 127.0.0.1:6379> multi #開啓事務 OK 127.0.0.1:6379> decr ticket #票數-1 QUEUED 127.0.0.1:6379> decrby lisi 100 #lisi準備買-100 QUEUED 127.0.0.1:6379> #此處尚未exec

假如就在exec前票被別人買走, 打開另外一個終端

127.0.0.1:6379> decr ticket #票-1 (integer) 0 127.0.0.1:6379> get ticket #此時票數爲0 "0" 127.0.0.1:6379> 

此時提交lisi

127.0.0.1:6379> exec 1) (integer) -1 #票變爲-1 2) (integer) 200 #錢-100 127.0.0.1:6379> 

所以上面的過程不合理


要解決上面的狀況,要採用監視

127.0.0.1:6379> set ticket 1 #票數爲1 OK 127.0.0.1:6379> set lisi 200 #lisi錢是100 OK 127.0.0.1:6379> set wang 300 #wang是300 OK 127.0.0.1:6379> 127.0.0.1:6379> watch ticket #監控ticket有沒有變更, 有變更的話則事務取消 OK 127.0.0.1:6379> multi #開啓事務 OK 127.0.0.1:6379> decr ticket #ticket-1 QUEUED 127.0.0.1:6379> decrby lisi 100 #錢-100 QUEUED 127.0.0.1:6379>

在exec前票又被另外一我的買走了

127.0.0.1:6379> decr ticket (integer) 0 127.0.0.1:6379> get ticket "0" 127.0.0.1:6379> 

此時票數爲0, lisi提交

127.0.0.1:6379> 127.0.0.1:6379> exec #失敗 (nil) 127.0.0.1:6379> 127.0.0.1:6379> get lisi #錢並無減小 "200" 127.0.0.1:6379> 

Redis 事務命令

下表列出了 redis 事務的相關命令:

 

序號 命令及描述
1 DISCARD 
取消事務,放棄執行事務塊內的全部命令。
2 EXEC 
執行全部事務塊內的命令。
3 MULTI 
標記一個事務塊的開始。
4 UNWATCH 
取消 WATCH 命令對全部 key 的監視。
5 WATCH key [key ...] 監視一個(或多個) key ,若是在事務執行以前這個(或這些) key 被其餘命令所改動,那麼事務將被打斷。
相關文章
相關標籤/搜索