redis 是一個高性能的key-value 數據庫。做爲no sql 數據庫redis 與傳統關係型數據庫相比有簡單靈活、數據結構豐富、高速讀寫等優勢。mysql
本文主要針對redis 在事物方面的處理與傳統關係型數據庫(使用mysql)進行比對,所使用的環境爲ubuntu 14.04.1,mysql 5.5.49,redis 3.2.3。程序員
1操做事務命令redis
mysql 使用start transaction 開啓事物,rollback 回滾事物,commit 提交事物。redis 使用multi 開始事物,discard 取消事物,exec 提交事物。開始實驗前爲mysql 和redis 準備測試數據,tom1 帳戶準備1000元,tom2 帳戶準備500元。sql
(圖1-數據準備)數據庫
場景1:使用事務從tom1轉100元到tom2 。ubuntu
步驟以下:服務器
mysql數據結構 |
redis併發 |
|
開啓事物app |
start transaction |
multi |
tom1 - 100 |
update tom1 |
decrty tom1 100 queued |
查詢tom1 餘額 |
tom1餘額減小了100 |
get tom1 queued |
tom2 + 100 |
update tom2 |
incrby tom2 100 queued |
查詢tom2 餘額 |
tom2餘額增長了100 |
get tom2 queued |
提交事物 |
commit |
exec 執行queue中的語句。 1) (integer) 900 2) "900" 3) (integer) 600 4) "600" |
查詢tom1,tom2餘額 |
select * from account where name in (‘tom1’,’tom2’); tom1金額減小100,tom2帳戶增長了100 |
mget tom1 tom2 tom1金額減小100,tom2帳戶增長了100 |
(圖2-mysql 事務執行過程)
(圖3-redis 事務執行過程)
從場景1中能夠看出mysql 開啓事務後事務中的sql 語句在commit 以前就已經執行了sql 語句的,只是並未真正提交到數據庫。redis 使用multi 開啓事務後,編寫的sql 語句都進入queue 隊列中,待執行exec 提交事物時才一次性按進入queue 隊列的順序提交到數據庫。一樣針對回滾,mysql 執行rollback 會將提交的數據回滾,redis 由於沒有提交到數據,使用discard 只是單純取消在queue 中的sql 語句。
場景2: 在執行tom1 向tom2 轉過程當中100元的過程當中使用了語法錯誤的sql。
步驟以下:
mysql |
redis |
|
開啓事物 |
start transaction |
multi |
tom1 - 100 |
update tom1 |
decrty tom1 100 queued |
查詢tom1 餘額 |
tom1餘額減小了100 |
get tom1 queued |
tom2 + 100 |
使用錯誤命令爲tom2+100 |
使用錯誤命令爲tom2+100 |
查詢tom2 餘額 |
tom2餘額未變 |
get tom2 queued |
提交事物 |
commit |
exec 由於queue中存在語法錯誤的語句,直接discard事務中的語句 |
查詢tom1,tom2餘額 |
select * from account where name in (‘tom1’,’tom2’); tom1金額減小100,tom2帳戶金額未變 |
mget tom1 tom2 tom1,tom2金額未變 |
(圖4-mysql 事務中存在語法錯誤語句)
(圖5-redis 事務中存在語法錯誤語句)
從場景2中能夠看出,mysql 事務即便遇到錯誤的語句也會提交正確的sql 到數據庫,須要程序員控制當遇到語句異常時進行回滾,redis 與mysql 不一樣,提交事務時當queue 中有語法錯誤語句會discard 整個事務中的sql 語句。
場景3: 在執行tom1 向tom2 轉過程當中100元的過程當中使用了錯誤的執行對象。
步驟以下:
mysql |
redis |
|
開啓事物 |
start transaction |
multi |
tom1 - 100 |
update tom1 |
decrty tom1 100 queued |
查詢tom1 餘額 |
tom1餘額減小了100 |
get tom1 queued |
tom2 + 100 |
使用錯誤對象命令爲tom2+’hundrud’ |
使用錯誤命令爲tom2+’hundred’ |
查詢tom2 餘額 |
tom2餘額未變 |
get tom2 queued |
提交事物 |
commit |
exec 逐條執行 |
查詢tom1,tom2餘額 |
select * from account where name in (‘tom1’,’tom2’); tom1金額減小100,tom2帳戶金額未變 |
mget tom1 tom2 tom1金額減小100,tom2帳戶金額未變 |
(圖6-mysql 中存在操做錯誤對象的sql 語句)
(圖7-redis 中存在操做錯誤對象的sql語句)
從場景3中能夠看出,mysql 和redis 事務當遇到操做對象類型不正確的時候都會提交執行事務。
2事務鎖
在關係型數據庫中主要經過樂觀鎖和悲觀鎖進行數據庫事務的併發控制。而在redis 中是經過watch 加樂觀鎖對數據庫進行併發控制。
mysql 悲觀鎖是經過select * from table where <condition> for update將數據加鎖,致使其餘線程或事務不能更新該數據。相對於悲觀鎖,在對數據庫進行處理的時候,樂觀鎖並不會使用數據庫提供的鎖機制。通常的實現樂觀鎖的方式就是記錄數據版本。數據版本爲數據增長的一個版本標識。當讀取數據時,將版本標識的值一同讀出,數據每更新一次,同時對版本標識進行更新。當咱們提交更新的時候,判斷數據庫表對應記錄的當前版本信息與第一次取出來的版本標識進行比對,若是數據庫表當前版本號與第一次取出來的版本標識值相等,則予以更新,不然認爲是過時數據。
關係型數據庫mysql 實現數據版本主要有兩種方式,第一種是使用版本號,第二種是使用時間戳。使用版本號時,能夠在數據初始化時指定一個版本號,每次對數據的更新操做都對版本號執行+1操做。並判斷當前版本號是否是該數據的最新的版本號。
(圖8-mysql樂觀鎖)
圖8展現了mysql 樂觀鎖的示例,開啓兩個mysql 客戶端,客戶端1執行start transaction 查詢數據,這時客戶端2更新了客戶端1查詢到的數據及version 字段,而後客戶端1在第3步試圖更新在第一步中查詢到的數據時因version 已改變所以更新失敗。
(圖9-redis 樂觀鎖(watch))
redis 是經過watch 命令監控數據是否發生變化實現樂觀鎖的。如圖9所示打開兩個redis 客戶端,客戶端1對數據tom2 進行watch,打開事務更改tom1,tom2,客戶端2更改客戶端1中watch 的對象,步驟3客戶端1提交事務因爲watch 的對象被更改了,致使事務放棄執行。
3事務的ACID 性質
下面討論下事務的ACID 性質。傳統關係型數據庫事務具有ACID 性質,這裏不作詳細討論。
針對Redis 事務具備原子性(Atomicity),一致性(Consistency),和隔離性(Isolation),當redis 在持久化模式運行時,也具有持久性(Durability)。在場景1~3和樂觀鎖示例中redis 所有執行事務隊列中的sql 語句即便其中sql存在操做對象錯誤,要麼所有discard放棄執行,所以redis 事務具備原子性(Atomicity)。
redis 事務在執行過程當中發生錯誤或進程被終結,都能保證數據的一致性(Consistency)。由於redis 使用單線程串行方式來執行事務的,在執行事務期間不會對事務進行中斷(一致性),所以redis 事務也具備隔離性(Isolation)。redis 能夠只將運行結果保存在內存中,但當redis 服務器使用AOF 持久化模式並appendfsync 設置爲always 時,程序執行sql 後會調用sync 函數將數據保存到硬盤裏,所以redis 事務也能夠具備持久性(Durability)。
經過下表對redis 事務和關係型數據庫事務作一個簡單的總結。
關係型數據庫(mysql) |
redis |
|
開啓事務 |
start transaction命令 |
multi命令 |
回滾事務 |
使用rollback命令能夠回滾事務 |
不能回滾事務。但使用discard命令能夠放棄事務queue中的sql |
提交事務 |
commit命令 即便遇到sql語法錯誤也會提交事務 |
exec命令 若是遇到sql語法錯誤會放棄事務中的sql |
悲觀鎖 |
使用select ... for update實現悲觀鎖 |
無 |
樂觀鎖 |
一般使用version或時間戳來實現樂觀鎖 |
使用watch監控對象變化來實現樂觀鎖 |
原子性(Atomicity) |
具有 |
具有 |
一致性(Consistency) |
具有 |
具有 |
隔離性(Isolation) |
具有 |
具有 |
持久性(Durability) |
具有 |
當redis服務器使用AOF持久化模式並appendfsync設置爲always時具有
|
轉自:http://www.sohu.com/a/111695683_464008