網站首頁 編程語言 正文
文章目錄
- 1. MVCC多版本并發控制機制
- 2. BufferPool緩存機制
1. MVCC多版本并發控制機制
Mysql可以在可重復讀隔離級別下可以保證事務較高的隔離性,這個隔離性是由MVCC機制來保證的,對一行數據的讀和寫兩個操作默認是不會通過加鎖互斥來保證隔離性,避免了頻繁加鎖互斥,而在Mysql在讀已提交和可重復讀隔離級別下都實現了MVCC機制。
undo日志版本鏈與read view機制詳解
undo日志版本鏈是指一行數據被多個事務依次修改過后,在每個事務修改完后,Mysql會保留修改前的數據undo回滾日志,并且用兩個隱藏字段trx_id(事務ID)和roll_pointer(會滾指針)把這些undo日志串聯起來形成一個歷史記錄版本鏈。
trx_id:表示對數據修改的事務的ID
roll_pointer:指向被修改數據的上一個版本
在可重復讀隔離級別,當事務開啟,執行任何查詢sql時會生成當前事務的一致性視圖read-view,該視圖在事務結束之前都不會變化(如果是讀已提交隔離級別在每次執行查詢sql時都會重新生成),這個視圖由執行查詢時所有未提交事 id數組(數組里最小的id為min_id)和已創建的最大事務id(max_id)組成,事務里的任何sql查詢結果需要從對應版本鏈里的最新數據開始逐條跟read-view做比對從而得到最終的快照結果。
注意:begin/start transaction命令并不是一個事務的起點,在執行到它們之后的第一個修改操作InnoDB表的語句時事務才算是真正的啟動,此時事務會向Mysql申請事務ID,mysql內部是嚴格按照事務的啟動順序來分配事務ID的
版本鏈比對規則:
- 如果 row 的 trx_id 落在綠色部分( trx_id<min_id ),表示這個版本是已提交的事務生成的,這個數據是可見的;
- 如果 row 的 trx_id 落在紅色部分( trx_id>max_id ),表示這個版本是由將來啟動的事務生成的,是不可見的(若 row 的 trx_id 就是當前自己的事務是可見的);
- 如果 row 的 trx_id 落在黃色部分(min_id <=trx_id<= max_id),那就包括兩種情況
a. 若 row 的 trx_id 在視圖數組中,表示這個版本是由還沒提交的事務生成的,不可見(若 row 的 trx_id 就是當前自
己的事務是可見的);
b. 若 row 的 trx_id 不在視圖數組中,表示這個版本是已經提交了的事務生成的,可見。
read-view執行原理案例分析
這里首先列出了五個事務的sql語句執行情況,如下:
由上圖可見,總共有五個事務,事務的id為
100
,200
,300
,1
,2
,下面我們就來逐步分析:
- 首先事務300執行
update account set name = 'lilei300' where id = 1;
然后提交。接著事務ID為1的事務執行select name from account where id = 1;
我們可以很簡單的看出查詢結果為lilei300,那為什么會是lilei300。首先我們可以看出此時的undo.log的日志版本鏈的情況是這樣的:
其中藍色為最新的數據,紅色為老數據。然后mysql會維護一個事務集,如下:
然后mysql會從日志版本鏈的最新的數據開始向下遍歷,這個例子先從上面第一個藍色的開始,取出其事務ID為300,到事務集合中去比對,發現位于黃色的區域(即有可能還是未提交),然后判斷是否在事務ID的read-view數組中(該數組前面說過是由活躍事務的ID數組以及最大已提交事務組成),目前由第一張事務的執行情況圖我們可以看出,目前事務ID為1的read-view為
[100,200],300
構成,而300沒在read-view的活躍數組中,所以300對數據進行的更改對事務1是可見的,所以日志版本鏈就不需要向下遍歷了,查詢的結果即為lilei300。
- 然后ID為100的事務又執行了兩條更新語句,分別為
update account set name = 'lilei1' where id = 1;
和update account set name = 'lilei2' where id = 1;
,接著ID為1的事務再次執行了查詢select name from account where id = 1;
同樣從會滾日子鏈開始分析,藍色同樣為最新的數據,紅色為老數據,目前的日子版本鏈的情況如下所示:
首先從最新的數據開始便利日志版本鏈,首先拿到ID為100的數據,比對事務集合,其同樣落在黃色區域,然后事務ID1比較其readview,發現其落在了活躍數組[100,200]中(注意除非一些特殊情況ID為1的事務對數據進行了一些修改,其readview在整個過程中一般是不變的,雖然每次執行一次查詢語句都會重新生成read-view),所以其對ID為1的事務是不可見的。然后接著向下遍歷,知道遍歷到ID為300的事務,發現其是可見的,所以查詢到的數據同樣是lilei300。后面的過程同樣是按照這個過程分析。
mysql通過這個機制在RR隔離級別下實現了事務的可重復讀而不用加鎖。而在讀已提交的情況下Mysql同樣實現了MVCC機制,不同于RR級別,在讀已提交的隔離級別下,每當一個事務被提交,其它事務相應的read-view會及時更新。
2. BufferPool緩存機制
InnoDB引擎SQL執行的BufferPool緩存機制如下所示:
對上圖進行分析:
- 首先客戶端執行sql語句
update t set name='zhuge666' where id=1
- 然后經過連接器進行客戶端的權限驗證(判斷該客戶端是否有修改該條數據的權限)
- 然后通過分析器對sql語句進行詞法分析
- 接著經過優化器來生成sql執行計劃以及選擇合適的索引
- 最后來到執行器開始真正執行sql(InnoDB層)
- 首先InnoDB會將id為1的數據所在的頁加載到BufferPool中
- 然后將要更新的那條數據舊的值寫入到undo日志中(用于后期會滾)
- 然后更新BufferPool中的緩存數據
- 然后將更新情況寫入到redo日志緩存中
- 客戶端準備提交事務
- 首先redo日志寫入到磁盤
- binlog(主要用于回復數據庫磁盤中的數據)日志寫入磁盤
- 寫入commit標記到redo日志文件中,提交事務完成,該標記為了保證事務提交后redo于binlog日志的一致性。
- Mysql后臺IO線程隨機以page為單位將更新后的數據寫入到磁盤
以上便是該sql語句執行的整個過程,但對于上面的過程可能還會存在下面這些問題:
- InnoDB體系結構是什么樣子的?
InnoDB存儲引擎有多個內存塊,可以認為這些內存塊共同組成了一個大的內存池,負責如下工作:
- 維護所有進程/線程需要訪問多個內部數據結構
- 緩沖磁盤上的數據,方便快速讀?。╞ufferPool),同時在對磁盤文件的數據修改之前在這里緩存
- 重做日志緩存(redo.log)
…
后臺線程的主要作用是負責刷新內存池中的數據,保證緩沖池中的內存緩存是最近的數據。此外將修改的數據文件刷新到磁盤文件,同時保證在數據庫發生異常的情況下InnoDB能夠恢復到正常運行的狀態。
- Mysql的bufferpool的組成?
3. Mysql是如何對緩沖池進行管理?
通常來說數據庫中的緩沖池是通過LRU算法來進行管理的,即最頻繁使用的頁在LRU列表的前端,而最少使用的頁在LRU列表的尾端。但InnoDB使用的并不是樸素的LRU算法,而是在LRU列表中加入了midpoint的位置,midpoint將LRU列表的數據頁分為熱數據和冷數據兩部分。(這部分還是比較復雜的)
- rode日志的刷新時機?
- Master Thread每一秒將重做日志緩存刷新到重做日志文件
- 每個事務提交時會將重做日志緩存刷新到重做日志文件
- 當重做日志緩存剩余空間下雨1/2時,重做日志緩存刷新到重做日志文件
- 為什么Mysql不能直接更新磁盤上的數據而且設置這么一套復雜的機制來執行SQL了?
因為來一個請求就直接對磁盤文件進行隨機讀寫,然后更新磁盤文件里的數據性能可能相當差。因為磁盤隨機讀寫的性能是非常差的,所以直接更新磁盤文件是不能讓數據庫抗住很高并發的。 Mysql這套機制看起來復雜,但它可以保證每個更新請求都是更新內存BufferPool,然后順序寫日志文件,同時還能保證各種異常情況下的數據一致性。 更新內存的性能是極高的,然后順序寫磁盤上的日志文件的性能也是非常高的,要遠高于隨機讀寫磁盤文件。 正是通過這套機制,才能讓我們的MySQL數據庫在較高配置的機器上每秒可以抗下幾千的讀寫請求。
原文鏈接:https://blog.csdn.net/qq_43456605/article/details/133577606
- 上一篇:沒有了
- 下一篇:沒有了
相關推薦
- 2022-10-21 Golang驗證器之validator是使用詳解_Golang
- 2022-04-21 C#?TrackBar拖動條改變滑塊顏色_C#教程
- 2023-03-20 C#實現拷貝文件到另一個文件夾下_C#教程
- 2022-11-17 React路由渲染方式與withRouter高階組件及自定義導航組件應用詳細介紹_React
- 2023-10-09 markdown和富文本編輯器的區別
- 2022-12-07 C++?IO設備讀寫功能實現詳解_C 語言
- 2022-10-29 css transform導致字體像素模糊的問題解決辦法
- 2021-12-05 C語言SetConsoleCursorInfo函數使用方法_C 語言
- 欄目分類
-
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支