當前讀和快照讀
InnoDB 給每一個事務生成一個唯一事務 ID 的方法稱為生成快照,因此這種場景稱為快照讀。
但是對於更新數據不能使用快照讀,因為更新數據時如果使用快照讀會可能會覆蓋其他事務的更改。
另外查詢時如果加鎖也會采用當前讀的方式。當前讀就是讀這個數據最新的提交數據。
InnoDB 的多版本並發控制實現了在串行化的隔離級別下讀不加鎖,提高了並發性能。
當前讀
當前讀的場景有下面幾種:
update ... (更新操作)
delete ... (刪除操作)
insert ... (插入操作)
select ... lock in share mode (共享讀鎖)
select ... for update (寫鎖)
當前讀,讀取的是最新版本,並且對讀取的記錄加鎖,阻塞其他事務同時修改相同記錄,避免出現安全問題。
例如,假設要update一條記錄,但是另一個事務已經delete這條數據並且commit了,如果不加鎖就會產生沖突。所以update的時候肯定要是當前讀,得到最新的信息並且鎖定相應的記錄。
快照讀
快照讀的場景:
單純的select操作
(不包括上面當前讀的select ... lock in share mode,select ... for update)
Read Committed隔離級別:每次select都生成一個快照讀
Read Repeatable隔離級別:開啟事務后第一個select語句才是快照讀的地方,而不是一開啟事務就快照讀
例子
下面通過一個例子來理解快照讀和當前讀:
首先建一個表 t,並插入一條數據。
mysql-> create table t(k int)ENGINE=InnoDB;
mysql-> insert into t(k) values (1);
然后將事務的隔離級別設置為 REPEATABLE-READ,接着開啟三個事務,並按照下面的順序進行執行。
MySQL 中事務開始的時間
一般我們會認為 begin/start transaction 是事務開始的時間點,也就是一旦我們執行了 start transaction,就認為事務已經開始了,其實這是錯誤的。事務開始的真正的時間點(LSN),是 start transaction 之后執行的第一條語句,不管是什么語句,不管成功與否。
但是如果你想要達到將 start transaction 作為事務開始的時間點,那么我們必須使用:
start transaction with consistent snapshot
它的含義是:執行 start transaction 同時建立本事務一致性讀的 snapshot . 而不是等到執行第一條語句時,才開始事務,並且建立一致性讀的 snapshot .
效果等價於: start transaction 之后,馬上執行一條 select 語句(此時會建立一致性讀的snapshot)。
事務A | 事務B | 事務C | 說明 |
---|---|---|---|
start transaction with consistent snapshot | 開啟事務A | ||
start transaction with consistent snapshot | 開啟事務B | ||
select k from t; | 事務A讀取結果是1(快照讀) | ||
select k from t; | 事務B讀取結果是1(快照讀) | ||
update t set k = k + 1; | 事務C提交的k為2 | ||
update t set k = k + 1; | update 語句進行了一次當前讀將 k 的值更新為事務 C 已經提交的結果 2,並且在此基礎上再加1得到3 | ||
select k from t; | 執行了 update 操作時會創建一個新版本的數據,並且將自己的事務 ID 作為該數據的版本號,因此在該事務內可以讀到自己更新的數據。因此事務 B 最后一次查詢的結果是 3。 | ||
commit; | 事務B提交 | ||
select k from t; | 事務A讀取結果是1(快照讀) | ||
commit; | 事務A提交 | ||
select k from t; | 讀取結果是 3 |
圖示:
參考
https://juejin.im/post/6844903928627200007
https://www.cnblogs.com/digdeep/p/4947694.html