一、前言咱們都熟悉mysql數據庫服務架構,也清楚 sql 的執行順序,mysql的數據在磁盤和內存中的存儲結構是採用B+樹的數據結構,可是在InnoDB引擎中,數據在內存和磁盤中的展現形式以及怎麼和mysql的服務架構創建聯繫,sql 查詢和 InnoDB 引擎以前的聯繫,可能就不是不清楚了。 mysql 的邏輯架構圖以下所示: 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;
每一個表空間由 段 segment 區 extent 頁 page 組成。頁是數據存儲數據的基本單位,默認大小爲 16kb。 區是由連續頁組成的空間,默認大小爲 1MB。多個區構成表的段。 InnoDB 邏輯存儲結構 sql
在咱們執行sql時,不管是查詢仍是修改,myql 總會把數據從磁盤讀取內內存中,並且在讀取數據時,不會單獨加在一條數據,而是直接加載數據所在的數據頁到內存中,而讀取的方式有兩種,現行預讀方式和隨機預讀方式,默認採用線性預讀方式。數據庫
InnoDB 引擎架構 緩存
線性預讀是以 extent 爲單位,而隨機預讀是以 extent 中的page 爲單位,線性預讀着眼於將下一個extent 數據讀取到 buffer pool 中,而隨機預讀是將當前extent中剩餘的page讀到 buffer pool 中。 若是一個extent 區中被順序讀取得page數量超過必定的數量( innodb_read_ahead_threshold),則直接加載 extent 中剩餘的數據頁。數據結構
插入緩衝(Insert Buffer/Change Buffer)爲了提高插入性能,insert buffer 是 insert buffer 的加強版,insert buffer 只對插入有效,而change buffer對 insert/update/delete 都有效。插入緩存只對非惟一索引和輔助索引有效,對每一次的插入不是寫到索引頁中,而是先判斷插入的非彙集索引頁是否在緩存中,若是在則直接插入,不存在則插入到 insert buffer 中,按照必定的頻率進行合併操做,寫回到磁盤。這樣將多個插入操做合併進一個操做中,目的是爲了減小隨機IO帶來的性能損耗。架構
插入緩存給 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找到該頁的一個副本,將其複製到表空間文件,再應用重作日誌。異步
hash是一種等值查詢,InnoDB 存儲引擎會監控對錶上各個索引頁的查詢,若是觀察到創建hash索引會帶來速度提高,則創建相應的索引,所以稱爲自適應哈希索引(Adaptive Hash Index,AHI)。AHI是經過緩衝池中的B+樹頁構造而來,創建速度比較快,並且不須要對整張表創建哈希索引,只是創建熱點頁的索引。AHI默認是開啓的狀態。ide
爲了提升磁盤的操做性能, 當前的數據庫系統通常採用異步IO(Asynchronous IO,AIO)的方式來處理磁盤操做,InnoDB 存儲引擎也是如此,AIO的優點在於減小SQL查詢須要的時間,另外也能夠進行IO Merge 操做,就是將多個IO合併爲1個IO,這樣就能夠提升IOPS的性能。函數
# 開啓本地 AIO show valiables like 'innodb_use_native_aio';
InnoDB 存儲引擎提供了 Flush Neighbor Page(刷新鄰接頁)的特性,當刷新一個髒頁時,InnoDB 存儲引擎會檢測該區內是否存在其它髒頁,若是存在,則一併進行刷新,這樣作得好處顯而易見,能夠將多個操做合併成一個操做,對於機械硬盤有着明顯的優點,但對於固定硬盤,本事就有較高的IOPS,是否開啓須要根據狀況而定,參數設置以下:
show varables like 'innodb_flush_neighbors'
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 processlist
InnoDB引擎BufferPool、LogBuffer、OS Buffer、Log files 之間的關係。
mysql 在執行增刪改sql時,InnoDB 引擎的執行步驟以下:
先寫redo log 再寫 bin log的緣由: 因爲mysql 是經過 bin log 進行復制傳輸的,若是先提交了 redo log,尚未寫bin log時出現了宕機,mysql 實例恢復時根據 redo log進行恢復,就會形成 從庫和主庫之間的數據不一致。
二進制日誌文件的記錄格式爲 statement、row 和 mixed,statement 模式就是直接執行sql,若是其中有函數操做(好比數據庫時間設置爲 now() )那就會形成數據不許確。row 模式就是同步全部行的數據,若是全表操做修改狀態,那這種模式就不合適了,所以在數據同步時須要根據狀況採用 mixed 的混合模式。
記錄全部未被佔用的數據頁,按照順序將加載到內存的數據放入buffer pool 中,並刪除對應 Free List 中的節點
將冷熱數據塊鏈接起來,根據 LRU 算法進行維護。若是加載進內存的數據一次性放入列表頭部,再不肯定這批數據的熱度狀況下,會形成一部分數據的淘汰,mysql InnoDB 的作法是將數據放置在靠後的位置,若是數據在1s內被訪問了,才能進入鏈表頭部,即數據熱區。
# 將新加載的數據放置在鏈表的位置 默認爲 37 即5/8處, show variables like 'innodb_old_blocks_pct'; # 冷區數據間隔多久訪問才放入鏈表的熱端,默認爲1000ms show variables like 'innodb_old_blocks_time';
記錄內存中修改的數據頁,使用雙向鏈表進行鏈接,在方便的時候作落盤操做。
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';