您好,登錄后才能下訂單哦!
通常我們提及數據庫都不可避免的要提到事務,那么什么是事務呢?事務是指作為單個邏輯工作單元執行的一系列操作。所以,首先事務是一系列操作,這一系列操作具有二態性,即完全地執行或者完全地不執行。因此事務處理可以確保除非事務單元內的所有操作的成功完成,否則不會想數據庫更新面向數據的資源。我們這里舉一個例子,數據庫中除查詢操作以外,插入(Insert)、刪除(Delete)和更新(Update)這三種操作都會對數據造成影響,因為事務處理能夠保證一系列操作可以完全地執行或者完全不執行,因此在一個事務被提交以后,該事務中的任何一條SQL語句在被執行的時候,都會生成一條撤銷日志(Undo Log),而撤銷日志中記錄的是和當前擦作完全相反的操作,比如刪除的相反操作是插入,插入的相反操作是刪除等。我們通常所說的事務回滾其實就是去執行這些插銷日志里的相反操作,這同樣告訴我們一個道理,只有事務中的一系列操作完全執行的情況下可以回滾,如果是在意外情況下導致事務中的一系列操作沒有完全執行,這個時候我們是不能保證數據一定可以回滾的。
在數據庫相關理論中,一個邏輯工作單元想要成為事務,就必須滿足 ACID,即原子性、一致性、隔離性和持久性。
我們對數據庫中事務處理的相關理論有了一個基本的認識,或許這個世界上的數據庫系統千差萬別,但我相信在事務處理這個問題上它們最終會殊途同歸,就像我們解決并發過程中的沖突問題,常規的做法依然是加鎖一樣,這是我之所以要花費精力去理解和解釋這些理論知識的原因,技術可謂是日新月異,如果我們總是一味地為新技術而疲于奔命,那么或許我們會漸漸地失去對這個行業的熱愛,我相信原理永遠比框架更為重要。
redis事務提供了一種“將多個命令打包, 然后一次性、按順序地執行”的機制, 并且事務在執行的期間不會主動中斷 —— 服務器在執行完事務中的所有命令之后, 才會繼續處理其他客戶端的其他命令。
Redis中的事務是可以視為一個隊列,即我們可以通過MULTI開始一個事務,這相當于我們聲明了一個命令隊列。接下來,我們向Redis中提交的每條命令,都會被排入這個命令隊列。當我們輸入EXEC命令時,將觸發當前事務,這相當于我們從命令隊列中取出命令并執行,所以Redis中一個事務從開始到執行會經歷 開始事務 、 命令入隊 和 執行事務 三個階段。下面是一個在Redis中使用事務的簡單示例:
127
.0
.0
.1
:6379>
MULTI
OK
127
.0
.0
.1
:6379>
SET
Book_Name "
GIt
Pro"
QUEUED
127
.0
.0
.1
:6379>
SADD
Program_Language "
C++" "
C#" "
Jave" "
Python"
QUEUED
127
.0
.0
.1
:6379>
GET
Book_Name
QUEUED
127
.0
.0
.1
:6379>
EXEC
1)
OK
2) (
integer) 4
3) "
GIt
Pro"
我們可以注意到Redis中的事務和通常意義上的事務基本上是一致的,即
一個事務從開始到執行會經歷以下三個階段:
下面將分別介紹事務的這三個階段。
1)開始事務
MULTI命令的執行標記著事務的開始:
redis>
MULTI
OK
這個命令唯一做的就是, 將客戶端的 REDIS_MULTI 選項打開, 讓客戶端從非事務狀態切換到事務狀態。
圖1 客戶端狀態轉換
2)命令入隊
當客戶端處于非事務狀態下時, 所有發送給服務器端的命令都會立即被服務器執行:
redis>
SET msg
"hello moto"
OK
redis> GET msg
"hello moto"
但是, 當客戶端進入事務狀態之后, 服務器在收到來自客戶端的命令時, 不會立即執行命令, 而是將這些命令全部放進一個事務隊列里, 然后返回QUEUED, 表示命令已入隊:
redis>
MULTI
OK
redis> SET msg
"hello moto"
QUEUED
redis> GET msg
QUEUED
其原理如圖2所示
圖2. 命令入隊
3)執行事務
前面說到, 當客戶端進入事務狀態之后, 客戶端發送的命令就會被放進事務隊列里。
但其實并不是所有的命令都會被放進事務隊列, 其中的例外就是 EXEC 、 DISCARD 、 MULTI 和 WATCH 這四個命令 —— 當這四個命令從客戶端發送到服務器時, 它們會像客戶端處于非事務狀態一樣, 直接被服務器執行:
圖3 執行事務
如果客戶端正處于事務狀態, 那么當EXEC命令執行時, 服務器根據客戶端所保存的事務隊列, 以先進先出(FIFO)的方式執行事務隊列中的命令: 最先入隊的命令最先執行, 而最后入隊的命令最后執行。
執行事務中的命令所得的結果會以 FIFO 的順序保存到一個回復隊列中。
當事務隊列里的所有命令被執行完之后,EXEC命令會將回復隊列作為自己的執行結果返回給客戶端, 客戶端從事務狀態返回到非事務狀態, 至此, 事務執行完畢。
redis事務使用了multi、exec、discard、watch、unwatch命令,命令的作用如圖4所示:
圖4 事務命令
使用案例:
圖5. 正常執行
圖6 放棄事務
圖7 命令錯誤
圖8 語法錯誤
使用watch檢測balance,事務期間balance數據未變動,事務執行成功
圖9 watch用法1
WATCH命令用于在事務開始之前監視任意數量的鍵: 當調用EXEC命令執行事務時, 如果任意一個被監視的鍵已經被其他客戶端修改了, 那么整個事務不再執行, 直接返回失敗。
圖10 watch用法2
圖11 修改balance
在每個代表數據庫的 redis.h/redisDb 結構類型中, 都保存了一個 watched_keys 字典, 字典的鍵是這個數據庫被監視的鍵, 而字典的值則是一個鏈表, 鏈表中保存了所有監視這個鍵的客戶端。
比如說,以下字典就展示了一個 watched_keys 字典的例子:
圖11 watch實現的原理
其中, 鍵 key1 正在被 client2 、 client5 和 client1 三個客戶端監視, 其他一些鍵也分別被其他別的客戶端監視著。
WATCH 命令的作用, 就是將當前客戶端和要監視的鍵在 watched_keys 中進行關聯。
舉個例子, 如果當前客戶端為 client10086 , 那么當客戶端執行 WATCH key1 key2 時, 前面展示的 watched_keys 將被修改成這個樣子:
圖12 watch實現原理2
通過watched_keys字典, 如果程序想檢查某個鍵是否被監視, 那么它只要檢查字典中是否存在這個鍵即可; 如果程序要獲取監視某個鍵的所有客戶端, 那么只要取出鍵的值(一個鏈表), 然后對鏈表進行遍歷即可。
圖13 watch的觸發
當客戶端發送 EXEC 命令、觸發事務執行時, 服務器會對客戶端的狀態進行檢查:
在Redis中,事務總是具有原子性(Atomicity)、一致性(Consistency)和隔離性(Isolation),并且當Redis運行在某種特定的持久化模式下,事務也具有持久性性(Durability)。
事務具有原子性指的是, 數據庫將事務中的多個操作當作一個整體來執行,服務器要么就執行事務中的所有操作, 要么就一個操作也不執行。對于Redis的事務功能來說,事務隊列中的命令要么就全部都執行,要么就一個都不執行,因此, Redis的事務是具有原子性的。
Redis的事務和傳統的關系型數據庫事務的最大區別在于, Redis不支持事務回滾機制(rollback), 即使事務隊列中的某個命令在執行期間出現了錯誤,整個事務也會繼續執行下去,直到將事務隊列中的所有命令都執行完畢為止。 下面展示了即使RPUSH命令在執行期間出現了錯誤,事務的后續命令也會繼續執行下去, 并且之前執行的命令也不會有任何影響:
127
.0
.0
.1
:6379>
set
msg
hello
OK
127
.0
.0
.1
:6379>
multi
OK
127
.0
.0
.1
:6379>
sadd
fruit
apple
banana
cherry
QUEUED
127
.0
.0
.1
:6379>
rpush
msg
bye
redis
QUEUED
127
.0
.0
.1
:6379>
sadd
alphabet
a
b
c
QUEUED
127
.0
.0
.1
:6379>
exec
1) (
integer) 3
2) (
error)
WRONGTYPE
Operation
against
a
key
holding
the
wrong
kind
of
value
3) (
integer) 3
不支持事務回滾是因為這種復雜的功能和Redis追求簡單高效的設計主旨不相符,并且Redis事務的執行時錯誤通常都是編程錯誤產生的, 這種錯誤通常只會出現在開發環境中, 而很少會在實際的生產環境中出現。
事務的一致性是指,如果數據庫執行前是一致的,那么在事務執行后,無論事務是否執行成功,數據庫也應該是一致的。
事務的耐久性指的是,當一個事務執行完畢時,執行這個事務所得的結果巳經被保存到 永久性存儲介質(比如硬盤)里面了, 即使服務器在事務執行完畢 之后停機, 執行事務所得的結果也不會丟失。Redis事務的耐久性由服務器所使用持久化模式決定的:(1) 當服務器在無持久化的內存模式下運作時,事務不具有耐久性。因為一旦服務器停機,服務器所有的數據都將丟失。(2) 當服務器在ROB持久化模式下運作時,事務同樣不具有耐久性。因為服務器只會在特定的保存條件下才會執行BGSAVE命令,并且異步執行的BGSAVE命令不能保證事務的數據第一時間被保存到硬盤上。(3) 當服務器運行在AOF持久化模式下,并且appendfsync選項的值為always時,程序總會在執行命令之后調用同步(sync)函數,將命令數據真正地保存到硬盤里。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。