MySQL存儲引擎InnoDB的核心特性

大家好,我是anyux。本文介紹MySQL存儲引擎InnoDB的核心特性。

MySQL存儲引擎InnoDB的核心特性

事務

事務的核心特性

Atomic原子性

所有語句作為一個單元全部成功執行或全部取消。不能出現中間狀態

Consistent一致性

如果數據庫在事務開始時處於一種狀態,則在執行該事務期間將保留一致狀態

Isolated隔離性

事務之間不相互影響

Durable持久性

事務成功後,所做的所有更改會準確地記錄在數據庫中。所做的更改不會丟失


事務的生命週期(事務控制語句)

如何開啟事務

<code> begin;
 或者
 start transaction;/<code>

標準事務語句

事務語句就是DML數據操作語句

insert updatedelete

<code> update city set countrycode='CHN' where id=1;
 update city set countrycode='CHN' where id=2;
 update city set countrycode='CHN' where id=3;/<code>

事務的結束

<code> 確認,提交
 commit;
 取消,回滾
 rollback;/<code>

自動提交機制(autocommit)

https://blog.csdn.net/Lkeven/article/details/87367591

默認情況下, MySQL啟用自動提交模式(變量autocommit為ON)。這意味著,只要你執行DML操作的語句,MySQL會立即隱式提交事務(Implicit Commit)

在 MySQL 命令行的默認設置下,事務都是自動提交的,即執行 SQL 語句後就會馬上執行 COMMIT 操作。因此要顯式地開啟一個事務須使用命令 BEGIN 或 START TRANSACTION,或者執行命令 SET AUTOCOMMIT=0,用來禁止使用當前會話的自動提交。

MySQL存儲引擎InnoDB的核心特性

<code> select @@autocommit;
 關閉/開啟 自動提交
 set autocommit=0/1;
 set session autocommit=0;
 全局級別修改後,需要重連後才能生效
 set global autocommit=0;
 如果要永久生效,在配置文件中修改系統變量。
 [mysqld]
 autocommit=0/<code>


隱式提交事務的情況

在同一個會話中,開啟了一個事務,未提交,又開啟新的事務,會觸發隱式提交

在同一個會話中,開啟了一事務,未提交,開啟了自動提交,會觸發隱式提交

導致提交的非事務語句:

DDL語句,(alter,create,drop)

DCL語句,(grant,revoke,set password)

鎖定語句,(lock table和unlock table)

事務的ACID如何保證

概念名詞

redo log:重做日誌

ib_logfile0~1 默認50M 輪詢使用

redo log buffer: redo 內存區域

ibd :存儲數據行和索引

data buffer pool:數據緩衝區池,數據和索引的緩衝

LSN:日誌序列號,ibd,redo log,data buffer pool,redo buffer.MySQL每次啟動數據庫,都會比較磁盤數據頁和redlog的LSN,必須要求兩者一致數據庫才能正常啟動

WAL: write ahead log 日誌優先寫的方式實現持久化,日誌是優先於數據寫入磁盤的動作

髒頁:內存髒頁,內存中發生了修改,沒寫入到磁盤之前,把內存頁稱之為髒頁

CKPT:Checkpoint檢查點,將髒頁寫入到磁盤的動作

TXID:事務號,InnoDB會為每一個事務生成一個事務號,伴隨著整個事務


redo重做日誌

作用:重點保證持久性,也在一定程度上保證了原子性,一致性

記錄了什麼:記錄了內存數據頁的變化

  1. 內存數據的變化
  2. 提供快速的持久化功能(WAL)
  3. CSR過程中實現前滾的操作(磁盤數據頁和redo日誌LSN一致)

redo日誌位置

redo的日誌文件:iblogfile0,iblogfile1

redo的buffer:數據頁的變化信息+數據頁當時的LSN號

redo的刷寫策略

commit;

刷寫當前事務的redo buffer到磁盤,還會順便將一部分redo buffer中沒有提交的事務日誌也刷新到磁盤

MySQL:在啟動時,必須保證redo日誌文件和數據文件LSN必須一致,如果不一致就會觸發CSR,最終數據一致


情況一:

我們做了一個事務,begin;update;commit;

1.在begin時,會立即分配一個TXID=tx_01;

2.update時,會將需要修改的數據頁(dp_01,LSN=101),加載到data_buffer中

3.DBWR線程,會進行dp_01數據頁修改更新,並更新LSN=102

4.LOGBWR日誌寫線程,會將dp_01數據頁的變化+LSN+TXID存儲到redobuffer中

5.執行commit時,LGWR日誌寫線程會將refo buffer信息寫入redo log日誌文件中,基於WAL原則,在日誌完全寫入磁盤後,commit命令才執行成功,(會將此日誌打上commit標記)

6.假如此時宕機,內存髒頁沒有來得及寫入磁盤,內存數據全部丟失

7.MySQL再次重啟時,必須要redo log和磁盤數據頁的LSW一致的,但是,此時dp_01,TXID=tx_01磁盤是LSW=101,dp_01,TXID=tx_01,redolog中LSN=102。MySQL此時無法正常啟動,MySQL觸發CSR,在內存追平ckpt,將內存數據頁更新到磁盤,從而保證磁盤數據頁和redlog的LSN一值,這裡MySQL正常啟動

以上的工作過程,稱之為基於redo的"前滾操作"


undo:回滾日誌

作用:在ACID特性中,主要保證A的特性,同時對CI也有一定的功效

1.記錄了數據修改之前的狀態

2.rollback將內存的數據修改恢復到修改之前

3.在CSR看實現未提交數據的回滾操作

4.實現一致性快照,保證MVCC,訊和寫的操作不會互相阻塞


實現了事務之間的隔離功能,InnoDB中實現的是行級鎖,row-level lock gap next-lock

鎖是為了實現ACID中的C,保證隔離性


讀的隔離級別

隔離級別主要控制的是讀的隔離性

查看默認隔離級別

<code> select @@tx_isolation;/<code>
MySQL存儲引擎InnoDB的核心特性

RU:讀未提交,可髒讀,一般不提交出現

RC:讀已提交,可能出現幻讀,可以防止髒讀

RR:可重複讀(默認級別),功能是防止幻讀現象,利用的是undo的快照技術+GAP(間隙鎖)+NextLock(下鍵鎖)

SR:可串行化讀,可以防止死鎖,但是併發事務性能差

補充:在RC級別下,可以減輕GAP+NextLock鎖的問題,但是會出現幻讀現象,一般在為了讀一致性會在正常select後添加for update語句。但是,請記住執行完一定要commit否則容易出現鎖等待比較嚴重

RU:給示事務已經創建,但是未提交,類似取2000塊錢還沒有按下確認,此時就查詢,顯示餘額減少2000塊,明顯是錯誤的

設置隔離級別

修改配置文件,以下隔離級別選擇一個,然後重啟服務,後面驗證相應的隔離級別

<code> vim /etc/my.cnf
 [mysqld]
 未提交讀,RU隔離級別
 transaction_isolation=read-uncommitted

 已提交讀,RC隔離級別
 transcation_isolation=read-committed
 可重複讀,RR隔離級別
 transcation_isolation=repeatable-read/<code>

此處演示RU隔離級別

開兩個窗口,同時登錄mysql服務器,使用world數據庫。更新操作的命名為A窗口,查詢操作的命名為B窗口。

  1. B窗口查詢更新前的數據
  2. A窗口更新,執行更新操作,在內存中更新了,但是未提交到磁盤中
  3. B窗口查詢事務已創建,查詢更新的數據,在內存中獲取數據,此時內存中的數據與磁盤中數據不同,讀取的內容為髒頁,讀取的行為叫髒讀
  4. A窗口回滾,rollback回滾數
  5. B窗口查詢,查詢更新的數據
MySQL存儲引擎InnoDB的核心特性


此處演示RC隔離級別

設置隔離級別操作為RC級別

B窗口查詢更新前的數據

MySQL存儲引擎InnoDB的核心特性

A窗口更新,執行更新操作,在內存中更新了,但是未提交到磁盤中

MySQL存儲引擎InnoDB的核心特性


B窗口查詢事務已創建,但未提交的數據

MySQL存儲引擎InnoDB的核心特性

A窗口提交數據,即將內存中的數據寫入到磁盤中

MySQL存儲引擎InnoDB的核心特性

B窗口查詢更新後,且已提交的數據

MySQL存儲引擎InnoDB的核心特性

A窗口更新,執行更新操作,並提交事務

MySQL存儲引擎InnoDB的核心特性

B窗口查詢更新後,且已提交的數據

MySQL存儲引擎InnoDB的核心特性

RC隔離級別的演示中,可以看出,如果事務沒有被commit,則查詢結果不會改變。提交讀的方式完全可以避免髒讀的產生。

但是,這在非金融行業中完全可以接受。對於金融行業如結算、統計,報表這種要示可以重複讀取數據且保持不變的情況,RC隔離級別不適合。在RC隔離級別中,每一次commit後,查詢的結果都會發生相應的變化,這樣對於金融行業的一些業務不適用,就需要使用了RR隔離級別

演示RR可重複讀演示

設置隔離級別操作為RR級別

B窗口查詢更新前的數據

MySQL存儲引擎InnoDB的核心特性

A窗口更新,執行更新操作,在內存中更新了,但是未提交到磁盤中

MySQL存儲引擎InnoDB的核心特性

B窗口查詢事務已創建,但未提交的數據

MySQL存儲引擎InnoDB的核心特性

A窗口提交數據,即將內存中的數據寫入到磁盤中

MySQL存儲引擎InnoDB的核心特性

B窗口查詢更新後,且已提交的數據

MySQL存儲引擎InnoDB的核心特性

再開一個窗口C查詢數據

MySQL存儲引擎InnoDB的核心特性

在RR隔離級別中,可以看到即使commit提交了數據,寫入磁盤,在B窗口中也不會查詢到已提交的數據,只要B窗口不斷開連接,它查詢的數據永遠是一致的。而在C窗口中,查詢的數據為新的數據。RR模式為最高的隔離級別

這裡利用了MVCC機制,基於undo的快照,MVCC機制會在每個會話開啟時生成一個致性快照,不論表數據怎麼被修改,讀取的永遠都是快照中的數據。多版本併發就是不同會話擁有不同快照,查詢出的信息也不同

RR通過MVCC機制解決了不可重複讀的問題,但是有可以還會出現幻讀現象,可以通過GAP和Next-lock進行避免

MySQL存儲引擎InnoDB的核心特性


演示幻讀

  1. 創建test表
<code>    use test;/<code>

創建數據表,插入數據表

<code>    create table test(id int auto_increment primary key,name char(10))engine=innodb charset=utf8;
    insert into test values(1,"A"),(2,"B"),(3,"C"),(4,"D");/<code>

查看隔離級別,此時設置為RC隔離級別

<code>    select @@tx_isolation;/<code>
  1. 窗口A 業務需要將id大於2的,name屬性全部修改為x
<code> update test set name='x' where id>2;/<code>
MySQL存儲引擎InnoDB的核心特性

  1. 窗口B 插入新的數據,並提交
<code> insert into test values(5,'E');
 commit;/<code>
MySQL存儲引擎InnoDB的核心特性

  1. 窗口A ,提交。按照預想的那樣,所有id大於2的name屬性都會被修改為x
<code> commit;/<code>
MySQL存儲引擎InnoDB的核心特性

  1. 窗口A,查詢數據
<code> select * from test where id>2;/<code>
MySQL存儲引擎InnoDB的核心特性

以上情形,窗口A在批量更新過程中,在窗口B也在更新數據,預想中的數據所有屬性應該為x,實際上卻存在著name為E的記錄。這種情況稱為幻讀

如何避免幻讀現象呢

首先需要使用RR級別,然後使用的列為索引列,但是RR隔離級別也解決不了幻讀現象。需要使用到兩把鎖提供幫助。將從id為2的行開始,將其以後所有的行都進行鎖定,並且不允許插入,更新,刪除操作。如果出現5-8之間為空的id記錄使用GAP鎖,鎖定9到最大值的鎖就稱為Next-lock鎖。GAP和Next-lock就可以防止幻讀

演示RR隔離級別下避免幻讀

注意需要在RR隔離級別下操作

  1. 創建gap表,並插入數據 use test;
    create table gap(id int auto_increment primary key,name char(10))engine=innodb charset=utf8;
    insert into gap values(1,"A"),(2,"B"),(3,"C"),(9,"D");
  2. 窗口A 業務需要將id大於2的,name屬性全部修改為x
<code> update test set name='x' where id>2;/<code>
MySQL存儲引擎InnoDB的核心特性

  1. 窗口B 插入新的數據
<code> insert into test values(5,'E');/<code>
MySQL存儲引擎InnoDB的核心特性

在更新數據的過程中,語句無法執行,是因為區間鎖的原因.這表明RR級別下,可以通過Next-lock和GAP鎖防止幻讀的出現


分享到:


相關文章: