使用redis的小伙伴都知道,redis有事務的概念,同樣的,mysql中也有事務的概念,那么這兩者之間有什么關系呢?區別到底大不大?今天詳細總結了一下,我們來一探究竟~
在介紹區別之前,再次熟悉一下事務的概念:
0. 概念
事務:Transaction
本質是一組命令的集合,可以一次執行多個命令,所有命令都會序列化,按順序地串行化執行而不會被其它命令插入,不許插隊。將一組需要一起執行的命令放到multi和exec兩個命令之間。multi命令代表事務開始,exec命令代表事務結束,它們之間的命令是原子順序執行的。
0.1 Redis事務的三個特性
-
單獨的隔離操作:事務中的所有命令都會序列化、按順序地執行。事務在執行的過程中,不會被其他客戶端發送來的命令請求所打斷;
-
沒有隔離級別的概念:隊列中的命令沒有提交之前都不會實際的被執行,因為事務提交前任何指令都不會被實際執行,也就不存在”事務內的查詢要看到事務里的更新,在事務外查詢不能看到”這個讓人萬分頭痛的問題;
-
不保證原子性:redis同一個事務中如果有一條命令執行失敗,其后的命令仍然會被執行,沒有回滾;
0.2 Redis事務執行的三個階段
-
開啟:以MULTI開始一個事務;
-
入隊:將多個命令入隊到事務中,接到這些命令並不會立即執行,而是放到等待執行的事務隊列里面;
-
執行:由EXEC命令觸發事務;
舉個例子:
1、正常執行:
2、放棄事務
3、全體連坐(命令集合中含有錯誤的指令(注意是語法錯誤),均連坐,全部失敗)
4、冤有頭,債有主(運行時錯誤,即非語法錯誤,正確命令都會執行,錯誤命令返回錯誤)
但是我們注意到,在錯誤指令后面的指令也會被執行,這里就是"get age"得到的是“11”。
好了,接下來我們看一下Mysql中的事務和redis中的事務的主要區別,
主要從以下四個方面:
1、事務命令
mysql:
- Begin:顯式的開啟一個事務;
- Commit:提交事務,將對數據庫進行的所有的修改變成永久性的;
- Rollback:結束用戶的事務,並撤銷現在正在進行的未提交的修改;
redis:
- Multi:標記事務的開始;
- Exec:執行事務的commands隊列;
- Discard:結束事務,並清除commands隊列;
2、默認狀態
mysql:
- mysql會默認開啟一個事務,且缺省設置是自動提交,即每成功執行一次sql,一個事務就會馬上commit,所以不能rollback;
- 默認情況下如上所述,但是非默認情況下,可以rollback;
redis:
- redis默認不會開啟事務,即command會立即執行,而不會排隊,並不支持rollback;
3、使用方式
mysql(包含兩種方式):
- 用Begin、Rollback、commit顯式開啟並控制一個 新的 Transaction;
- 執行命令 set autocommit=0,用來禁止當前會話自動commit,控制 默認開啟的事務;
redis:
- 用multi、exec、discard,顯式開啟並控制一個Transaction。(注意:這里沒有強調 “新的” ,因為默認是不會開啟事務的)。
4、實現原理
mysql:
- mysql實現事務,是基於undo/redo日志;
undo
記錄修改前
狀態,rollback
基於undo日志實現;redo
記錄修改后
的狀態,commit
基於redo日志實現;- 在mysql中無論是否開啟事務,sql都會被立即執行並返回執行結果,只是
事務開啟
后執行后的狀態
只是記錄在redo日志
,執行commit
之后,數據才會被寫入磁盤;
如: int insertSelective = serviceOrderMapper.insertSelective(s);
上述代碼,insertSelective 將會被立即賦值(無論是否開啟事務,只是結果或未被寫入磁盤):
redis:
- redis實現事務,是基於
commands隊列;
- 如果沒有開啟事務,command將會被立即執行並返回執行結果,並且直接寫入磁盤;
- 如果事務開啟,command不會被立即執行,而是排入隊列,並返回
排隊狀態
(具體依賴於客戶端(例如:spring-data-redis)自身實現)。調用exec
才會執行commands隊列;
boolean a = redisTemplate.opsForZSet().add("generalService",orderId,System.currentTimeMillis());
上述代碼,
- 如果沒有開啟事務,操作被立即執行,a將會被立即賦值(true/false);
- 如果開啟事務,操作不會被立即執行,將會返回null值,而a的類型是boolean,所以將會拋出異常:java.lang.NullPointerException;
參考並致謝:
Over......