第一章 MySQL 體系架構和存儲引擎
mysql是數據庫也是數據庫實例
mysql ?是一個單進程多線程架構的數據庫 ?daemon 守護進程
當啟動實例時,MySQL數據庫會去讀取配置文件,根據配置文件的參數來啟動數據庫實例。
用以下命令可以查看當Mysql 數據庫實例啟動時,會在哪些位置查找配置文件。
mysql —help | grep my.cnf
mysql由一下幾部分組成:
連接池組件
管理服務和工具組件
sql接口組件
查詢分析器組件
優化器組件
緩沖(cache)組件
插件式存儲引擎
物理文件
最大的特點就是其插件式的表存儲引擎。存儲引擎是基于表的,而不是數據庫。
mysql獨有的插件式體系架構,存儲引擎的是mysql區別于其他數據庫的一個重要的特性。存儲引擎的好處是,每個存儲引擎都有各自的特點,能夠根據具體的應用建立不同存儲引擎表。
MySql 存儲引擎的改進http:/code.google.com.p/mysql-heap-dynamic-rows/
MySQL 數據庫開源特性,存儲引擎可以分為MySql官方存儲引擎和第三方存儲引擎。Inno存儲引擎,是mysql 數據庫OLTP(online transaction Processing在線事務處理)應用中使用最廣泛的存儲引擎。
1.3.1 ?InnoDB存儲引擎
InnoDB存儲引擎支持事務,其設計目標主要面向在線事務OLTP的應用。其特點是行級鎖設計、支持外鍵,并支持類似于Oracle的非鎖定讀,從5.5.8開始InnoDB存儲引擎是默認的存儲引擎。
InnoDB通過使用多版本并發控制MVCC來獲得高并發性,并且實現了SQL標準的四種隔離級別,默認的為REPEATABLE級別,同時使用一種被稱為next-key locking的策略來避免幻讀(phantom)現象的產生。除此之外,InnoDB存儲引擎還提供了插入緩沖(Insert buffer)、二次寫(double write)、自適應哈希索引(adaptive hash index)、預讀(read ahead)等高性能和高可用的功能。
對于表中的數據存儲,InnoDB存儲引擎采用聚集(cluster)的方式,因此每張表的存儲都是按主鍵的順序進行存放。如果沒有顯式地在表定義時指定主鍵,InnoDB存儲引擎會為每一行生成一個6個字節的ROWID ,并以此作為主鍵。
1.3.2 MyISAM存儲引擎
MyISAM存儲引擎不支持事務、表鎖設計,支持全文索引,主要面向一些OLAP數據庫應用。
1.3.3 NDB 存儲引擎是一個集群存儲引擎,類似于oracle的RAC集群,不過Oracle,Oracle RAC share everything 架構不同是,器結構是share nothing的集群架構,因此能夠提供更高的可用性。NDB的特點是數據全部放在內存中,因此主鍵查詢(primary key lookups)的速度極快,并且通過添加NDB數據存儲節點(data Node)可以線性地提高數據庫性能,是高可用、高性能的集群系統。
關于NDB存儲引擎有一個問題值得注意,那就是NDB存儲引擎的鏈接操作JOIN時在MySQL數據庫層完成的,而不是在存儲引擎層完成的,這意味著復雜的鏈接操作需要巨大的網絡開銷,因此查詢速度很慢,如果解決了這個問題,NDB存儲引擎的市場應該是非常大的。
1.3.4 Memory存儲引擎
Memory存儲引擎(之前稱之為HEAP存儲引擎)將表中的數據存放在內存中,如果數據庫重啟或者發生奔潰,表中的數據將消失。它非常適合用于存儲臨時數據的臨時表,以及數據倉庫中的維度表。Memory存儲引擎默認使用hash索引,而不是我們熟悉的B+樹索引。
Memory存儲引擎速度快,但只支持表鎖,并發性能較差,并且不支持TEXT和BLOB列類型,最重要的是,村粗變長字段varchar時是按照定常字段方式進行的,因此會浪費內存。
1.3.5 Archive 存儲引擎
Archve 存儲引擎只支持INSERT 和SELECT操作,從MySQL開始支持索引。
1.3.6 Federated 存儲引擎
1.3.7 Maria 存儲引擎
Maria存儲引擎是新開發的引擎,設計目標主要是用來取代原有的MyISAM存儲殷勤,從而成為MySQL的默認存儲引擎。Maria存儲引擎的開發者是MySQL的創始人之一。Maria存儲引擎的特點是:支持緩存數據和索引文件,應用了行鎖設計,提供了MVCC功能,支持事務和非事務安全的選項,以及更好的BLOB字符類型的處理性能。
總結: MySQL 的InnoDB 存儲引擎的效率在OLTP中效率更好,對于ETL MyISAM存儲引擎更具有優勢。
當數據量大于1000萬時MySQL的性能會急劇下降嗎?不!,MySQL是數據庫,不是文件,隨著數據行的增加,性能當然會有所下降,但是這些下降不是線性的,如果用戶選擇了正確的存儲引擎,以及正確的配置,再多的數據量MySQL也能承受。InnoDB上存儲超過1TB的數據,還有一些其他網站使用InnoDB存儲引擎,處理插入/更新的操作平均800次/s
衡量標準:
存儲容量的限制、事務的支持、鎖的粒度、MVCC支持、支持的索引、備份和復制等
各大存儲引擎
第2章 InnoDB存儲引擎
InnoDB是事務安全的MySQL存儲引擎,設計上采用了類似于Oracle數據庫的架構。通常來說,InnoDB存儲引擎是OLTP應用中核心代表的首選存儲引擎。同時,也正式因為InnoDB的存在,才使得MySQL數據庫變得有魅力。
2.1 InnoDB存儲引擎概述
最早由Innobase Oy公司開發,其特點行鎖設計、支持MVCC、支持外鍵、提供一致性非鎖定讀,同時被設計用來最有效地利用以及內存和CPU。
2.3 InnoDB體系架構
Inno存儲引擎有多快內存塊,可以認為這些內存組成了一個大的內存池,負責如下工作:
1、維護所有進程/線程需要訪問的多個內部數據結構
2、緩存磁盤上的數據,方便快速地讀取,同時在對磁盤文件的數據修改之前在這里緩存
3、重做日志redo緩沖
后臺線程的主要負責刷新內存池中的數據,保證緩沖池的內存緩存的是最近的數據,此外將已經修改的數據文件刷新到磁盤文件,同時保證在數據庫發生的異常情況下,Inno能恢復到正常的運行狀態。
2.3.1 后臺線程
InnoDB存儲引擎是多線程的模型,因此其后臺有多個不同的后臺線程,負責處理不同的任務。
1、Master Thread
Master Thread 是一個非常核心的后臺線程,主要負責將緩沖池中的數據異步刷新到磁盤,保證數據的一致性,包括臟頁的刷新、合并插入緩沖(INSERT BUFFER)、UNDO頁的回收等。
2、IO Thread
在InnoDB存儲引擎中大量使用了AIO(Async IO)來處理寫IO請求,這樣可以極大提高數據庫的性能。而IO Thread 的工作主要負責這些IO請求的回調(call back)處理。
3、Purge(凈化) Thread
事務被提交后,其所使用的undolog可能不在需要,因此需要PurgeThread來回收并分配的undo頁面。Purge操作可以獨立到單獨的線程中,以此來減輕Master Thread的工作,從而提高CPU的使用率以及提升存儲引擎的性能。用戶可以再MySQL 數據庫的配置文件中添加如下命令來啟動獨立的Purge ?Thread
innodb_Purge_threads=1
2.3.2內存
1、緩沖池
InnoDB 存儲引擎是基于磁盤存儲的,并將其中的記錄按照頁的方式進行管理。因此可將視為基于磁盤的數據庫系統。
緩沖池簡單說就是一塊內存區域,通過內存的速度來彌補磁盤速度較慢對數據庫性能的影響,首先將從磁盤讀到的頁存放在緩沖池中,這個過程稱為將頁“FIX”在緩沖池中。下一次在讀相同的也頁時,首先判斷該頁是否在緩沖池中,若在緩沖池中,稱該頁在緩沖池中被命中,直接讀取該頁。否則讀取磁盤上的頁。
對于數據庫中也的修改操作,則首先修改緩沖池中的頁,然后再以一定的頻率刷新到磁盤上。這里需要注意的是,頁從緩沖池刷新回磁盤的操作斌不是每次頁發生更新時觸發,而是通過一種稱為CheckPoint的機制刷新回磁盤。同樣這也是為了提高數據庫的整體性能。
具體來看,緩沖池中緩存的數據頁類型有:索引頁,數據頁、undo頁、插入緩沖(insert buffer)、自適應哈希索引(adaptive hash index)、InnoDB存儲的鎖信息(lock info)、數據字典信息(data dictionary)等,InnoDB存儲引擎中內存的結構情況。
允許多個緩沖池實例,每個頁根據哈希值平均分配到不同的緩沖池實例中,這種做的好處是減少數據庫內部的資源競爭,增加數據庫的并發處理能力。可以通過參加innodb_buffer_pool_instances來進行配置,該值默認為1
2、LRU List、Free List和Flush List
通常來說,數據庫中的緩沖池是通過LRU (Latest Recent Used),最近最少使用算法來進行管理的。即最頻繁使用的頁在LRU列表的前端,而最少使用的頁在LRU列表的尾端。當緩沖池不能存放新讀取到頁時,將首先釋放LRU列表中的尾端的頁。
InnoDB存儲引擎中,緩沖池中頁的大小默認為16KB,同樣使用LRU算法對緩沖池進行管理,稍有不同的是InnoDB存儲引擎對傳統的LRU算法做了一些優化。在InnoDB的存儲引擎中,LRU列表中還加入了midpoint位置。新讀取到的頁,雖然是最新訪問的頁面,但并不是直接放入到LRU列表的首部,而是放入到LRU列表的midpoint位置。這個算法在InnoDB存儲引擎下稱為midpoint insertion strategy,在默認配置下,該文職在LRU列表長度得5/8處。midpoin位置可由參數innodb_old_blocks_pct控制,在InnoDB存儲引擎中,把midpoint之后的列表稱為old列表,之前的列表稱為new列表??梢院唵蔚睦斫鉃閚ew列表中的頁都是最為活躍的熱點數據。
為什么不直接采用樸素的LRU算法,直接將讀取的頁放入到LRU列表的首部?這是因為若直接將讀取到的頁放入到LRU的首部,那么某些SQL操作可能會使緩沖池中的頁被刷新出,從而影響緩沖池的效率。常見的這類操作為索引或數據的掃描操作。這類操作需要訪問列表中的許多頁,設置是全部的頁,而這些頁面通常來說又僅在這次查詢操作中需要,并不是活躍的熱點數據。如果頁被放入LRU列表的首部,那么非??赡軐⑺枰臒狳c數據從LRU列表中移除,而在下一次需要讀取該頁時,InnoDB存儲引擎需要再次訪問磁盤。
為了解決這個問題,InnoDB存儲引擎引入了另一個參數來進一步管理LRU列表,這個參數是innodb_old_blocks_time,用于表示頁讀取到mid位置后需要等待多久才會被加入到LRU列表的熱端。因此當需要執行上述所說的SQL操作時,可以通過下面的方法盡可能使LRU列表中熱點數據不被刷出。
LRU列表用來管理已經讀取的頁,但當數據庫剛啟動時,LRU列表是空的,即沒有任何頁,這是頁都存放在free 列表中,當需要從緩沖池中分頁時,首先從Free列表中查找是否有可用的空閑頁面,若有則將該頁從free列表中刪除,放入到LRU列表中,否則根據LRU算法,淘汰LRU列表末尾的頁面,將該內部空間分配給新的頁面。當頁從LRU列表的old部分加入到new部分時候,稱此時操作為page made young,而因為inno_old_blocks_time的設置導致頁從old部分移動new部分操作稱為page not made young??捎猛ㄟ^命令SHOW ENGINE INNODB STATUS來觀察LRU列表及Free列表的使用情況和運行狀態。
在LRU列表中的頁被修改后,稱該頁為臟頁(dirty page),即緩沖池中的頁和磁盤上的頁數據產生的不一致。這時數據庫會通過CHECKPOINT機制將臟頁刷新回磁盤,而Flush列表中,LRU列表用來管理緩沖池中的頁的可用性,Fluash列表用來管理將頁刷新回磁盤,二者互補影響。
3、重做日志緩沖
InnoDB存儲引擎的內存區域除了有緩沖池外,還有重做日志緩沖(redo log buffer)。InnoDB存儲引擎首先將重做日志信息放入到這個緩沖區,然后按一定的頻率將其刷新到重做日志文件。重做日志緩沖一般不需要社會得很大,因為一般情況下每秒鐘會將重做日志緩沖刷新到新的日志文件,因此用戶只需要保證每秒產生的事務在這個緩沖大小之內即可,該值可由配置參數innodb_log_buffer_size控制,默認為8MB
在通常情況下,8MB的重做日志緩沖池足以滿足絕大部分的應用,因為重做日志在系列三種情況下會將重做日志緩沖中的內容刷新到外掛磁盤的重做日志文件中。
1、Master Thread每一秒將重做日志緩沖刷新到重做日志文件;
2、每個事務提交時會將重做日志緩沖刷新到重做日志文件;
3、當重做日志緩沖池剩余空間小于1/2,重做日志日志緩沖刷新到重做日志文件。
4、額外的內存池
在InnoDB存儲引擎中,對內存的管理是通過一種稱為內存堆(heap)的方式進行的。在對一些數據結構本身的內存進行分配時,需要從額外的內存池中進行申請,當該區域的內存不夠時,會從緩沖池中進行分配,
2.4 checkpoint技術
緩沖池的設計目的為了協調CPU速度和磁盤速度的鴻溝。因此頁的操作首先都是在緩沖池中完成。如果一條DML語句,如果update或者Delete改變了頁中國的記錄,那么此時頁是臟的,即緩沖池中的頁的版本要比磁盤的新。數據庫需要將新版本的頁從緩沖池刷新到磁盤。
倘若每次一個頁發生變化,就將新頁面的版本刷新到磁盤,那么這個開銷是非常大的。若熱點數據中在某幾個頁中,那么數據庫的性能將變得非常差。同時如果在從緩沖池將頁的新版本刷新到磁盤時發生了宕機,那么數據就不能恢復了。為了避免發生數據丟失的問題,當前事務數據庫系統普遍都采用了Write Ahead Log策略,即當事務提交時,先重做日志,再修改頁面。當由于發生宕機導致數據丟失時,通過重做日志來完成數據的恢復,這也是事務ACID中D(Durability持久性)的要求。
思考下面的場景,如果重做日志可以無限地增大,同時緩沖池也足夠大,能夠緩沖所有數據庫的數據,那么是不需要將緩沖池中的頁的新版本刷新回磁盤。因為當發生宕機的時候,完全可以通過重寫日志來恢復整個數據庫中的數據到宕機發生的時刻。但是這需要兩個前提條件:
1、緩沖池可以緩存數據庫中所有的數據;
2、重做日志可以無限大
對于第一個前提條件,有經驗的用戶都知道,當數據庫開始創建時,表中沒有任何數據。緩沖池的確可以緩存所有的數據庫文件
。。。。。
Checkpoint 技術的目的是解決一下幾個問題:
1、縮短數據庫恢復的時間
2、緩沖池不夠用,將臟頁刷新到磁盤
3、重做日志不可用時,刷新臟頁面;
當數據庫發生宕機時,數據庫不需要重做所有的日志,因為CheckPoint之前的頁都已經刷新回磁盤。故數據庫值需要對Checkpoint后的重做日志進行恢復,這樣就大大縮短了恢復時間。
此外,當緩沖池不夠用時,根據LRU算法會溢出最近最少使用的頁,若此頁面為臟頁,那么需要強制執行Checkpoint,將臟頁也就是也的新版本刷回磁盤。
重做日志出現不可用的情況是因為當前事務數據庫系統對重做日志的設計都是循環使用的,并不是讓其無限增大,這從成成本及管理上都是比較困難的,重做日志可以被重用的部分是指這些重做日志已經不再需要,即當數據庫發生宕機時,數據庫恢復操作不需要這部分的操作日志,因此這部分就可以被覆蓋重用。若此時重做日志還需要使用,那么必須強制產生checkpoint,將緩沖池中的頁至少刷新到當前重做日志的位置。
對于InnoDB存儲引擎而言言,其是通過LSN(log Sequence Number)來標記版本的。而LSN是8字節的數字,其單位是字節。每個頁有LSN,重做日志中也有LSN,checkpoint也有LSN,
在InnoDB存儲引擎中,Checkpoint發生的時間,條件以臟頁的選擇都非常復雜。而Checkpoint所做的事情無法外乎是將緩沖池中的昂頁刷回到磁盤。不同之處在于每次刷新多少頁到磁盤,每次從哪里取臟頁,以及什么時間觸發Checkpoint,在InnoDB存儲引擎內部,有兩種checkpoint,分別為:
1、Sharp Checkpoint
2、Fuzzy Checkpoint
Sharp Checkpoint發生在數據庫關閉時將所有臟頁都刷新回磁盤,這是默認的工作方式
參數 innodb_fast_shutdown=1
但是若數據庫在運行時也使用Sharp Checkpoint,那么數據庫的可用性就會受到很大的影響,所以在InnoDB存儲引擎內部使用Fuzzy CheckPoint進行頁面刷新,即只刷新一部分臟頁,而不是刷新所有臟頁回磁盤。
下面幾種情況的Fuzzy CheckPoint
1、Master Thread Checkpoint
2、FLUSH_LRU_LIST_CheckPoint
3、Async/Sync Flush Checkpoint
4、Dirty page too much Checkpoint
Master Thread CheckPoint 以每秒或每十秒的速度從緩沖池的臟頁列表中刷新一定比例的頁回磁盤。這個過程是異步的,即此時Inno搜索引擎開源進行其他的操作,用戶查詢線程不會阻塞。
FLUSH_LRU_LIST CheckPoint是因為InnoDB存儲引擎需要保證LRU列表中需要有差不多100多個空閑頁可供使用。需要檢查LRU列表中是都有足夠的可用的空間操作發生在用戶查詢線程中,顯然會阻塞用戶的查詢操作。倘若沒有100個可用空閑頁面,那么InnoDB存儲引擎會將LRU列表尾端的頁移除。如果這些有臟頁,那么需要進行checkpoint,而這些頁是來自LRU列表的因此成為FLUSH_LRU_LIST checkpoint。
在mysql 5.6 也就是InnoDB1.2.x版本開始,這個檢查被一個單獨的Page Cleanner線程進行,并且用戶可以通過innodb_lru_scan_depth控制LRU列表中可用頁面的數量,該值默認為1024
Async/Sync flush checkpoint 指的是重做日志文件不可用的情況,這是需要強制將一些頁刷新回磁盤,而此時臟頁是從臟頁列表中選取的。若將已經寫入重做日志的LSN標記為redo_lsn,將已經刷新回磁盤最新也的LSN記為checkpoint_lsn
checkpoint_age = radio_lsn - checkpoint_lsn
再定義以下的變量:
async_water_mark = 75% * total_redo_log_file_size
sync_what_mark = 90% * total_log_file_size
2.5 Master Thread 工作方式
InnoDB存儲引擎的主要工作都是在一個獨立的后臺線程Master Thread中完成,
2.5.1 InnoDB 1.0.x版本之前的Master Thread
Master Thread 具有最高的線程的優先級別。其內部由多個循環loop組成:主循環loop、后臺循環backgroup loop、刷新循環flush loop、暫停循環suspend ?loop。Master Thread 會根據數據庫運行的狀態在loop,backgroud loop 、flush loop 和suspend loop中進行切換。
可以看到,loop循環通過thread sleep來實現,這意味著所謂的每秒一次或者10秒一次的操作時不精確的,在負載很大的情況下可能會有延遲(delay),只能說大概在這個頻率下。當然InnoDB源代碼中還通過了其他方法來盡量保證這個頻率。
每秒一次的操作包括:
1、日志緩沖刷新到磁盤,即使這個事務還沒有提交(總是)
2、合并插入緩沖(可能)
3、至多刷新100個InnoDB的緩沖池中的臟頁到磁盤(可能)
4、如果當前沒有用戶活動,則切換到background loop(可能)
即使某個事務還沒有提交,InnoDB存儲引擎任然每秒會將重做日志緩沖中的內容刷新到重做日志文件。這一點是必須要知道的,因為這可以很好地解釋為什么再大的事務提交commit的時間也是很短的。
合并插入緩沖(Insert Buffer)并不是每秒都會發生的。InnoDB存儲引擎會判斷當前一秒內發生的IO次數是否小于5,如果小于5次,InnoDB認為當前的IO壓力很小,可以執行合并插入緩沖的操作。
同樣,刷新100個臟頁也不是每秒會發生的,InnoDB存儲引擎通過判斷當前緩沖池中臟頁的比例(buf_get_modified_ratio_pct)是否超過了配置文件中innodb_max_dirty_pages_pct 這個參數(默認為90%),如果超過了這個閾值,InnoDB存儲引擎認為需要做磁盤同步的操作,將100臟頁寫入磁盤中。
10秒的操作
1、刷新100個臟頁到磁盤(可能的情況下)
2、合并至多5個插入緩沖(總是)
3、將日志緩沖刷新到磁盤(總是)
4、刪除無用的undo頁(總是)
5、刷新100個或者10個臟頁到磁盤(總是)。
在以上的過程中,InnoDB存儲引擎會先判斷過去10秒內磁盤的IO操作是否是小于200次,,如果是,InnoDB存儲引擎認為目前有足夠的磁盤IO操作能力,因此將100個臟頁刷新到磁盤。接著,InnoDB存儲引擎會合并插入緩沖。不同于每秒一次操作時可能發生的合并插入緩沖的操作,這次的合并插入緩沖操作總會在這個階段進行。之后,InnoDB存儲引擎會進行一次將日志緩沖刷新到磁盤的操作。這和每秒一次時發生的操作是一樣的。
接著InnoDB存儲引擎會執行full purge操作,即刪除無用的Undo頁面。對表進行update、delete這類操作,原先的行被標記為刪除,但是因為一致性讀(consistance read)的關系,需要保留這些行版本的信息。但是在full purge的過程中,InnoDB存儲引擎會判斷當前事務系統中已被刪除的行是都可以刪除,比如有時候可能還有查詢操作需要讀取之前的版本undo的信息,如果可以刪除,InnoDB會立即將其刪除。從源代碼中可以發現,InnoDB存儲引擎在執行full purge操作時,每次最多嘗試回收20個undo頁。
然后InnoDB存儲引擎會判斷緩沖池頁面的比例buf_get_modified_ratio_pct,如果有超過50%的臟頁面,則刷新100個臟頁到磁盤,如果臟頁的比例小于70%,則只需要刷新10%的臟頁面到磁盤。
若當前沒有用戶活動(數據庫空閑)或者數據庫關閉(shutdown),就會切換到這個循環。background loop會執行以下操作:
1、刪除無用的undo頁(總是)
2、合并20個插入緩存(總是)
3、跳回到主循環(總是)
4、不斷刷新100個頁面知道服務條件(可能,跳轉到flush loop中完成)。
若flush loop中也什么事情可以做,InnoDB存儲引擎會切換到suspend_loop,將Master Thread掛起,等待事情的發生。若用戶啟用enable了InnoDB存儲引擎,卻沒有使用任何InnoDB存儲引擎的表,那么Master Thread 總是處于掛起的狀態。
2.5.2 InnoDB1.2.x版本之前的Master Thread
InnoDB存儲引擎對于IO其實是有限制的,在緩沖池想磁盤刷新時,其實都做了一定的硬編碼(hard coding)。在磁盤技術飛速發展的今天,當固態硬盤SSD出現時,這種規定在很大程度上限制了InnoDB存儲引擎對磁盤IO的性能,尤其是寫入性能。
InnoDB存儲引擎最大只會刷新100個臟頁到磁盤,合并20個插入緩沖。如果再寫入密集的應用程序中,每秒可能產生大于100個臟頁面,如果是產生了大于20個插入緩沖的情況,Master Thread 似乎會“忙不過來”,或者說它總是做的很慢。即使磁盤能在1秒內處理多于100個臟頁的寫入和20個插入緩存的合并,但是由于hard coding。Master Thread 也只會選擇刷新100個臟頁和并20個插入緩沖。同時,當發生宕機需要恢復時,由于很多數據還沒有刷新回磁盤,會導致恢復的時間可能需要很久,尤其是對于insert buffer來說。經過谷歌團隊的修正提供了innodb_io_capacity的百分比來進行控制,規則如下:
1、在合并插入緩沖時,合并插入緩沖的數量為innodb_io_capacity值的5%
2、在從緩沖區刷新臟頁時,刷新臟頁的數量為Innodb_io_capacity
如果用戶使用了SSD類的磁盤,或者將盡快磁盤做了RAID,當存儲設備擁有更高的IO速度時,玩可以將innodb_io_capacity的值調得再高點,知道符合磁盤IO的吞吐量為止。
另一個問題是,參數innodb_max_dirty_pages_pct默認值的問題,在InnoDB 1.0.x版本之前,該值的默認為90,意味著臟頁占緩沖池的90%,但是該值“太大”了,因為InnoDB存儲引擎在每秒刷新緩沖池和flush loop時會判斷這個值,如果該值大于innodb_max_dirty_pages_pct,才刷新100個臟頁,如果有很大的內存,或者數據庫服務器的壓力很大,這時刷新臟頁的速度反而會降低。同樣在數據庫的恢復階段可能需要更多的時間。
在很多論壇上都有對這個問題的討論,有人甚至將這個值調到 20或10,然后測試發現性能所有提高,但是將Innodb_max-dirty_pages_pct調到20或10會增加磁盤的壓力,系統的負擔還是會有所增加的。google在這個問題上進行了測試,證明20并不是一個最優值。從1.0.x版本開始,inno_max_dity_pages_pct默認值變為75和google測試的80%比較接近,這樣既可以加快刷新臟頁頻率,又能保證磁盤IO的負載。
InnoDB 1.0.x版本帶來的另一個參數是innodb_adaptive_flushing(自適應地刷新)
該值影響每秒刷新臟頁的數量,不刷新臟頁;大于innodb_max_dirty_pages_pct時,刷新100個臟頁。隨著inno_adaptive_flushing參數的引入,InnoDB存儲引擎會通過一個名為buf_flush_get_desired_flush_rate的函數來判斷需要刷新臟頁最合適的數量。粗略地翻閱源代碼后發現buf_flush_get_desired_flush_rate通過判斷產生重做日志undo log的速度來決定適合的刷新臟頁的數量。因此當臟頁的比例小于innodb_max_dirty_pages_pct時,也會刷新一定量的臟頁。
還有一個改變:之前每次進行full purge操作時,最多回收20個undo頁面,從InnoDB 1.0.x版本開始引入參數innodb_purge_batch_size這個參數可以控制每次full purge回收的undo頁的數量。該參數的默認值為20,并可以動態地對其進行修改。
2.5.3 InnoDB 1.2.x版本的Master Thread
srv_master_do_idle_tasks() 之前版本中每10秒的操作
srv_master_do_active_tasks()之前版本每秒的操作
從Master Thread線程分離到一個單獨的Page Cleaner Thread,從而減輕了Master Thread的工作,同時進一步挺高了行的并發性。
2.6 InnoDB關鍵特性
1、插入緩沖 (Insert buffer)
2、兩次寫(double write)
3、自適應哈希索引(Adaptive Hash Index)
4、異步IO(Async IO)
5、刷新鄰接頁(Flush Neighbor Page)
一般情況下按照主鍵有序插入,如自增長,這樣的插入方式速度快,
如遇到UUID或者輔助索引,secondary index 并非有序,需要離散地訪問非聚集索引頁,由于隨機讀寫的存在而導致插入操作性能下降,而B+樹的特性決定了非聚集索引插入的離散性。
插入緩沖(insert buffer)
InnoDB存儲引擎開創性地設計了Insert Buffer,對于非聚集索引的插入或更新操作,不是每一次直接插入到索引頁中,而是先判斷插入的非聚集的引頁是都在緩沖池中,若在,則直接插入,若不在,則先放入到一個Insert Buffer對象中。然后再以一定的頻率和情況進行Insert buffer和輔助索引頁子節點的merge(合并)操作,這是通常能將多個插入合并到同一個操作中(因為在一個索引頁面中),這就大大提高了非聚集索引插入的性能。
insert buffer 的使用需要滿足以下兩個條件:
1、索引是輔助索引(secondary index)
2、索引不是唯一索引(unique)的
3、insert buffer的內部實現
以前版本每個表都有一顆Insert buffer B+樹
現在的版本內部只要一個B+樹,負責對所有的表的輔助索引進行Insert buffer。而這棵B+樹存放在共享表的空間中,默認也是在ibdata1。因此試圖通過獨立表空間ibd文件恢復表中的數據時,往往會導致CHECK TABLE失敗。這是因為表的輔助索引中的數據可能換在Insert Buffer中,也是共享空間中,所以通過ibd文件進行回復后,還需要進行REPAIR TABLE操作來重建表上的所有輔助索引。
Insert Buffer 是一個B+樹,因此由也是節點和非葉子節點組成。非葉子節點存放的是查詢的search key(鍵值)構造如下
search key一共占用9個字節,其中space表示帶插入記錄所在的表的表空間id,space占用4個字節。marker占用1個字節,它是用來兼容老版本的insert buffer,offset表示頁所在的偏移量,占4個字節
當一個輔助索引要插入到頁(space,offset)時,如果這個頁不在緩沖池中,那么InnoDB存儲引擎首先根據上述規則茍澤一個search key,接下來查詢insert buffer 這課B+樹,然后再將這條記錄插入到Insert Buffer B+樹的葉子節點中
2.6.2兩次寫 doublewrite
2.6.3 自適應哈希索引
哈希(hash)是一種非??斓牟檎曳绞剑谝话愕那闆r下這種查找的時間復雜度為O(1),即一般僅需要一次查找就能定位的數據。而B+樹的查找次數,取決于B+樹的高度,在生產環境中,B+樹的高度一般為3-4層,古只需要3-4查詢。
InnoDB存儲引擎會監控對表上各索引頁的查詢。如果觀察到建立哈希索引可以帶來速度提升,則建立哈希索引,稱之為自適應哈希索引(Adaptive Hash Index,AHI)。AHI是通過緩沖池的B+樹頁構造而來,因此建立的速度很快,而且不需要對整張表構建哈希索引。InnoDB存儲引擎會自動根據訪問的頻率和模式來自動地為某些熱點建立哈希索引。
2.6.4異步IO
2.6.5刷新鄰接頁
Flush Neighbor Page(刷新緊鄰頁)的特性,其工作原理為:當刷新一個臟頁時,Inno存儲引擎會檢測該頁所在區(extent)的所有頁,如果是臟頁,那么一起進行刷新。