consistent non-locking read,經過行多版本控制的方式讀取當前執行時間點的記錄mysql
默認狀況下innodb select沒有任何鎖,讀到的記錄在更新就經過undo讀以前版本,serializable時候讀會被阻塞,由於它默認加一個lock in share mode
--->like oraclegit
原理
undo && read_view
經過read_view判斷一條記錄是否可見,不可見(在更新被鎖住)就經過undo回滾到以前版本,以前的版本再讀trx_id,還不可見再回滾,rc只要回滾一個版本,rr可能要回滾不少版本,最大trx_id是持久化的,保存在共享表空間中github
其實理解下就是事務在不在活躍列表中,在的話這個事務對記錄作的動做就不可見須要找記錄的前鏡像(rc老是讀最新的非鎖定版本,rr老是讀最老的非鎖定版本,後續會有專門的文章說明)sql
舉個栗子數據庫
session1: (root@localhost) [test]> select * from t; +------+ | a | +------+ | 1 | +------+ 1 row in set (0.00 sec) (root@localhost) [test]> begin; Query OK, 0 rows affected (0.00 sec) (root@localhost) [test]> update t set a = a + 1 where a = 1; Query OK, 1 row affected (0.01 sec) Rows matched: 1 Changed: 1 Warnings: 0 (root@localhost) [test]> select * from t; +------+ | a | +------+ | 2 | +------+ 1 row in set (0.00 sec) session2: (root@localhost) [test]> begin; Query OK, 0 rows affected (0.00 sec) (root@localhost) [test]> select * from t; +------+ | a | +------+ | 1 | +------+ 1 row in set (0.00 sec)
第一個會話開啓事務更新記錄,不提交,此時記錄是被鎖住的編程
新開一個會話去select 這條記錄,並不會由於有鎖而阻塞,讀到的是原來的記錄session
此時commit以後,以前版本的undo是不能被立刻回收的,由於其餘線程可能還在引用以前版本的undo,真正的回收undo是purge線程作的mvc
purge的做用是刪除undo,真正刪除一條記錄(完成update和delete)oracle
delete from table where pk=1; 在page中只是標記爲刪除,page上並無真正的刪除 相關參數:innodb_purge_threads 默認是1,5.7中設大一點,4或者8,都是ssd性能比較好
5.5以前全部的purge操做都是master thread作的分佈式
默認只有一個purge thread
innodb_purge_threads={0|1}5.6
N purge thread
innodb_purge_threads={4}
1024個槽------1024個undo回滾段,每一個槽對應不一樣的undo日誌
一旦事務提交,undo就放到hitory list中
tips:
由於記錄不是有序的,因此purge操做須要大量離散讀取操做
undo不斷增大,不能有效回收,致使系統空間不斷增大,
最主要的緣由有兩個:
索引沒有添加
檢查slow log存在大事務
拆大爲小
其實就一點,一個事務執行時間很長,那對應的undo就不能回收,至少要commit完成後才能回收
另外回滾比提交慢很是多,commit很快,rollback須要的時間就是事務執行的時間,邏輯回滾
tips:
目前MySQL已經支持在線回收undo,詳見阿里數據庫內核月報
以前咱們談到binlog和redo的一致性是經過一個內部的xa事務保證的,這裏簡單聊下外部的分佈式事務
(root@localhost) [test]> xa start 'a'; -- 開啓一個分佈式事務 Query OK, 0 rows affected (0.00 sec) (root@localhost) [test]> insert into t values(2000); Query OK, 1 row affected (0.09 sec) (root@localhost) [test]> insert into t values(3000); Query OK, 1 row affected (0.00 sec) (root@localhost) [test]> xa end 'a'; -- 結束 Query OK, 0 rows affected (0.00 sec) (root@localhost) [test]> xa prepare 'a'; -- 寫prepare Query OK, 0 rows affected (0.03 sec) (root@localhost) [test]> xa recover; -- 看一眼,有一個分佈式事務 +----------+--------------+--------------+------+ | formatID | gtrid_length | bqual_length | data | +----------+--------------+--------------+------+ | 1 | 1 | 0 | a | +----------+--------------+--------------+------+ 1 row in set (0.00 sec) (root@localhost) [test]> xa rollback 'a'; -- 回滾 Query OK, 0 rows affected (0.01 sec) (root@localhost) [test]> xa recover; -- 再看下,沒了 Empty set (0.00 sec)
這是再單實例上模擬的,意義不大
真正應用程序中兩個實例作分佈式事務,須要兩邊的prepare都成功才能最終提交
create procedure load1(count int unsigned) begin declare s int unsigned default 1; declare c char(80) default repeat('a',80); while s <= count do insert into t1 select NULL,c; set s = s+1; end while; end call load1(1000)
上面這個存儲過程的調用,auto commit致使了insert會處罰一千次fsync
正確姿式:
begin; call load1(1000) commit;
事務拆大爲小,緣由就是binlog在搞鬼,其實不必定是大事務,大的操做都要拆吧
計算利息,拆了批量執行
update account set account_total = account_total + (1 + interest_rate)
爲何要拆?老生常談的、
題外話:
binlog是有點討厭不像oracle用redo,歷史緣由,很差說
也有好處,作大數據平臺集成很是簡單,把MySQL的的數據實時推到大數據平臺上太簡單,github上一搜一大把項目直接用