MySQL 内核原理分析(一)
生活随笔
收集整理的這篇文章主要介紹了
MySQL 内核原理分析(一)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
- 學一個技術,我們先要 跳出來,看整體,先要在腦中有一個這個技術的全貌。然后再 鉆進去,看本質,深入的研究細節。這樣方便我們建立一個立體的知識網絡。不然單學多個知識點,是串不起來的。不容易記住,理解也不會深刻。
- 所以,我們先把 MySQL 拆解一下,看看內部有哪些組件,我們 Java系統執行一條SQL,MySQL的內部是如何運作,給我們返回結果的。
- 我們先從我們訪問數據庫說起
- 我們想要查詢數據庫,首先得建立網絡連接
- MySQL 驅動負責建立網絡連接,然后請求 MySQL 數據庫
- 其實就是創建了一個數據庫連接
- Java系統的 數據庫連接池
- 如果我們的系統所有線程訪問數據庫時,都使用一個連接會怎樣
- 所有線程搶奪一個連接,沒有連接 就操作不了數據庫,效率極低,因為需要后面的線程需要等待前面的線程處理完才行
- 所有線程搶奪一個連接,沒有連接 就操作不了數據庫,效率極低,因為需要后面的線程需要等待前面的線程處理完才行
- 如果我們的系統所有線程訪問數據庫時,都使用一個連接會怎樣
- 我們的系統如果每個線程訪問數據庫時,都創建一個連接,然后銷毀,會怎樣
- 創建連接需要網絡通信,網絡通信是很耗時的
- 好不容易創建了連接,查詢完就給銷毀了,那效率肯定低
- 所以,我們要使用數據庫連接池
- 數據庫連接池里,會有多個數據庫連接
- 每個線程使用完連接后,會放回池子,連接不會銷毀
- 常用的數據庫連接池有 DBCP、C3P0、Druid
- 數據庫連接池里,會有多個數據庫連接
- MySQL 的 連接器
- Java 系統要和MySQL 建立多個連接,那 MySQL 自然也需要維護與系統之間的連接
- 所以,MySQL 整體架構的第一個組件就是 連接器
- 所以,MySQL 整體架構的第一個組件就是 連接器
- Java 系統要和MySQL 建立多個連接,那 MySQL 自然也需要維護與系統之間的連接
- MySQL 連接器的功能
- 連接器負責跟客戶端建立連接、獲取權限、維持和管理連接
- 連接器內部也是一個 連接池,里面維護了各個系統跟這個數據庫創建的所有連接
- Java 系統連接Mysql 的過程
- 首先完成TCP的三次握手,創建一個網絡連接
- 然后開始權限認證,也就是 你的 用戶名 、密碼 是否正確
- 連接成功后,如果沒有后續動作,這個連接會處于空閑狀態
- 空閑一定時間后,會自動斷開連接,由參數 wait_timeout 控制的,默認值是 8 小時
- 我們現在已經知道,我們執行SQL,一定要先連接到數據庫。數據庫的 連接器 會對系統進行權限認證,如果認證成功,就創建了一個數據庫連接。
- 那么,連接之后是怎么執行SQL語句的呢?
- 一個基本的知識點,網絡連接必須要分配給一個線程去處理
- 由一個 線程 來監聽 和 讀取 Java系統請求的數據
- 線程會從網絡請求中解析出我們發送的sql語句
- 線程獲取到了我們寫好的SQL語句,那交給誰來執行呢
- 其實在執行之前,還有一步,就是查詢緩存
- 之前執行過的語句及其結果可能會以 key-value 對的形式,被直接緩存在內存中
- key 是查詢的語句
- value 是查詢的結果
- 如果在緩存中找到 key,那么這個 value 就會被直接返回給客戶端
- 但是,建議不要使用緩存,往往弊大于利
- 查詢緩存的失效非常頻繁,只要有對一個表的更新,這個表上所有的查詢緩存都會被清空
- 查詢緩存的命中率會非常低
- 可以將參數 query_cache_type 設置成 DEMAND,關閉緩存
- MySQL 8.0 版本直接將查詢緩存的整塊功能刪掉了
- 所以接下來的圖,我就不畫 查詢緩存 這個步驟了
- 沒有了查詢緩存這個功能,我們寫好的SQL,都是交給查詢解析器來分析的
- 解析器
- 我們寫的SQL 語句,人認識,但是機器是不認識的,所以必須要解析我們的語句
- 拿一條SQL舉例
- select id,name from user where id = 10
- 我們的SQL 是由 字符串 和 空格 組成的
- 有些字符串 是 MySQL 的關鍵字
- MySQL 會識別這些關鍵字
- 語法解析器 會將 上面的SQL拆解為幾部分
- 要從 user 表里查詢數據
- 查詢 id 字段的值,等于 10 的那行數據
- 對查詢的那行數據,提取出 id 、name 兩個字段
- 如果語法不對,解析器會提示我們
- ERROR 1064 (42000): You have an error in your SQL syntax;
- ERROR 1064 (42000): You have an error in your SQL syntax;
- 我們的SQL 是由 字符串 和 空格 組成的
- select id,name from user where id = 10
- 優化器
- 經過了解析器,MySQL 就知道你要做什么了,在開始執行之前,還要先經過優化器的處理,選擇一個最優路徑
- 我們的表可能創建了多個索引,或者多表關聯(join)的時候
- 這時,是有多個路徑可以查詢到結果的,但是執行效率會不同
- 查詢優化器就是干這個事的,它會選一個效率最高的路徑
- 這個我們后面會仔細分析,這里知道它會選一個最優路徑就好。先了解MySQL的整體架構,再深究細節
- 執行器
- MySQL 通過分析器知道了你要做什么
- 通過優化器知道了該怎么做,生成執行計劃
- 于是就進入了執行器階段,負責這個計劃的執行
- 執行器 主要是 操作存儲引擎來返回結果的,我們重點要關注的是存儲引擎的執行原理
- 接下來,我們來研究一下,存儲引擎的架構設計,以及如何基于存儲引擎來完成一條更新語句的執行。
- MySQL 有多種存儲引擎,InnoDB、MyISAM等,我們就說最常用的InnoDB。接下來的圖,我就只畫 InnoDB 存儲引擎這部分的了,連接、解釋器、優化器會去掉,不然畫不下了。
- 我們以一條更新操作來看一下 InnoDB 的運行流程
- 用這個SQL舉例:
- update users set name = ‘李四’ where id = 10
- InnoDB 中 重要的內存結構 Buffer Pool
- Buffer Pool 緩沖池,是 InnoDB 存儲引擎的核心組件。這里會緩存大量的數據,查詢時會先看 緩沖池 內是否有這條數據,如果有,就可以不用查磁盤了
- 如果 Buffer Pool 中沒有這條數據,就要先從磁盤中加載進來
- Buffer Pool 中的數據是緩存頁,磁盤中的表數據是數據頁,內部有其數據結構。我們這里忽略,先看一下整體的運行流程,之后再分析里面的物理結構。
- undo 日志文件
- 如果我們執行一個更新語句,在沒有提交事務之前,我們都是可以對數據進行回滾的
- undo 日志文件就是保證我們可以回滾數據的一個組件
- 舉例:
- 如果我們要把 id = 10 的數據的 name 從 張三 改為 李四
- 第一步是把數據加載到 Buffer Pool 里
- 第二步 就要把 id = 10 ,name = 張三 的這條原始數據,寫到undo日志文件
- 如果數據回滾,就會從 undo 日志文件中讀取原始數據恢復
- 如果我們要把 id = 10 的數據的 name 從 張三 改為 李四
- 備注:InnoDB 是個存儲引擎,步驟2 其實也是我們上面說到的 執行器 來把原始數據寫到磁盤上的,后面的步驟,但凡有寫磁盤、讀磁盤的操作,都是執行器執行的。這里為了畫圖方便,直接連線了
- 然后 執行器 會更新 Buffer Pool 中的緩存數據
- 現在,緩存內的數據已經從 張三 更新到 李四 了
- 那么,現在有一個問題,如果 MySQL 此時宕機了會有問題嗎
- 因為現在還沒有提交事務,代表這條語句還沒執行完
- 所以,此時宕機沒有關系,事務沒提交,重啟后內存數據就沒了,磁盤數據也沒變化
- 磁盤的數據也是原始數據,所以沒關系
- 我們在內存中修改的數據,終究要刷到磁盤上的。MySQL 不會馬上把這條數據刷到磁盤上,會等系統不忙碌時,再刷回去。因為刷磁盤這事,本來實時性要就不高,我們查詢的時候也是基于內存的,磁盤是什么數據無所謂,只要保證最終一致就好了。
- 我們只有提交事務后,才能把內存修改的數據刷到磁盤上
- 提交事務是一個過程,這個過程中我們需要先寫入幾份日志文件,只有這幾個日志文件都寫成功了,事務才算提交成功
- 所以,這里開始介紹 InnoDB 存儲引擎中的另一個組件 Redo log Buffer
- 內存中的 Redo log Buffer 配合 磁盤上的 redo log 日志文件,可以在 MySQL 意外宕機的情況下,恢復內存數據的。它會記錄內存中修改的數據,然后把這些數據刷到磁盤上的 redo log 日志中
- 之前,我們已經修改了內存數據,在修改完成后,執行器就會向Redo log Buffer 中寫入日志,到這一步為止,我們已經執行完了這條SQL語句,就差提交事務
- 如果我們提交事務,第一步就是把 Redo log Buffer 中的日志刷到磁盤上的 redo log 中
- 此時,如果 MySQL 宕機,數據是不會丟失的。重啟后,會加載磁盤上的 redo log 日志文件,恢復到內存中
- redo log 日志是 偏物理層面的日志,也叫 重做日志。而 binlog 是歸檔日志(這個后面說)
- 為什么說是偏物理層面的日志,就是說不是給人看的,你看了也不知道修改的啥
- 比如,對哪些數據頁上的什么數據做了什么修改
備注:提交事務,不是一步完成的,是一個過程。后面的步驟 5、6、7都屬于提交事務的過程,只要有一步失敗,那提交就是不成功的
- 比如,對哪些數據頁上的什么數據做了什么修改
- 為什么說是偏物理層面的日志,就是說不是給人看的,你看了也不知道修改的啥
- 把 redo log 從內存刷到磁盤的策略有三種
- 通過參數 innodb_flush_log_at_trx_commit 來配置 ,默認值為 1
- 值為 0 :提交事務后,不會把 redo log buffer 里的日志刷到磁盤
- 此時如果 MySQL 宕機,redo log buffer 內數據全部丟失
- 值為 1 :提交事務后立刻把日志刷到磁盤,只要提交事務成功,那 redo log 一定在磁盤
- 值為 2 :提交事務后會把 redo log 先刷到 os cache(操作系統緩存) 里 ,然后 os cache 在適當的時機刷入磁盤
- 在os cache 沒刷磁盤期間,如果 MySQL 宕機,這部分數據會丟失
- 值為 0 :提交事務后,不會把 redo log buffer 里的日志刷到磁盤
- 我們平時開發還是要用 innodb_flush_log_at_trx_commit = 1 ,立刻刷磁盤。保證提交事務后,數據絕對不會丟失
- 通過參數 innodb_flush_log_at_trx_commit 來配置 ,默認值為 1
- redo log 是偏物理層面的日志。如果發生數據庫操作失誤,我們不能根據這個來恢復數據。我們需要用 binlog 來恢復,binlog 是偏邏輯性的日志
- binlog
- binlog 也叫 歸檔日志,是邏輯性的日志
- 如:對 users 表的 id = 10 的一行數據做了更新操作
- binlog 不是 InnoDB 存儲引擎特有的日志文件,是屬于 MySQL Server 自己的日志文件
- 我們開始提交事務,第一步是把 redo log 日志刷到磁盤, 接下來執行器還要繼續寫 binlog 日志到磁盤
- binlog 刷磁盤有兩種策略,通過 sync_binlog 參數來配置,默認值 0
- 值為 0 :先刷到 os cache 緩存,然后不定時刷入磁盤
- 如果宕機,可能會丟失數據
- 值為 1 :直接刷到 磁盤文件中 ,是要提交事務成功,數據一定不會丟失
- 值為 0 :先刷到 os cache 緩存,然后不定時刷入磁盤
- binlog 也叫 歸檔日志,是邏輯性的日志
- 最后,是 事務提交的最后一步
- 執行器 會把本次更新對應的 binlog 日志的文件名 和 本次更新的 binlog 日志在文件中的位置,都寫入 redo log 日志文件中
- 同時,還會寫入一個 commit 標記
- 只有完成了這一步,才算是 事務提交成功
- 為什么要在 redo log 中寫入 commit 標記呢?
- 用來保證 redo log 和 bin log 的數據一致性
- 舉例:
- 如果完成了第5步,刷入了 redo log 后,MySQL 宕機了,那 bin log 就沒法寫入 commit 標記,那這條數據沒有 commit 標記,就是無效的,提交事務失敗
- 如果第6步,刷入了 binlog 后,MySQL 宕機了,一樣沒有 commit 標記,也是無效的
- 現在,本條更新語句已經提交了事務,更新完畢了
- 此時,內存上的數據 已經是 更新過的 name = 李四 ,磁盤上是 name = 張三
- 此時,MySQL 宕機是無所謂的,數據不會丟失,重啟后會從redo log 加載到緩沖池
- 然后,是最后一個步驟
- MySQL 有一個后臺的 IO 線程,在之后的某個時間,會隨機的把內存 Buffer pool 中修改的臟數據刷回磁盤的數據文件中
- 臟數據:就是內存和磁盤不一致,但是沒有什么影響
- 臟數據:就是內存和磁盤不一致,但是沒有什么影響
- MySQL 有一個后臺的 IO 線程,在之后的某個時間,會隨機的把內存 Buffer pool 中修改的臟數據刷回磁盤的數據文件中
- 到現在,我們已經知道了 MySQL 的整體運行流程,和內部的運行原理,MySQL 的全貌我們已經看見了。我們在腦海中要有下面這張圖
接下來,我們重點研究每個組件的底層原理,深入的分析里面的細節
總結
以上是生活随笔為你收集整理的MySQL 内核原理分析(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 认清GPU的流处理器作用
- 下一篇: java 判断文件是否pdf_如何确定文