【Redis】五、Redis事務處理

MULTI 、EXEC 、DISCARD 和WATCH 是 Redis 事務的基礎

1.MULTI  命令用於開啓一個事務,它老是返回 OK 。
MULTI 執行以後,客戶端能夠繼續向服務器發送任意多條命令,這些命令不會當即被執行,而是被放到一個隊列中javascript

2.EXEC 命令被調用時,全部隊列中的命令纔會被執行。css

 

+++++++++++命令 +++++++++++java

redis 192.168.1.53:6379> multi OK redis 192.168.1.53:6379> incr foo QUEUED redis 192.168.1.53:6379> set t1 1 QUEUED redis 192.168.1.53:6379> exec 1) (integer) 2 2) OK

+++++++++++對應的java代碼 +++++++++++nginx

Jedis jedis = new Jedis("192.168.1.53", 6379); Transaction tx = jedis.multi(); tx.incr( "foo"); tx.set( "t1", "2"); List<Object> result = tx.exec(); if (result == null || result.isEmpty()) { System. err.println( "Transaction error..."); return ; } for (Object rt : result) { System. out.println(rt.toString()); }

使用事務時可能會趕上如下兩種錯誤:程序員

1.事務在執行EXEC 以前,入隊的命令可能會出錯。好比說,命令可能會產生語法錯誤(參數數量錯誤,參數名錯誤,等等),或者其餘更嚴重的錯誤,好比內存不足redis

(若是服務器使用 maxmemory 設置了最大內存限制的話)。編程

2.命令可能在EXEC 調用以後失敗。舉個例子,事務中的命令可能處理了錯誤類型的鍵,好比將列表命令用在了字符串鍵上面,諸如此類。

第一種錯誤的狀況:
服務器端:
在 Redis 2.6.5 之前,Redis 只執行事務中那些入隊成功的命令,而忽略那些入隊失敗的命令服務器

不過,從 Redis 2.6.5 開始,服務器會對命令入隊失敗的狀況進行記錄,並在客戶端調用EXEC 命令時,拒絕執行並自動放棄這個事務。ui

+++++++++++命令 +++++++++++spa

 

redis 192.168.1.53:6379> multi OK redis 192.168.1.53:6379> incr foo QUEUED redis 192.168.1.53:6379> set ff 11 22 (error) ERR wrong number of arguments for 'set' command redis 192.168.1.53:6379> exec 1) (integer) 4

由於個人版本是:2.6.4,因此Redis 只執行事務中那些入隊成功的命令,而忽略那些入隊失敗的命令

客戶端(jredis):

客戶端之前的作法是檢查命令入隊所得的返回值:若是命令入隊時返回 QUEUED ,那麼入隊成功;不然,就是入隊失敗。若是有命令在入隊時失敗,

那麼大部分客戶端都會中止並取消這個事務。

 

第二種錯誤的狀況:

至於那些在EXEC 命令執行以後所產生的錯誤,並無對它們進行特別處理:即便事務中有某個/某些命令在執行時產生了錯誤,事務中的其餘命令仍然會繼續執行。

+++++++++++命令+++++++++++

redis 192.168.1.53:6379> multi OK redis 192.168.1.53:6379> set a 11 QUEUED redis 192.168.1.53:6379> lpop a QUEUED redis 192.168.1.53:6379> exec 1) OK 2) (error) ERR Operation against a key holding the wrong kind of value

+++++++++++對應的java代碼 +++++++++++

 

Jedis jedis = new Jedis("192.168.1.53", 6379); Transaction tx = jedis.multi(); tx.set( "t1", "2"); tx.lpop( "t1"); List<Object> result = tx.exec(); if (result == null || result.isEmpty()) { System. err.println( "Transaction error..."); return ; } for (Object rt : result) { System. out.println(rt.toString()); }

Redis 在事務失敗時不進行回滾,而是繼續執行餘下的命令

這種作法可能會讓你以爲有點奇怪,如下是這種作法的優勢:
1.Redis 命令只會由於錯誤的語法而失敗(而且這些問題不能在入隊時發現),或是命令用在了錯誤類型的鍵上面:這也就是說,從實用性的角度來講,

失敗的命令是由編程錯誤形成的,而這些錯誤應該在開發的過程當中被發現,而不該該出如今生產環境中。
2.由於不須要對回滾進行支持,因此 Redis 的內部能夠保持簡單且快速。

 

鑑於沒有任何機制能避免程序員本身形成的錯誤,而且這類錯誤一般不會在生產環境中出現,因此 Redis 選擇了更簡單、更快速的無回滾方式來處理事務。

 

3.DISCARD  命令時,事務會被放棄,事務隊列會被清空,而且客戶端會從事務狀態中退出

+++++++++++命令 +++++++++++

 

redis 192.168.1.53:6379> set foo 1 OK redis 192.168.1.53:6379> multi OK redis 192.168.1.53:6379> incr foo QUEUED redis 192.168.1.53:6379> discard OK redis 192.168.1.53:6379> get foo "1"

4.WATCH  命令能夠爲 Redis 事務提供 check-and-set (CAS)行爲
被WATCH 的鍵會被監視,並會發覺這些鍵是否被改動過了。若是有至少一個被監視的鍵在EXEC 執行以前被修改了,那麼整個事務都會被取消

+++++++++++第一條命令 +++++++++++

 

redis 192.168.1.53:6379> watch foo OK redis 192.168.1.53:6379> set foo 5 OK redis 192.168.1.53:6379> multi OK redis 192.168.1.53:6379> set foo 9 QUEUED

+++++++++++暫停(執行完第二條命令才執行下面的)+++++++++++

redis 192.168.1.53:6379> exec (nil) redis 192.168.1.53:6379> get foo "8"

+++++++++++第二條命令+++++++++++

redis 192.168.1.53:6379> set foo 8 OK

+++++++++++對應的java代碼 +++++++++++

 

Jedis jedis = new Jedis("192.168.1.53", 6379); jedis.watch( "foo"); Transaction tx = jedis.multi(); tx.incr( "foo"); List<Object> result = tx.exec(); //運行時在這邊打斷點,而後經過命令行改變foo的值 if (result == null || result.isEmpty()) { System. err.println( "Transaction error..."); return; } for (Object rt : result) { System. out.println(rt.toString()); }

若是在WATCH 執行以後,EXEC 執行以前,有其餘客戶端修改了 mykey 的值,那麼當前客戶端的事務就會失敗。程序須要作的,就是不斷重試這個操做,直到沒有發生碰撞爲止。這種形式的鎖被稱做樂觀鎖,它是一種很是強大的鎖機制。而且由於大多數狀況下,不一樣的客戶端會訪問不一樣的鍵,碰撞的狀況通常都不多,因此一般並不須要進行重試。

相關文章
相關標籤/搜索