一篇文章讓你搞懂Mysql InnoDB內存結構

      網友投稿 1063 2022-05-28

      前言

      我們都熟悉mysql數據庫服務架構,也清楚SQL的執行順序,Mysql的數據在磁盤和內存中的存儲結構是采用B+樹的數據結構,但是在InnoDB引擎中,數據在內存和磁盤中的展示形式以及怎么和Mysql的服務架構建立聯系,SQL查詢和InnoDB引擎之前的聯系,可能就不是不清楚了。

      Mysql的邏輯架構圖如下所示:

      InnoDB存儲引擎結構

      InnoDB存儲引擎的邏輯存儲結構是什么呢,其實所有的數據都被邏輯地放在了一個空間中這個空間中的文件就是實際存在的物理文件,即表空間。默認情況下,一個數據庫表占用一個表空間,表空間中存放該表對應的數據、索引、insert buffer bitmap undo信息、insert buffer 索引頁、double write buffer 等是放在共享表空間中的。

      # 默認一個數據庫表單獨占有一個表空間 show variables like '%innodb_file_per_table%' innodb_file_per_table=ON # 修改設置 SET GLOBAL innodb_file_per_table=OFF; 1.2.3.4.5.

      2.1、InnoDB表存儲引擎文件

      每個表空間由 段 segment 區 extent 頁 page 組成。頁是數據存儲數據的基本單位,默認大小為 16kb。 區是由連續頁組成的空間,默認大小為 1MB。多個區構成表的段。 InnoDB 邏輯存儲結構

      ![](https://img-blog.csdnimg.cn/img_convert/d7c5173e457761be9d5703cae0da33d8.png

      在我們執行sql時,不論是查詢還是修改,myql 總會把數據從磁盤讀取內內存中,而且在讀取數據時,不會單獨加在一條數據,而是直接加載數據所在的數據頁到內存中,而讀取的方式有兩種,現行預讀方式和隨機預讀方式,默認采用線性預讀方式。

      InnoDB 引擎架構

      ![](https://img-blog.csdnimg.cn/img_convert/afe95810daa17f8feead88f38fd4ed9a.png

      2.2、InnoDB 預讀機制

      線性預讀和隨機預讀:

      線性預讀是以 extent 為單位,而隨機預讀是以 extent 中的page 為單位,線性預讀著眼于將下一個extent 數據讀取到 buffer pool 中,而隨機預讀是將當前extent中剩余的page讀到 buffer pool 中。 如果一個extent 區中被順序讀取得page數量超過一定的數量( innodb_read_ahead_threshold),則直接加載 extent 中剩余的數據頁。

      2.3、InnoDB 特性

      2.3.1、插入緩存

      插入緩沖(Insert Buffer/Change Buffer)為了提升插入性能,insert buffer 是 insert buffer 的增強版,insert buffer 只對插入有效,而change buffer對 insert/update/delete 都有效。插入緩存只對非唯一索引和輔助索引有效,對每一次的插入不是寫到索引頁中,而是先判斷插入的非聚集索引頁是否在緩存中,如果在則直接插入,不存在則插入到 insert buffer 中,按照一定的頻率進行合并操作,寫回到磁盤。這樣將多個插入操作合并進一個操作中,目的是為了減少隨機IO帶來的性能損耗。

      2.3.2、二次寫 (double write)

      插入緩存給 InnoDB 存儲引擎帶來了性能上的提升,而 double write 則是保障 InnoDB 存儲引擎操作數據頁的可靠性。double write 分為兩部分組成,一部分在內存中的 double write buffer, 大小為 2MB,另一部分是物理磁盤上共享表空間中連續的128個數據頁,即2個區大小(同樣是2MB)。在對緩沖池的臟頁進行刷新時,并不是直接寫磁盤,而是通過 memcpy 函數將臟頁復制到內存中的 doublewrite buffer,之后通過doublewrite buffer 在分兩次,每次1MB 順序地寫入共享表空間的物理磁盤上,然后馬上調用 fsync 將數據同步至磁盤。由于doublewrite 是連續的空間,這樣的順序寫IO開銷不大。在doublewrite頁寫完后,再次離散寫入各個表空間。如果操作系統在將數據頁寫入磁盤發生崩潰,那么在恢復的過程中,InnoDB 引擎會從共享表空間中的doublewrite找到該頁的一個副本,將其復制到表空間文件,再應用重做日志。

      ![](https://img-blog.csdnimg.cn/img_convert/d0e6808f2e535df42ef1e81ac90dfef9.png

      2.3.3、自適應hash索引

      hash是一種等值查詢,InnoDB 存儲引擎會監控對表上各個索引頁的查詢,如果觀察到建立hash索引會帶來速度提升,則建立相應的索引,因此稱為自適應哈希索引(Adaptive Hash Index,AHI)。AHI是通過緩沖池中的B+樹頁構造而來,建立速度比較快,而且不需要對整張表建立哈希索引,只是建立熱點頁的索引。AHI默認是開啟的狀態。

      2.3.4、異步IO

      為了提高磁盤的操作性能, 當前的數據庫系統一般采用異步IO(Asynchronous IO,AIO)的方式來處理磁盤操作,InnoDB 存儲引擎也是如此,AIO的優勢在于減少SQL查詢需要的時間,另外也可以進行IO Merge 操作,就是將多個IO合并為1個IO,這樣就可以提高IOPS的性能。

      # 開啟本地 AIO show valiables like 'innodb_use_native_aio';1.2.

      2.3.5、刷新鄰接頁

      InnoDB 存儲引擎提供了 Flush Neighbor Page(刷新鄰接頁)的特性,當刷新一個臟頁時,InnoDB 存儲引擎會檢測該區內是否存在其它臟頁,如果存在,則一并進行刷新,這樣做得好處顯而易見,可以將多個操作合并成一個操作,對于機械硬盤有著明顯的優勢,但對于固定硬盤,本事就有較高的IOPS,是否開啟需要根據情況而定,參數設置如下:

      show varables like 'innodb_flush_neighbors'1.

      3、SQL 執行的邏輯

      3.1、SQL 執行

      mysql寫文件有2塊緩存。一塊是自己定義在內存的log buffer, 另一個是磁盤映射到內存的os cache。 mysql可以 調用 flush 主動將log buffer 刷新到磁盤內存映射,也可以調用 fsync 強制操作系同步磁盤映射文件到磁盤。默認情況下innodb_flush_log_at_trx_commit和sync_binlog 配置都為1。

      不僅InnoDB引擎中有 buffer 的概念,這個是在用戶空間中,而且在內核空間中也有 OS buffer的概念

      還可以同時調用 flush + fsync, 將緩存直接落盤。 innodb_flush_log_at_trx_commit = 0 就是每秒調用 flush + fsync ,定時器自己維護。 innodb_flush_log_at_trx_commit = 1 就是實時調用 flush + fsync 沒法批處理,性能很低。 innodb_flush_log_at_trx_commit = 2 就是實時flush ,定時 fsync 交給OS維護定時器。 sync_binlog 配置 等于0:表示每次提交事務只write不fsync 等于1:表示每次提交事務都執行fsync 等于n:表示事務在write后,會累積N個事務后才fsync。 show variables like 'sync_binlog'; show variables like 'innodb_flush_log_at_trx_commit'; # 查看 mysql 正在執行的進程 show processlist1.2.3.4.5.6.7.8.9.10.11.12.13.

      InnoDB引擎BufferPool、LogBuffer、OS Buffer、Log files之間的關系。

      Mysql在執行增刪改sql時,InnoDB引擎的執行步驟如下:

      1 執行器拿到需要執行的sql,需要根據更新條件從磁盤中加載需要修改的數據到內存中,也就是存放在buffer pool中。

      2 在修改對應的數據之前,需要將其數據進行備份,也就是將數據放進undo log中,方便在事務回滾時進行操作。

      3 直接在內存中按照sql語句修改對應的值。

      4 修改完后將按照修改后的數據寫redo log buffer。

      5 將 redo log的內容進行寫盤操作,這一步的操作參見innodb_flush_at_trx_commit的配置,一般是先寫入系統的緩存中,然后由操作系統DMA異步操作寫入系統文件中。 flush 操作只是把系統內存中的數據寫入操作系統的緩沖中,數據讀寫一般是由內核線程完成的,這一步是數據從用戶線程轉變成內核線程進行操作,在讀寫文件時,在磁盤文件和內存之間會有多級緩存,用于提高數據交換效率,這里的os cache起到的就是這個作用。

      6 在寫完redo log 后,然后進行bin log寫入操作。

      7 和 redo log的操作類似,也是先寫入os cache再有操作系統刷到磁盤文件中。sync_log的配置如圖所示。一般情況下,數據庫innodb_flush_at_trx_commit 和 sync_log 配置都為1。

      8 在redo log 和 bin log寫完后,就可以進行事務提交。在數據進行寫盤操作時, InnoDB采用兩次寫的方式進行寫數據。

      先寫redo log再寫bin log的原因: 由于mysql 是通過bin log進行復制傳輸的,如果先提交了redo log,還沒有寫bin log時出現了宕機,mysql 實例恢復時根據 redo log進行恢復,就會造成 從庫和主庫之間的數據不一致。

      二進制日志文件的記錄格式為 statement、row 和 mixed,statement 模式就是直接執行sql,如果其中有函數操作(比如數據庫時間設置為 now() )那就會造成數據不準確。row 模式就是同步所有行的數據,如果全表操作修改狀態,那這種模式就不合適了,因此在數據同步時需要根據情況采用 mixed 的混合模式。

      3.2、FreeList、LRU List 和 Flush List

      Free List空閑列表

      記錄所有未被占用的數據頁,按照順序將加載到內存的數據放入buffer pool 中,并刪除對應 Free List 中的節點

      LRU List LRU 數據訪問列表

      將冷熱數據塊連接起來,根據 LRU 算法進行維護。如果加載進內存的數據一次性放入列表頭部,再不確定這批數據的熱度情況下,會造成一部分數據的淘汰,mysql InnoDB 的做法是將數據放置在靠后的位置,如果數據在1s內被訪問了,才能進入鏈表頭部,即數據熱區。

      # 將新加載的數據放置在鏈表的位置 默認為 37 即5/8處, show variables like 'innodb_old_blocks_pct'; # 冷區數據間隔多久訪問才放入鏈表的熱端,默認為1000ms show variables like 'innodb_old_blocks_time';1.2.3.4.

      Flush List 刷新臟頁列表

      記錄內存中修改的數據頁,使用雙向鏈表進行連接,在方便的時候做落盤操作。

      InnoDB 中的 redo log 大小是固定的,是保證事務持久性的,其文件個數也是可以根據需要進行配置,通過循環寫文件的方式來實現的,當 write pos 追趕上 checkpoint 后,這個時候就不能再繼續執行新的命令,需要把check point 往前推進,也就是把redo log 里的內容持久化,騰出空間繼續寫日志。

      數據操作

      redo log buffer 循環寫入

      這里先寫日志再寫磁盤的關鍵點也是一個技術,Write-Ahead Logging(WAL技術)。

      關于 redo log 的配置可以參見如下命令執行查看。

      登錄后復制

      show variables like '%innodb_log%' ------- 執行結果 ------ innodb_log_buffer_size 16777216 innodb_log_checksums ON innodb_log_compressed_pages ON innodb_log_file_size 50331648 innodb_log_files_in_group 2 innodb_log_group_home_dir ./ innodb_log_write_ahead_size 8192 innodb_log_buffer_size 為內存中 redo log buffer 的大小,16777216/1024/1024=16MB innodb_log_file_size 為每個redo log 的大小,50331648/1024/1024=48MB innodb_log_files_in_group 為 redo log 文件組中文件的個數,默認為2個 查看數據庫表狀態 show table status like 'my_table';

      最后

      整理了一張Mysql知識圖譜分享給大家:

      一篇文章讓你搞懂Mysql InnoDB內存結構

      全套的Java面試寶典手冊:性能調優+微服務架構+并發編程+開源框架+分布式”等七大面試專欄,包含Tomcat、JVM、MySQL、SpringCloud、SpringBoot、Dubbo、并發、Spring、SpringMVC、MyBatis、Zookeeper、Ngnix、Kafka、MQ、Redis、MongoDB、memcached等等。

      有需要的朋友可以關注公眾號【程序媛小琬】即可獲取。

      MySQL SQL

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:C語言 | 輸入一些字符,直到輸入“#”為止
      下一篇:MapReduce快速入門系列(11) | MapTask,ReduceTask以及MapReduce運行機制詳解
      相關文章
      亚洲午夜精品在线| 亚洲成AV人在线播放无码| 亚洲欧洲校园自拍都市| 亚洲国产a∨无码中文777| 亚洲精品国产精品乱码视色 | 久久久久亚洲av毛片大| 国产亚洲精品无码专区| 国产午夜亚洲精品国产成人小说| 狠狠亚洲狠狠欧洲2019| 中文亚洲成a人片在线观看| 中文字幕不卡亚洲| 亚洲精品乱码久久久久久| 亚洲成av人片天堂网| 亚洲国产精品线在线观看| 久久久久亚洲av无码专区喷水| 777亚洲精品乱码久久久久久| 亚洲一区精品中文字幕| 亚洲欧洲国产成人精品| 久久精品国产亚洲αv忘忧草 | 伊人久久大香线蕉亚洲| 久久精品国产亚洲综合色| 亚洲成在人天堂在线| 久久99亚洲网美利坚合众国| 亚洲欧洲日产国码在线观看| 亚洲AV无码国产精品色| 亚洲熟妇AV日韩熟妇在线| 国产AV无码专区亚洲AV蜜芽| 亚洲精品动漫人成3d在线| 亚洲欭美日韩颜射在线二| 亚洲AV日韩AV永久无码久久| 亚洲精品美女久久久久| 国产成人精品日本亚洲专| 亚洲a∨无码精品色午夜| 亚洲国产精品一区二区第四页| 国产亚洲视频在线播放| 久久久久亚洲精品影视| 亚洲午夜电影一区二区三区| 亚洲欧美日韩中文二区| 亚洲精品线路一在线观看| 久久精品国产精品亚洲艾| 亚洲国产精品成人综合色在线婷婷|