诸葛io的技术架构图_【总结】MySQL技术内幕二:InnoDB存储引擎技术特性
二、InnoDB存儲引擎
InnoDB是事務安全的MySQL存儲引擎,通常是OLTP應用中核心表的首選存儲引擎。同時,也正是因為InnoDB的存在,才使MySQL數據庫變得更有魅力。
從MySQL 5.5版本開始是默認的表存儲引擎(之前的版本InnoDB存儲引擎僅在Windows下為默認的存儲引擎)。該存儲引擎是第一個完整支持ACID事務的MySQL存儲引擎,其特點是行鎖設計、支持MVCC、支持外鍵、提供一致性非鎖定讀,同時被設計用來最有效地利用以及使用內存和CPU。
InnoDB存儲引擎同MySQL數據庫一樣,在GNU GPL 2下發行。目前應用最廣泛的存儲引擎之一,幾乎所有大型互聯網公司都有使用。
2.1 InnoDB體系架構
InnoDB的簡單體系架構圖:
InnoDB存儲引擎有多個內存塊,可看成一個大的內存池,負責如下工作:
□ 維護所有進程/線程需要訪問的多個內部數據結構。
□ 緩存磁盤上的數據,方便快速地讀取,同時在對磁盤文件的數據修改之前在這里緩存。
□ 重做日志(redo log)緩沖。
……
后臺線程的主要作用是負責刷新內存池中的數據,保證緩沖池中的內存緩存的是最近的數據。此外將已修改的數據文件刷新到磁盤文件,同時保證在數據庫發生異常的情況下InnoDB能恢復到正常運行狀態。
2.1.1 后臺線程
InnoDB存儲引擎是多線程的模型,因此其后臺有多個不同的后臺線程,負責處理不同的任務。1.Master線程
Master線程是一個非常核心的后臺線程,主要負責將緩沖池中的數據異步刷新到磁盤,保證數據的一致性,包括臟頁的刷新、合并插入緩沖(INSERT BUFFER)、UNDO頁的回收等。后面會詳細介紹。http://2.IO線程
在InnoDB存儲引擎中大量使用了AIO(Async IO)來處理寫IO請求,這樣可以極大提高數據庫的性能。而IO Thread的工作主要是負責這些IO請求的回調(call back)處理。
主要有4個IO線程:write、read、insert buffer和log IO線程。
命令show engine innodb status可以查看IO線程的詳細情況,其中0號是insert buffer IO線程,1號是log線程,其他分別是讀寫線程。3.Purge線程
事務被提交后,其所使用的undolog可能不再需要,因此需要Purge線程來回收已經使用并分配的undo頁。在1.1版本之前,purge操作Master線程中完成,從1.1版本開始,purge操作可以獨立到單獨的線程中進行。啟用獨立Purge線程的配置:
命令SHOW VARIABLES LIKE 'innodb_purge_threads'可以查看purge線程的數量。
4.Page Cleaner線程
Page Cleaner線程從1.2.x版本開始引入。其作用是將之前版本中臟頁的刷新操作都放入到單獨的線程中來完成。而其目的是為了減輕原Master Thread的工作及對于用戶查詢線程的阻塞,進一步提高InnoDB存儲引擎的性能。
2.1.2 內存
1.緩沖池
InnoDB存儲引擎是基于磁盤存儲的,并將其中的記錄按照頁的方式進行管理。由于CPU速度與磁盤速度之間的鴻溝,使用緩沖池技術來提高數據庫的整體性能。
對于頁的讀取操作,首先將從磁盤讀到的頁存放在緩沖池中,這個過程稱為將頁“FIX”在緩沖池中。下一次再讀相同的頁時,首先判斷該頁是否在緩沖池中。若在緩沖池中,稱該頁在緩沖池中被命中,直接讀取該頁。否則,讀取磁盤上的頁。
對于頁的修改操作,則首先修改在緩沖池中的頁,然后再以一定的頻率刷新到磁盤上。這里需要注意的是,頁從緩沖池刷新回磁盤的操作并不是在每次頁發生更新時觸發,而是通過一種稱為Checkpoint的機制刷新回磁盤。同樣,這也是為了提高數據庫的整體性能。
緩沖池的大小直接影響著數據庫的整體性能。為了讓數據庫使用更多的內存,強烈建議采用64位的操作系統。
命令SHOW VARIABLES LIKE 'innodb_buffer_pool_size'查看緩沖池大小,同樣通過innodb_buffer_pool_size來設置大小。
緩沖池中緩存的數據頁類型如下圖:
從1.0.x版本開始,允許有多個緩沖池實例。每個頁根據哈希值平均分配到不同緩沖池實例中。這樣的好處是減少數據庫內部的資源競爭,增加數據庫的并發處理能力。可以通過參數innodb_buffer_pool_instances來進行配置,該值默認為1。
2.LRU列表、Free列表和Flush列表
數據庫中的緩沖池是通過LRU(Latest Recent Used,最近最少使用)算法來進行管理的。緩沖池中頁的大小默認為16KB。
InnoDB對傳統的LRU算法做了一些優化,在LRU列表中引入了midpoint位置。新讀取到的頁,并不直接放入到LRU列表的首部,而是放入到LRU列表的midpoint位置。midpoint之后的列表稱為old列表,之前的列表稱為new列表,new列表中的頁都可以理解是熱點數據。默認的midpoint位置是37%(約3/8)。
這樣優化的理由是,像索引或數據的掃描這類操作,需要訪問表中的許多頁,甚至是全部的頁,而這些頁通常并不是熱點數據。如果這些頁放入LRU的首部,很有可能把真正的活躍頁給替換掉,從而影響緩沖池的效率。
InnoDB還引入了另一個參數(innodb_old_blocks_time),用于表示頁讀取到mid位置后需要等待多久才會被加入到LRU列表的熱端。
當數據庫剛啟動時,LRU列表是空的,這時頁都存放在Free列表中。當需要從緩沖池中分頁時,首先從Free列表中查找是否有可用的空閑頁,若有則將該頁從Free列表中刪除,放入到LRU列表中。否則,根據LRU算法,淘汰LRU列表末尾的頁,將該內存空間分配給新的頁。當頁從LRU列表的old部分加入到new部分時,稱此時發生的操作為page made young,而因為innodb_old_blocks_time的設置而導致頁沒有從old部分移動到new部分的操作稱為page not made young。
緩沖池的命中率(Buffer poolhit rate)需要重點關注,通常該值不應該小于95%,若小于95%這種情況,需要觀察是否是由于全表掃描引起的LRU列表被污染的問題。
InnoDB從1.0.x版本開始支持壓縮頁的功能,即將原本16KB的頁壓縮為1KB、2KB、4KB和8KB。對于非16KB的頁,是通過unzip_LRU列表進行管理的,而且對不同壓縮頁大小的頁分別進行管理,通過伙伴算法進行內存的分配。
在LRU列表中的頁被修改后,稱該頁為臟頁(dirty page),即緩沖池中的頁和磁盤上的頁的數據產生了不一致。這時數據庫會通過CHECKPOINT機制將臟頁刷新回磁盤,而Flush列表中的頁即為臟頁列表。臟頁同時存在于LRU列表和Flush列表。LRU列表用來管理緩沖池中頁的可用性,Flush列表用來管理將頁刷新回磁盤,二者互不影響。
LRU/unzip_LRU列表、Free列表、Flush列表的使用情況和運行狀態,都可以通過命令SHOW ENGINE INNODB STATUS查看。3.重做日志緩沖
InnoDB首先將重做日志信息先放入到這個緩沖區,然后按一定頻率刷新到重做日志文件。因為每秒會將重做日志緩沖刷新到日志文件,因此重做日志緩沖不需要設置很大。該值可由配置參數innodb_log_buffer_size控制,默認為8MB。
重做日志緩沖刷新到重做日志文件的三種情況:
□ Master線程定時(每秒)刷新;
□ 事務提交時;
□ 重做日志緩沖區剩余空間小于1/2時。4.額外的內存池
在InnoDB中,對內存的管理是通過內存堆(heap)的方式進行的。在對一些數據結構本身的內存進行分配時,需要從額外的內存池中進行申請,當該區域的內存不夠時,會從緩沖池中進行申請。例如,分配了緩沖池(innodb_buffer_pool),但是每個緩沖池中的幀緩沖(frame buffer)還有對應的緩沖控制對象(buffer control block),這些對象記錄了一些諸如LRU、鎖、等待等信息,而這個對象的內存需要從額外內存池中申請。因此,在申請了很大的InnoDB緩沖池時,也應考慮相應地增加這個值。
2.2 Checkpoint技術
如果每次頁有發生變化,都刷新到磁盤,那么這個開銷會非常大,數據庫的性能也變得非常差。但如果在發生宕機時,緩沖區還有修改的頁沒有刷新到磁盤,那么就會丟數據。為了避免數據丟失,數據庫普遍都采用了Write Ahead Log策略,即當事務提交時,先寫重做日志,再修改頁。當因宕機而導致數據丟失時,可通過重做日志來完成數據的恢復。這也是事務ACID中D(Durability持久性)的要求。
因為緩沖池大小有限,且內存成本大,不可能存下數據庫所有數據。而重做日志理論上可以存下所有數據,但恢復數據的時間會相當長。由此引入了Checkpoint技術來解決如下問題:
□ 縮短數據庫的恢復時間。因為Checkpoint之前的頁都已經刷新回磁盤。故數據庫只需對Checkpoint后的重做日志進行恢復。
□ 緩沖池不夠用時,將臟頁刷新到磁盤。LRU算法會溢出最近最少使用的頁,若此頁為臟頁,那么需要強制執行Checkpoint,將臟頁刷回磁盤。
□ 重做日志不可用時,刷新臟頁。若此時重做日志還需要使用,那么必須強制產生Checkpoint,將緩沖池中的頁至少刷新到當前重做日志的位置。
InnoDB通過LSN(Log Sequence Number)標記版本。LSN是8字節的數字,其單位是字節。每個頁有LSN,重做日志中也有LSN,Checkpoint也有LSN。
Checkpoint發生的時間、條件及臟頁的選擇等都非常復雜。而Checkpoint所做的事情無外乎是將緩沖池中的臟頁刷回到磁盤。InnoDB有兩種Checkpoint:
□ Sharp Checkpoint,發生在數據庫關閉時將所有的臟頁都刷新回磁盤,這是默認的工作方式,即參數innodb_fast_shutdown=1。
□ Fuzzy Checkpoint,在數據庫運行時,只刷新一部分臟頁到磁盤。如果數據庫運行時使用Sharp Checkpoint會影響數據庫的可用性。
如下幾種情況的Fuzzy Checkpoint:
□ Master Thread Checkpoint,差不多以每秒或每十秒的速度從緩沖池的臟頁列表中刷新一定比例的頁回磁盤,這個過程是異步的。
□ FLUSH_LRU_LIST Checkpoint,InnoDB需要保證LRU列表中有約100個空閑頁可供使用,否則會將LRU列表尾端的頁移除。如果這些頁中有臟頁,那么需要進行Checkpoint。
□ Async/Sync Flush Checkpoint,是指重做日志文件不可用的情況(這部分較為復雜,下面單獨介紹)。
□ Dirty Page too much Checkpoint,臟頁的數量太多,會強制進行Checkpoint,也是為了保證緩沖池中有足夠可用的頁。可由參數innodb_max_dirty_pages_pct控制。Async/Sync Flush Checkpoint
重做日志文件不可用時,需要強制將一些頁刷新回磁盤。此時臟頁是從臟頁列表中選取的。下面詳細說明如何選取臟頁。
重做日志的LSN記為redo_lsn,已經刷新回磁盤最新頁的LSN記為checkpoint_lsn,則可定義:
若重做日志文件的大小為1GB,并且定義了兩個重做日志文件,則重做日志文件的總大小為2GB。則:
□ 當checkpoint_age < async_water_mark時,不需要刷新回磁盤;
□當async_water_mark < checkpoint_age < sync_water_mark時觸發AsyncFlush,刷新足夠的臟頁回磁盤,使滿足checkpoint_age < async_water_mark;
□ checkpoint_age > sync_water_mark情況很少發生,除非設置的重做日志文件太小,并且在進行類似LOAD DATA的BULK INSERT操作。此時觸發Sync Flush操作,刷新足夠的臟頁回磁盤,使滿足checkpoint_age<async_water_mark。
1.2.x版本之前,Async Flush Checkpoint會阻塞發現問題的用戶查詢線程,而Sync Flush Checkpoint會阻塞所有的用戶查詢線程。從InnoDB 1.2.x版本開始,這些刷新操作放到了Page Cleaner線程中,不會阻塞用戶查詢線程。
2.3 Master線程的工作方式
InnoDB存儲引擎的主要工作都是在Master線程中完成的。
2.3.1 版本1.0.x之前的Master線程
Master線程具有最高的線程優先級別。其內部由多個循環(loop)組成:
- 主循環(loop)
- 后臺循環(backgroup loop)
- 刷新循環(flush loop)
- 暫停循環(suspend loop)
Master線程會根據數據庫運行的狀態在這幾個loop之間進行切換。Loop被稱為主循環,因為大多數的操作是在這個循環中,其中有兩大部分的操作:每秒的操作和每10秒的操作。這里的每秒或每10稱不精確,負載大時會有延遲的情況。
主循環每秒的操作包括:
□ 日志緩沖刷新到磁盤,即使這個事務還沒有提交(總是)。這就是為什么再大的事務提交(commit)的時間也是很短的。
□ 合并插入緩沖(可能)。一秒內發生的IO次數小于5次,可以執行合并插入緩沖的操作。
□ 至多刷新100個InnoDB的緩沖池中的臟頁到磁盤(可能)。就是Sync Flush Checkpoint操作,以及相對應的觸發條件下執行。
□ 如果當前沒有用戶活動,則切換到background loop(可能)。
主循環每10秒的操作包括:
□ 刷新100個臟頁到磁盤(可能),過去10秒之內磁盤的IO操作小于200次時執行;
□ 合并至多5個插入緩沖(總是);
□ 將日志緩沖刷新到磁盤(總是);
□ 刪除無用的undo頁(總是),即執行full purge操作,每次最多嘗試回收20個undo頁;
□ 刷新100個或者10個臟頁到磁盤(總是)。如果臟頁超過70%,則刷新100個臟頁到磁盤,如果臟頁小于70%,則只刷新10%的臟頁到磁盤。
如果當前沒有用戶活動(數據庫空閑時)或者數據庫關閉(shutdown),就會切換到background循環,執行以下操作:
□ 刪除無用的Undo頁(總是);
□ 合并20個插入緩沖(總是);
□ 跳回到主循環(總是);
□ 不斷刷新100個頁直到符合條件(可能,跳轉到flush循環中完成)。
若flush循環也沒什么事情可做,會切換到suspend循環,將Master線程掛起,等待事件的發生。
2.3.2 版本1.2.x之前的Master
版本1.0.x之前的Master線程實現,對IO有限制,在緩沖池向磁盤刷新時都做了一定的硬編碼(hard coding)。當固態磁盤(SSD)出現時,這很大程度上限制了InnoDB對磁盤IO的性能,尤其是寫入性能。
無論什么情況,InnoDB最大只會刷新100個臟頁到磁盤,合并20個插入緩沖。對于寫密集的應用,很有可能超過這些值,Master線程可能一直處于忙碌狀態。即使處理得過來,當發生宕機需要恢復時,由于很多數據沒有刷新回磁盤,恢復時間也可能比較長,尤其是insert buffer。
因此從1.0.x版本開始提供了參數innodb_io_capacity,用來表示磁盤IO的吞吐量,默認值為200。對于刷新到磁盤頁的數量,會按照innodb_io_capacity的百分比來進行控制。規則如下:
□ 在合并插入緩沖時,合并插入緩沖的數量為innodb_io_capacity值的5%;
□ 在從緩沖區刷新臟頁時,刷新臟頁的數量為innodb_io_capacity。
當使用SSD或RAID等擁有更高IO速度時,可以根據磁盤IO的吞吐量來調高innodb_io_capacity的值。
另外,參數innodb_max_dirty_pages_pct默認值的問題,從默認值為90改成了75。這個值如果太大,刷新臟頁速度太慢,太小會增加系統負擔。默認75既可以加快刷新臟頁的頻率,又能保證了磁盤IO的負載。
這個版本還引入了其他參數:
- innodb_adaptive_flushing(自適應地刷新),該值影響每秒刷新臟頁的數量。
- innodb_purge_batch_size,控制每次full purge回收的undo頁的數量,默認值為20。
很多測試都顯示,1.0.x版本在性能方面有了極大的提高,其實和前面提到的改動密不可分。
2.3.3 版本1.2.x的Master線程
版本1.2.x中再次對Master線程進行了優化。其中srv_master_do_idle_tasks()就是之前版本中每10秒的操作,srv_master_do_active_tasks()就是之前每秒中的操作。同時對于刷新臟頁的操作,從Master線程分離到一個單獨的Page Cleaner線程,從而減輕了Master線程的工作,同時進一步提高了系統的并發性。
2.4 InnoDB關鍵特性
InnoDB的關鍵特性包括:
□ 插入緩沖(Insert Buffer)
□ 雙寫(Double Write)
□ 自適應哈希索引(Adaptive Hash Index)
□ 異步IO(Async IO)
□ 刷新鄰接頁(Flush Neighbor Page)
這些特性為InnoDB存儲引擎帶來更好的性能以及更高的可靠性。
2.4.1 插入緩沖
1.Insert Buffer
Insert Buffer可能是InnoDB存儲引擎關鍵特性中最令人激動與興奮的一個功能。但這個名字可能會讓人認為是緩沖池中的組成部分。其實不是,Insert Buffer和數據頁一樣,也是物理頁的一個組成部分。
在InnoDB中,主鍵是行唯一標識。通常行記錄的插入順序就是主鍵遞增的順序。因此,插入聚集索引(Primary Key)一般是順序的,不需要磁盤的隨機讀取,插入速度非常快。
像主鍵類似UUID之類的,以及非聚集索引葉子節點的插入不再是順序,此時插入性能會下降。這是由B+樹的特性決定的,非聚集索引插入本身就是離散的。如下面表中的b:
CREATE TABLE t( a INT AUTO_INCREMENT, b VARCHAR(30), PRIMARY KEY(a), KEY(b) );
在某些情況下,輔助索引(secondary index)的插入依然是順序的,或者說比較順序,如時間字段。
InnoDB存儲引擎開創性地設計了Insert Buffer,對于非聚集索引的插入或更新操作,不是每一次直接插入到索引頁中,而是先判斷插入的非聚集索引頁是否在緩沖池中,若在,則直接插入;若不在,則先放入到一個Insert Buffer對象中。然后再以一定的頻率和情況進行Insert Buffer和輔助索引頁子節點的merge(合并)操作,這時通常能將多個插入合并到一個操作中(因為在一個索引頁中),這就大大提高了對于非聚集索引插入的性能。
Insert Buffer的使用需要同時滿足以下兩個條件:
□ 索引是輔助索引(secondary index);
□ 索引不是唯一(unique)的。
需要考慮一種情況,大量的插入操作都涉及了不唯一的非聚集索引,也就是使用了InsertBuffer。若此時發生了宕機,勢必有大量的InsertBuffer并沒有合并到實際的非聚集索引中去。這時恢復可能需要很長的時間,在極端情況下甚至需要幾個小時。
輔助索引不能是唯一的,因為在插入緩沖時,數據庫并不去查找索引頁來判斷插入的記錄的唯一性。如果去查找肯定又會有離散讀取的情況發生,從而導致Insert Buffer失去了意義。
目前Insert Buffer存在一個問題是:在寫密集的情況下,插入緩沖會占用過多的緩沖池內存,默認最大可以占用到1/2的緩沖池內存。這對于其他的操作可能會帶來影響。2.Change Buffer
從1.0.x版本開始引入了Change Buffer,可視為InsertBuffer的升級。從這個版本開始,可以對DML操作——INSERT、DELETE、UPDATE都進行緩沖,他們分別是:Insert Buffer、Delete Buffer、Purge Buffer。Change Buffer適用的對象依然是非唯一的輔助索引。
UPDATE操作可能分為兩個過程:
□ 將記錄標記為已刪除。緩沖在Delete Buffer中;
□ 真正將記錄刪除。緩沖在Purge Buffer中。
參數innodb_change_buffering,用來開啟各種Buffer的選項。
參數innodb_change_buffer_max_size,用來控制Change Buffer最大使用內存的數量,默認為25,最大有效值為50。3. Insert Buffer的內部實現
Insert Buffer的數據結構是一棵全局的B+樹。存放在共享表空間中,默認也就是ibdata1中。
非葉節點存放的是查詢的search key(鍵值),其構造如下:
search key共占用9個字節,其中space表示表空間id,每個表有一個唯一的space id,占用4字節。marker占用1字節,它是用來兼容老版本的Insert Buffer。offset表示頁所在的偏移量,占用4字節。
對于插入到Insert Buffer B+樹葉子節點的記錄,并不是直接將待插入的記錄插入,而是需要根據如下的規則進行構造:
metadata占用4字節,內容如下:
IBUF_REC_OFFSET_COUNT是用來排序每個記錄進入Insert Buffer的順序,通過這個順序回放(replay)才能得到記錄的正確值。
metadata之后就是實際插入記錄的各個字段了。因此較之原插入記錄,Insert Buffer B+樹的葉子節點記錄需要額外13字節的開銷。
為了保證Merge InsertBuffer頁必須成功,還需要一個特殊的頁(Insert BufferBitmap)來標記每個輔助索引頁(space,page_no)的可用空間。
每個Insert Buffer Bitmap頁用來追蹤16384個輔助索引頁,也就是256個區(Extent)。每個輔助索引頁在Insert Buffer Bitmap頁中占用4位(bit),存儲的信息如下:
4. Merge Insert Buffer
Insert Buffer中的記錄何時合并(merge)到真正的輔助索引中呢?概括地說,Merge Insert Buffer的操作可能發生在以下幾種情況下:
□ 輔助索引頁被讀取到緩沖池時;
□ Insert Buffer Bitmap頁追蹤到該輔助索引頁已無可用空間時;
□ Master Thread。
第一種情況,需要根據Insert Buffer Bitmap頁確認該輔助索引頁是否在Insert Buffer中有記錄。若有,則將InsertBuffer中該頁的記錄插入到該輔助索引頁中。該頁多次的記錄操作通過一次操作合并到了原有的輔助索引頁中,性能會有大幅提高。
第二種情況,若插入輔助索引記錄時檢測到插入記錄后可用空間會小于1/32頁,則會強制進行一個合并操作。
第三種情況,Master線程每秒或每10秒會進行一次Merge Insert Buffer的操作,不同之處在于每次進行merge操作的頁的數量不同。
2.4.2 雙寫
Doublewrite(雙寫)帶給InnoDB存儲引擎的是數據頁的可靠性。
當發生數據庫宕機時,可能某個頁只寫了一部分,比如16KB的頁,只寫了前4KB,之后就發生了宕機,這種情況被稱為部分寫失效(partial page write)。在未使用doublewrite技術前,部分寫失效會導致數據丟失。
大家先想到的就是通過重做日志進行恢復,但因為重做日志中記錄的是對頁的物理操作,發生部分寫失效時,如果這個頁本身損壞,再對其進行重做是沒有意義的。
在應用(apply)重做日志前,用戶需要一個頁的副本,當寫入失效發生時,先通過頁的副本來還原該頁,再進行重做,這就是doublewrite。在InnoDB中doublewrite的體系架構如下圖:
doublewrite由兩部分組成:
- 內存中的doublewrite buffer,大小為2MB;
- 物理磁盤上共享表空間中連續的128個頁,即2個區(extent),大小同樣為2MB。
在對緩沖池的臟頁進行刷新時,并不直接寫磁盤,而是會通過memcpy函數將臟頁先復制到內存中的doublewrite buffer,之后通過doublewrite buffer再分兩次,每次1MB順序地寫入共享表空間的物理磁盤上,然后馬上調用fsync函數,同步磁盤,避免緩沖寫帶來的問題。在這個過程中,因為doublewrite頁是連續的,順序寫的,開銷并不大。在完成doublewrite頁的寫入后,再將doublewrite buffer中的頁寫入各個表空間文件中(為什么不是寫Insert Buffer?),此時的寫入則是離散的。
可通過SHOW GLOBAL STATUS LIKE 'innodb_dblwr'查看doublewrite的運行情況。如果發現系統在高峰時的Innodb_dblwr_pages_written : Innodb_dblwr_writes遠小于64∶1,說明系統寫入壓力并不高。
參數skip_innodb_doublewrite可以禁止使用doublewrite功能。主服務不建議啟用這個參數,從服務可以啟用來提供較高的性能。
有些文件系統提供了部分寫失效的防范機制,如ZFS文件系統,就不要啟用doublewrite了。
2.4.3 自適應哈希索引
哈希(hash)的查找非常快,時間復雜度為O(1)。而B+樹的查找復雜度取決于B+樹的高度,O(log n),在生產環境中,B+樹的高度一般為3~4層,故需要3~4次的查詢。
InnoDB會監控對表上各索引頁的查詢。如果觀察到建立哈希索引提升速度,則建立哈希索引,稱之為自適應哈希索引(Adaptive Hash Index,AHI)。AHI是通過緩沖池的B+樹頁構造而來,因此建立的速度很快,而且不需要對整張表構建哈希索引。InnoDB會自動根據訪問的頻率和模式來自動地為熱點頁建立哈希索引。
AHI如下的要求:
□ 對這個頁的連續訪問模式必須是一樣的,即where條件一樣;
□ 以該模式訪問了100次;
□ 頁通過該模式訪問了N次,其中N=頁中記錄*1/16。
官方文檔顯示,啟用AHI后,讀取和寫入速度可以提高2倍,輔助索引的連接操作性能可以提高5倍。AHI是非常好的優化模式,其設計思想是數據庫自優化的(self-tuning)。
哈希索引只能用來搜索等值的查詢,如WHERE index_col='xxx'。而對于如范圍查找,不能使用哈希索引。
命令SHOW ENGINE INNODB STATUS可以查看AHI的使用狀況。
參數innodb_adaptive_hash_index用來禁用或啟動此特性,默認AHI為開啟狀態。
2.4.4 異步IO
為了提高磁盤操作性能,數據庫系統都采用異步IO(Asynchronous IO,AIO)的方式來處理磁盤操作,InnoDB也是。
異步IO就是,在發出一個IO請求后立即再發出另一個IO請求,當全部IO請求發送完畢后,等待所有IO操作的完成。
AIO的另一個優勢是可以進行IO Merge操作,多個IO合并為1個IO,可以提高IOPS的性能。
在1.1.x之前,AIO的實現是通過代碼來模擬實現。而從1.1.x開始(InnoDB Plugin不支持),提供了內核級別AIO的支持,稱為Native AIO。Native AIO需要操作系統提供支持。Windows系統和Linux系統都提供Native AIO支持,而Mac OSX系統則未提供。
參數innodb_use_native_aio用來控制是否啟用Native AIO,在Linux下,默認值為ON。
官方的測試顯示,啟用Native AIO,恢復速度可以提高75%。
2.4.5 刷新鄰接頁
InnoDB還提供了Flush Neighbor Page(刷新鄰接頁)的特性。原理是:當刷新一個臟頁時,會檢測該頁所在區(extent)的所有頁,如果是臟頁,那么一起進行刷新。
這樣做的好處顯而易見。對傳統機械磁盤有顯著的優勢。但是需要考慮到下面兩個問題:
□ 是不是可能將不怎么臟的頁進行了寫入,而該頁之后又會很快變成臟頁?
□ 固態硬盤有著較高的IOPS,是否還需要這個特性?
為此,從1.2.x版本開始提供了參數innodb_flush_neighbors,用來控制是否啟用該特性。對于傳統機械硬盤建議啟用該特性,而對于固態硬盤有著超高IOPS性能的磁盤,則建議關閉。
總結
以上是生活随笔為你收集整理的诸葛io的技术架构图_【总结】MySQL技术内幕二:InnoDB存储引擎技术特性的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 暂列金结算时怎么处理(暂列金是什么意思)
- 下一篇: 有左撇子开的车吗?比如左手拧油门那种