日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > 数据库 >内容正文

数据库

MySQL 内核原理分析(一)

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


接下來,我們重點(diǎn)研究每個(gè)組件的底層原理,深入的分析里面的細(xì)節(jié)

總結(jié)

以上是生活随笔為你收集整理的MySQL 内核原理分析(一)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。