前幾天有讀者說本身面試被問到Redis的事務,雖然不經常使用,可是面試居然被問到,平時本身沒有注意Redis的事務這一塊,面試的時候被問到很是很差受。程序員
雖然,這位讀者面試最後算是過了,可是薪資方面沒有拿到本身理想的薪資。web
其實這個也是正常的,通常面試被問到爛大街的,誰還問你啊,專門挑一些不常見的來問你,就是爲了壓你的薪資。面試
因此在這裏寫一篇文章對Redis的事務進行詳細的講解,估計對Redis事務從理解到原理深刻這一篇就夠了。redis
之後面試都不用擔憂了再被問道Redis的事務了,這一篇主要講解Redis事務原理和實操的演練,理解理論的同時也經過實操來證明理論。sql
Redis事務是一組命令的集合,將多個命令進行打包,而後這些命令會被順序的添加到隊列中,而且按順序的執行這些命令。數據庫
「Redis事務中沒有像Mysql關係型數據庫事務隔離級別的概念,不能保證原子性操做,也沒有像Mysql那樣執行事務失敗會進行回滾操做」。編輯器
這個與Redis的特色:「快速、高效」有着密切的關聯,「由於一些列回滾操做、像事務隔離級別那這樣加鎖、解鎖,是很是消耗性能的」。因此,Redis中執行事務的流程只須要簡單的下面三個步驟:性能
在Redis中事務的實現主要是經過以下的命令實現的:flex
命令 | 功能描述 |
---|---|
MULTI | 「事務開始的命令」,執行該命令後,後面執行的對Redis數據類型的「操做命令都會順序的放進隊列中」,等待執行EXEC命令後隊列中的命令纔會被執行 |
DISCARD | 「放棄執行隊列中的命令」,你能夠理解爲Mysql的回滾操做,「而且將當前的狀態從事務狀態改成非事務狀態」。 |
EXEC | 執行該命令後「表示順序執行隊列中的命令」,執行完後並將結果顯示在客戶端,「將當前狀態從事務狀態改成非事務狀態」。如果執行該命令以前有key被執行WATCH命令而且又被其它客戶端修改,那麼就會放棄執行隊列中的全部命令,在客戶端顯示報錯信息,如果沒有修改就會執行隊列中的全部命令。 |
WATCH key | 表示指定監視某個key,「該命令只能在MULTI命令以前執行」,若是監視的key被其餘客戶端修改,「EXEC將會放棄執行隊列中的全部命令」 |
UNWATCH | 「取消監視以前經過WATCH 命令監視的key」,經過執行EXEC 、DISCARD 兩個命令以前監視的key也會被取消監視 |
以上就是一個Redis事務的執行過程包含的命令,下面就來詳細的圍繞着這幾個命令進行講解。ui
MULTI
命令表示事務的開始,當看到OK表示已經進入事務的狀態: 該命令執行後客戶端會將「當前的狀態從非事務狀態修改成事務狀態」,這一狀態的切換是將客戶端的flags
屬性中打開REDIS_MULTI
來完成的,該命令能夠理解關係型數據庫Mysql的BEGIN TRANCATION
語句:
執行完MULTI命令後,後面執行的操做Redis五種類型的命令都會按順序的進入命令隊列中,該部分也是真正的業務邏輯的部分。
Redis客戶端的命令執行後如果當前狀態處於事務狀態命令就會進入隊列中,而且返回QUEUED
字符串,表示該命令已經進入了命令隊列中,而且「事務隊列是以先進先出(FIFO)的方式保存入隊的命令」的。 如果當前狀態是非事務狀態就會當即執行命令,並將結果返回客戶端。在事務狀態「執行操做事務的命令就會被當即執行」,如EXEC、DISCARD、UNWATCH
。 結合上面的分析,Redis執行命令的流程以下圖所示: 事務的命令隊列中有三個參數分別是:「要執行的命令」、「命令的參數」、「參數的個數」。例如:經過執行以下的命令:
redis> MULTI
OK redis> SET name "黎杜" QUEUED redis> GET name QUEUED 複製代碼
那麼對應上面的隊列中三個參數以下表格所示:
執行的命令 | 命令的參數 | 參數的個數 |
---|---|---|
SET | ["name", "黎杜"] | 2 |
GET | ["name"] | 1 |
當客戶端執行EXEC命令的時候,上面的命令隊列就會被按照先進先出的順序被執行,固然執行的結果有成功有失敗,這個後面分析。
上面說到當客戶端處於非事務的狀態命令發送到服務端會被當即執行,如果客戶端處於事務狀態命令就會被放進命令隊列。
命令入隊的時候,會按照順序進入隊列,隊列以先進先出的特色來執行隊列中的命令。
如果客戶端處於事務狀態,執行的是EXEC、DISCARD、UNWATCH
這些操做事務的命令,也會被當即執行。 「(1)正常執行」
仍是上面的例子,執行以下的代碼:
redis> MULTI
OK redis> SET name "黎杜" QUEUED redis> GET name QUEUED 複製代碼
全部的命令進入了隊列,當最後執行EXEC,首先會執行SET命令,而後執行GET命令,而且執行後的結果也會進入一個隊列中保存,最後返回給客戶端:
回覆的類型 | 回覆的內容 |
---|---|
status code reply | OK |
bulk reply | "黎杜" |
因此最後你會在客戶端看到「OK、黎杜」,這樣的結果顯示,這個也就是一個事務成功執行的過程。
至此一個事務就完整的執行完成,而且此時客戶端也從事務狀態更改成非事務狀態。
「(2)放棄事務」
固然你也能夠放棄執行該事務,只要你再次執行DISCARD操做就會放棄執行這次的事務。具體代碼以下所示:
redis> MULTI
OK redis> SET name "黎杜" QUEUED redis> GET name QUEUED redis> DISCARD // 放棄執行事務 OK 複製代碼
DISCARD命令取消一個事務的時候,就會將命令隊列清空,而且將客戶端的狀態從事務狀態修改成非事務的狀態。
「Redis的事務是不可重複的」,當客戶端處於事務狀態的時候,再次向服務端發送MULTI命令時,直接就會向客戶端返回錯誤。
WATCH
命令是在MULTI命令以前執行的,表示監視任意數量的key,與它對應的命令就是UNWATCH
命令,取消監視的key。
WATCH
命令有點「相似於樂觀鎖機制」,在事務執行的時候,如果被監視的任意一個key被更改,則隊列中的命令不會被執行,直接向客戶端返回(nil)表示事務執行失敗。
下面咱們來演示一下WATCH命令的操做流程,具體實現代碼以下:
redis> WATCH num OK redis> MULTI OK redis> incrby num 10 QUEUED redis> decrby num 1 QUEUED redis> EXEC // 執行成功 複製代碼
這個是WATCH
命令的正常的操做流程,如果在其它的客戶端,修改了被監視的任意key,就會放棄執行該事務,以下圖所示:
客戶端一 | 客戶端二 |
---|---|
WATCH num | |
MULTI | |
incrby num 10 | get num |
decrby num 1 | |
EXEC | |
執行失敗,返回(nil) |
WATCH命令的底層實現中保存了watched_keys
字典,「字典的鍵保存的是監視的key,值是一個鏈表,鏈表中的每一個節點值保存的是監視該key的客戶端」。 如果某個客戶端再也不監視某個key,該客戶端就會從鏈表中脫離。如client3,經過執行UNWATCH命令,再也不監視key1:
上面說到Redis是沒有回滾機制的,那麼執行的過程,如果不當心敲錯命令,Redis的命令發送到服務端沒有被當即執行,因此是暫時發現不到該錯誤。
那麼在Redis中的錯誤處理主要分爲兩類:「語法錯誤」、「運行錯誤」。下面主要來說解一下這兩類錯誤的區別。
「(1)語法錯誤」
好比執行命令的時候,命令的不存在或者錯誤的敲錯命令、參數的個數不對等都會致使語法錯誤。
下面來演示一下,執行下面的四個命令,先後的兩個命令是正確的,中間的兩個命令是錯誤的,以下所示:
127.0.0.1:6379> multi
OK 127.0.0.1:6379> set num 1 QUEUED 127.0.0.1:6379> set num (error) ERR wrong number of arguments for 'set' command 127.0.0.1:6379> ssset num 3 (error) ERR unknown command 'ssset' 127.0.0.1:6379> set num 2 QUEUED 127.0.0.1:6379> exec (error) EXECABORT Transaction discarded because of previous errors. 複製代碼
語法錯誤是在Redis語法檢測的時候就能發現的,因此當你執行錯誤命令的時候,也會即便的返回錯誤的提示。
最後,即便命令進入隊列,只要存在語法錯誤,該隊列中的命令都不會被執行,會直接向客戶端返回事務執行失敗的提示。
「(2)運行錯誤」
執行時使用不一樣類型的操做命令操做不一樣數據類型就會出現運行時錯誤,這種錯誤時Redis在不執行命令的狀況下,是沒法發現的。
127.0.0.1:6379> multi
OK 127.0.0.1:6379> set num 3 QUEUED 127.0.0.1:6379> sadd num 4 QUEUED 127.0.0.1:6379> set num 6 QUEUED 127.0.0.1:6379> exec 1) OK 2) (error) WRONGTYPE Operation against a key holding the wrong kind of value 3) OK 127.0.0.1:6379> get key "6" 複製代碼
這樣就會致使,正確的命令被執行,而錯誤的命令不會不執行,這也顯示出Redis的事務並不能保證數據的一致性,由於中間出現了錯誤,有些語句仍是被執行了。
這樣的結果只能程序員本身根據以前執行的命令,本身一步一步正確的回退,所謂本身的爛攤子,本身收拾。
咱們知道關係性數據庫Mysql中具備事務的四大特性:「原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)、持久性(Durability)」。
可是Redis的事務爲了保證Redis除了客戶端的請求高效,去除了傳統關係型數據庫的「事務回滾、加鎖、解鎖」這些消耗性能的操做,Redis的事務實現簡單。
原子性中Redis的事務只能保證單個命令的原子性,多個命令就沒法保證,如上面索道的運行時錯誤,即便中間有運行時錯誤出現也會正確的執行後面正確的命令,不具備回滾操做。
既然沒有了原子性,數據的一致性也就沒法保證,這些都須要程序員本身手動去實現。
Reids在進行事務的時候,不會被中斷知道事務的運行結束,也具備必定的隔離性,而且Redis也能持久化數據。