第二章 InnoDB 存儲引擎

  1. InnoDB 體系架構

 

  1.1 後臺線程

  後臺線程的主要做用是負責刷新內存池中的數據,保證緩衝池中緩存的是最近的數據。此外,還會將已修改的數據文件刷新到磁盤文件,同時保證在數據庫發生異常的狀況下InnoDB能恢復到正常運行的狀態。前端

  InnoDB存儲引擎是多線程的模型,其後臺有多個不一樣的後臺線程,負責處理不一樣的任務mysql

  1) Master threadios

  負責將緩衝池中的數據異步刷新到磁盤,保證數據的一致性,包括髒頁(因爲磁盤的讀寫速度遠趕不上內核的讀寫速度,系統把讀寫頻繁的數據放在內存中,稱爲高速緩存。當進程修改了高速緩存中的數據時,該頁被內核標爲髒頁)的刷新、合併插入緩衝、undo頁的回收等。算法

  Master thread具備最高線程優先級。內部由多個循環(loop)組成:主循環(loop)、後臺循環(background loop)、刷新循環(flush loop)、暫停循環(suspend loop)。Master Thread會根據數據庫運行狀態在不一樣循環之間切換。其操做僞碼以下:sql

void master_thread(){
    goto loop;
loop:
    for(int i = 0; i<10; i++){
    thread_sleep(1)  // sleep 1 second
    do log buffer flush to disk    // 日誌緩衝刷新到磁盤
    if (last_one_second_ios < 5% innodb_io_capacity(磁盤吞吐量))   // 當前一秒發生的IO次數
        do merge 5% innodb_io_capacity insert buffer              // 合併插入緩衝
    if (buf_get_modified_ratio_pct (緩衝池中髒頁比例) > innodb_max_dirty_pages_pct)  
        do buffer pool flush 100% innodb_io_capacity dirty page
    else if enable adaptive flush
        do buffer pool flush desired amount dirty page
    if ( no user activity )
        goto background loop
    }
    
    if (last_ten_second_ios < innodb_io_capacity)
        do buffer pool flush 100% innodb_io_capacity dirty page
    do merge 5% innodb_io_capacity insert buffer
    do log buffer flush to disk
    do full purge                                                 // 刪除無用的undo頁 對錶進行update delete操做時,原先的行被標記爲刪除
                                                                  // 因爲一致性讀的關係須要保留這些行版本信息。但在full purge時,會判斷
                                                                  // 這些標記爲刪除的行是否能夠刪除(有時,會有查詢操做還須要讀以前版本的
                                                                  // undo信息)
    if ( buf_get_modified_ratio_pct > 70% )
        do buffer pool flush 100% innodb_io_capacity dirty page
    else 
        do buffer pool flush 10% innodb_io_capacity dirty page
    goto loop
    
    background loop:
    do full purge
    do merge 100% innodb_io_capacity insert buffer
    if not idle:
    goto loop:
    else:
        goto flush loop
        
    flush loop:
    do buffer pool flush 100% innodb_io_capacity dirty page
    if ( buf_get_modified_ratio_pct>innodb_max_dirty_pages_pct)
        goto flush loop
    goto suspend loop
    
    suspend loop:
    suspend_thread()
    waiting event
    goto loop;
    }

  2) I/O thread數據庫

  InnoDB存儲引擎中大量使用AIO(Async IO)來處理寫IO請求,以提升數據庫性能緩存

  3) Purge thread多線程

  事務提交後,所使用的undo log(用於事務回滾,將數據庫恢復到修改前的樣子,在第七章 事務會詳細介紹)可能不在須要了,所以須要Purge Thread來回收這些undo 頁架構

  4) Page Cleaner thread併發

  爲了減輕Master Thread 的工做以及用戶查詢線程的阻塞,1,2.x版本將髒頁的刷新操做放到單獨的線程(Page Cleaner thread)來完成

  1.2 內存

  InnoDB內存數據對象

      

  1) 緩衝池

  InnoDB存儲引擎是基於磁盤存儲的,並將其中的記錄按照頁的方式進行管理,即基於磁盤的數據庫系統。

  一般將部分或所有的頁備份到緩衝池(一塊內存區域)中,在讀取頁時,先判斷緩衝池中是否有須要的頁,若是有,稱爲緩存命中;不然,從磁盤中讀取相應的頁。

  而對於數據庫中頁的修改,首先修改緩衝池中的頁,而後再經過checkpoint技術(本章第二節介紹)將頁刷新會磁盤中。

  2) 緩衝池內存管理

  LRU list

  數據庫中的緩衝池經過LRU(least recently used)算法進行管理。最頻繁使用的的頁在LRU列表的前端,最少使用的頁在尾端。當緩衝池不能存放新讀到的頁時,將首先釋放LRU列表中尾端的頁。InnoDB也對LRU算法作了改進,新讀取到的頁放到了LRU列表的midpoint位置(距列表尾端3/8的位置)。這是爲了防止進行諸如索引或數據掃描等,須要訪問表中不少頁的操做,在插入到首部時,將本來的熱點數據擠出LRU列表,這種操做訪問的數據一般僅在本次查詢中用到,並不是熱點數據。在下次查詢時,InnoDB須要再次訪問磁盤將熱點數據拷貝回緩衝池。

  innodb_old_blocks_time用於表示頁讀取到mid位置後,須要多久纔會被加入到LRU列表的熱端。當頁從LRU列表的old部分(midpoint以後的列表)加入到new部分時,稱爲page made young.

  Free list

  數據庫剛啓動時,LRU列表是空的。這時頁都存在Free list中。當須要從緩衝池中分頁時,首先從Free list查找是否有可用的空閒頁,如有則將該頁從free list中刪除,並放入到LRU list中。不然,根據LRU算法,淘汰LRU列表末尾的頁,將該內存空間分配給新頁。

  Flush list

  flush list 用來管理將髒頁刷新回磁盤。

  1.3 重作日誌緩衝

  存儲引擎通常先將重作日誌信息放到重作日誌緩衝(redo log buffer),而後按必定頻率刷新到重作日誌文件:

  • Master thread 每一秒將重作日誌緩衝刷新到重作日誌文件
  • 每一個事務提交時會將重作日誌緩衝刷新到重作日誌文件
  • 當重作日誌緩衝池剩餘空間小於1/2時,將重作日誌緩衝刷新到重作日誌文件

  1.4 額外的內存池

  緩衝池的幀緩衝還有對應的緩衝控制對象(記錄了LRU、鎖、等待等信息)存儲在額外內存池中。在申請了很大的InnoDB緩衝池時,也應考慮相應增長額外內存池的大小

  2. Checkpoint 技術

  Checkpoint 技術解決如下幾個問題:

  • 縮短數據庫恢復時間
  • 緩衝池不夠用時,將髒頁刷新到磁盤
  • 重作日誌不可用時,刷新髒頁(事務數據庫廣泛採用Write Ahead Log策略,先寫重作日誌,再修改頁,避免宕機形成的數據丟失)

  1) InnoDB存儲引擎確保LRU list有默認1024個頁可用,若是沒有,根據LRU算法會溢出最近最少使用的頁,若此頁爲髒頁,會執行checkpoint,將髒頁刷新回磁盤

  2) 重作日誌不可用的狀況出現是由於當前事務數據庫系統對重作日誌的設計都是循環使用的,而不是容許其無限增大。當重作日誌不可用時須要強制將一些頁刷新回磁盤

  3) Master thread 中發生的checkpoint,每秒或每十秒從緩衝池中的髒頁列表刷新必定比例的頁回磁盤

  4) 緩衝池中髒頁佔75%時,強制進行checkpoint 

  3. InnoDB關鍵特性

  3.1 插入緩衝 (insert buffer / change buffer(MySQL 5.5以後版本的叫法))

  該部份內容亦參考了博客 https://blog.csdn.net/qq_36652619/article/details/89460786    http://mysql.taobao.org/monthly/2015/07/01/

  插入彙集索引(clustered index)通常是順序的,不須要磁盤的隨機讀取。二級索引(secondary index,亦稱非彙集索引)一般不是惟一的,順序相對隨機,刪除和更新可能會影響不在索引樹中相鄰的二級索引頁。

  對於非彙集索引的插入或更新操做,不是每一次直接插入到索引頁中,而是先判斷插入的非彙集索引頁是否在緩衝池中,若在則直接插入;若不在,則先放入到一個Insert buffer對象中,以此減小二級索引的隨機IO,並達到操做合併的效果。Merge Insert/Change Buffer可能發生在如下幾種狀況:

  • 輔助索引頁被讀取到緩衝池中 (執行SELECT操做將頁緩衝到緩衝池中,須要檢查該輔助索引頁是否有記錄存放在Insert Buffer B+樹中,有則一次性更新操做到輔助索引頁中)
  • Insert Buffer Bitmap 頁追蹤到該輔助索引頁已無可用空間(至少有1/32的可用空間)
  • Master Thread (1.1 中master thread介紹過,每秒或每十秒進行一次merge insert buffer) 

  change buffer是一顆B+樹,ibuf btree經過三列(space id(每張表有惟一的ID), page no(表中頁的偏移量), counter)做爲主鍵來惟一決定一條記錄,其中counter是一個遞增值,目的是爲了維持不一樣操做的有序性,例如能夠經過counter來保證在merge時執行以下序列時的循序和用戶操做順序是一致的:INSERT x, DELETE-MARK x, INSERT x

  

  3.2 兩次寫

  若是說Insert Buffer帶給InnoDB存儲引擎性能上的提高,doublewrite帶給InnoDB存儲引擎的是數據頁的可靠性。

  但數據庫宕機時,可能InnoDB存儲引擎正在寫入某個頁到磁盤中,好比,16KB的頁,只寫了前4頁,以後就發生了宕機,這種狀況稱爲部分寫失效。

   

  由上圖可知,doublewrite由兩部分組成,一部分是內存中的doublewrite buffer,大小爲2MB,另外一部分是物理磁盤上共享表空間中連續的128個頁,即兩個區,大小一樣爲2MB。在對緩衝池的髒頁進行刷新時,並不直接寫磁盤,而是會經過memcpy函數將髒頁先複製到內存中的doublewrite buffer,以後經過doublewrite buffer分兩次,每次1MB順序寫入共享表空間的物理磁盤上(doublewrite頁是連續的,因此寫開銷並非很大),在完成doublewrite頁的寫入後,再將doublewrite buffer中的頁寫入各個表空間文件中。若是操做系統在將頁寫入磁盤的過程當中發生了崩潰,在恢復的過程當中,InnoDB存儲引擎能夠從共享表空間中的doublewrite中找到該頁的一個副本,將其複製到表空間文件,再應用重作日誌。

  3.3 自適應哈希索引

  B+ 樹中,哈希查找的次數取決於B+樹的高度。InnoDB存儲引擎會監控表上各索引頁的查詢,並根據如下兩種狀況創建自適應哈希索引(adaptive hash index):

  • 以某一模式連續訪問了100次
  • 頁經過該模式訪問了N次,N=頁中記錄/16

  須要注意的是,哈希索引只能用來搜索等值查詢,如 SELECT * FROM table WHERE index_col = XXX,不能用於範圍查詢。

  3.4 異步IO

  在同步I/O狀況下,查詢線程將I/O請求放入隊列,innodb後臺線程會遍歷請求隊列,每次處理一個請求。並行處理的請求個數受到後臺線程的數量控制(參數innodb_read_io_threads)。AIO狀況下,查詢線程直接將I/O請求分發給操做系統,從而避免的後臺線程數量對併發數的控制。innodb後臺線程只須要等待操做系統對IO請求的處理反饋信息。另外一個優勢是IO Merge,即將多個IO合併爲一個IO。例如:用戶訪問的頁(space,page_no)爲:(8,6)、(8, 7)、(8,8),每一個頁大小爲16KB,同步IO須要進行3次IO操做。而AIO會判斷這三個頁是連續的。所以發送一個IO請求,從(8,6)開始,讀取48KB的頁。

  Read ahead(預讀),髒頁的刷新(磁盤的寫入操做)都是經過AIO完成的。

  3.5 刷新鄰接頁

  當刷新一個髒頁時,InnoDB存儲引擎會檢測該頁所在區的全部頁,若是是髒頁,那麼經過AIO一塊兒進行刷新。但也存在兩個問題:

  • 有些刷新到磁盤的髒頁,可能很快又變成髒頁
  • 固態磁盤有着較高的IOPS(input/output operations per second),不太須要該特性(通常機械磁盤建議開啓該特性, innodb_flush_neighbors)
相關文章
相關標籤/搜索