??????
? ??????lnnoDB是事務安全的MySQL存儲引擎潘拱, 設計上采用了類似于Oracle數(shù)據(jù)庫的架構。 通常來說拧略,InnoDB存儲引擎是OLTP應用中核心表的首選存儲引擎 芦岂。同時, 也正是因為InnoDB的存在垫蛆, 才使MySQL數(shù)據(jù)庫變得更有魅力禽最。 本章將詳細介紹lnnoDB存儲引擎的體系架構及其不同于其他存儲引擎的特性。
2.1 InnoDB存儲引擎概述
????????lnnoDB存儲引擎特點是行鎖設計袱饭、 支持MVCC弛随、 支持外鍵、 提供一致性非鎖定讀宁赤, 同時被設計用來最有效地利用以及使用內(nèi)存和CPU。
2.3 lnnoDB體系架構
????????圖2-1簡單顯示了InnoDB的存儲引擎的體系架構栓票, 從圖可見决左,lnnoDB存儲引擎有多個內(nèi)存塊, 可以認為這些內(nèi)存塊組成了一個大的內(nèi)存池走贪, 負責如下工作:
1佛猛,維護所有進程/線程需要訪問的多個內(nèi)部數(shù)據(jù)結構。
2坠狡,緩存磁盤上的數(shù)據(jù)继找, 方便快速地讀取, 同時在對磁盤文件的數(shù)據(jù)修改之前在這里緩存逃沿。
3婴渡,重做日志(redo log)緩沖。
????????后臺線程的主要作用是負責刷新內(nèi)存池中的數(shù)據(jù)凯亮, 保證緩沖池中的內(nèi)存緩存的是最近的數(shù)據(jù)边臼。此外將已修改的數(shù)據(jù)文件刷新到磁盤文件, 同時保證在數(shù)據(jù)庫發(fā)生異常的情況下InnoDB能恢復到正常運行狀態(tài)假消。
2.3.1 后臺線程
????????InnoDB存儲引擎是多線程的模型柠并, 因此其后臺有多個不同的后臺線程, 負責處理不同的任務富拗。
1 . Master Thread
? ??????Master Thread是一個非常核心的后臺線程臼予, 主要負責將緩沖池中的數(shù)據(jù)異步刷新到磁盤, 保證數(shù)據(jù)的一致性啃沪, 包括臟頁的刷新粘拾、 合并插入緩沖(INSERT BUFFER)、 UNDO頁的回收等创千。2.5節(jié)會詳細地介紹各個版本中Master Thread的工作方式半哟。
2.IO Thread
? ??????在InnoDB存儲引擎中大量使用了AIO(Async IO)來處理寫IO請求酬滤, 這樣可以極大提高數(shù)據(jù)庫的性能。而IO Thread的工作主要是負責這些IO請求的回調(diào)(call back) 處理寓涨。
3. Purge Thread
? ??????事務被提交后盯串, 其所使用的undolog可能不再需要, 因此需要PurgeThread來回收已經(jīng)使用并分配的undo頁戒良。 在InnoDB 1.1版本之前体捏,purge操作僅在InnoDB存儲引擎的Master Thread中完成。 而從InnoDB 1.1版本開始糯崎,purge操作可以獨立到單獨的線程中進行几缭, 以此來減輕Master Thread的工作, 從而提高CPU的使用率以及提升存儲引擎的性能沃呢。
? ??????從InnoDB 1.2版本開始年栓,InnoDB支持多個Purge Thread, 這樣做的目的是為了進一步步加快undo頁的回收。同時由于Purge Thread需要離散地讀取undo頁薄霜, 這樣也能更進一步利用磁盤的隨機讀取性能某抓。
#undo log有兩個作用:提供回滾和多個行版本控制(MVCC)。
在數(shù)據(jù)修改的時候惰瓜,不僅記錄了redo否副,還記錄了相對應的undo,如果因為某些原因?qū)е率聞帐』蚧貪L了崎坊,可以借助該undo進行回滾备禀。
4. Page Cleaner Thread
? ??????Page Cleaner Thread是在InnoDB 1.2.x版本中引入的。其作用是將之前版本中臟頁的刷新操作都放入到單獨的線程中來完成奈揍。而其目的是為了減輕原Master Thread的工作及對于用戶查詢線程的阻塞曲尸, 進一步提高InnoDB存儲引擎的性能。
2.3.2 內(nèi)存
1. 緩沖池
? ??????lnnoDB存儲引擎是基于磁盤存儲的男翰, 并將其中的記錄按照頁的方式進行管理队腐。因此可將其視為基于磁盤的數(shù)據(jù)庫系統(tǒng)(Disk-base Database)。在數(shù)據(jù)庫系統(tǒng)中奏篙, 由于CPU速度與磁盤速度之間的鴻溝柴淘, 基于磁盤的數(shù)據(jù)庫系統(tǒng)通常使用緩沖池技術來提高數(shù)據(jù)庫的整體性能。
? ??????緩沖池簡單來說就是一塊內(nèi)存區(qū)域秘通, 通過內(nèi)存的速度來彌補磁盤速度較慢對數(shù)據(jù)庫性能的影響为严。在數(shù)據(jù)庫中進行讀取頁的操作, 首先將從磁盤讀到的頁存放在緩沖池中肺稀, 這個過程稱為將頁"FIX"在緩沖池中第股。下一次再讀相同的頁時,首先判斷該頁是否在 緩沖池中话原。若在緩沖池中夕吻, 稱該頁在緩沖池中被命中诲锹, 直接讀取該頁。否則涉馅, 讀取磁盤上的頁归园。
? ??????對于數(shù)據(jù)庫中頁的修改操作, 則首先修改在緩沖池中的頁稚矿, 然后再以一定的頻率刷新到磁盤上庸诱。這里需要注意的是, 頁從緩沖池刷新回磁盤的操作并不是在每次頁發(fā)生更新時觸發(fā)晤揣,而是通過一種稱為Checkpoint的機制刷新回磁盤桥爽。同樣,這也是為了提高數(shù)據(jù)庫的整體性能昧识。
? ??????綜上所述钠四,緩沖池的大小直接影響指數(shù)據(jù)庫的整體性能。由于32位操作系統(tǒng)的限制跪楞,在該系統(tǒng)下最多將該值設置為3G缀去。此外用戶可以打開操作系統(tǒng)的PAE選項來獲得 32位操作系統(tǒng)下最大64GB內(nèi)存的 支持。隨著內(nèi)存技術的不斷成熟习霹,其成本也在不斷下降。單條8GB的內(nèi)存變得非常普遍炫隶,而PC服務器已經(jīng)能支持512GB的內(nèi)存 淋叶。因此為了讓數(shù)據(jù)庫使用更多的內(nèi)存,強烈建議數(shù)據(jù)庫服務器都采用64位的操作系統(tǒng)伪阶。
? ??????對于InnoDB存儲引擎而言煞檩,其緩沖池的配置通過參數(shù)innodb_ buffer _pool_ size來設置。
? ??????具體來看栅贴,緩沖池中緩存的數(shù)據(jù)頁類型有:索引頁斟湃、數(shù)據(jù)頁、undo頁檐薯、插入緩沖(insert buffer)凝赛、自適應哈希索引(adaptive hash index)、InnoDB存儲的鎖信息(lockinfo)坛缕、數(shù)據(jù)字典信息(data dictionary)等墓猎。不能簡單地認為,緩沖池只是緩存索引頁和數(shù)據(jù)頁赚楚,它們只是占緩沖池很大的一部分而已毙沾。圖2-2很好地顯示了InnoDB存儲引擎中內(nèi)存的結構情況。
????????從InnoDB 1.0.x版本開始宠页,允許有多個緩沖池實例左胞。每個頁根據(jù)哈希值平均分配到不同緩沖池實例中 寇仓。這樣做的好處是減少數(shù)據(jù)庫內(nèi)部的資源競爭,增加數(shù)據(jù)庫的并發(fā)處理能力烤宙”榉常可以通過參數(shù)innodb_ buffer _pool_ instances來進行配置, 該值默認為1门烂。
2. LRU List乳愉、Free List和Flush List
????????通常來說, 數(shù)據(jù)庫中的緩沖池是通過LRU (Latest Recent Used, 最近最少使用)算法來進行管理的屯远。 即最頻繁使用的頁在LRU列表的 前端蔓姚, 而最少使用的頁在LRU列表的尾端。 當緩沖池不能存放新讀取到的頁時慨丐, 將首先釋放LRU列表中尾端的頁坡脐。
? ??????在InnoDB存儲引擎中, 緩沖池中頁的大小默認為16KB, 同樣使用LRU算法對緩沖池進行管理房揭。稍有不同的是InnoDB存儲引擎對傳統(tǒng)的LRU算法做了一些優(yōu)化备闲,在InnoDB的存儲引擎中,LRU列表中還加入了midpoint位置捅暴。新讀取到的頁恬砂,雖然是最新訪問的頁,但并不是直接放入到LRU列表的首部蓬痒,而是放入到LRU列表的midpoint位置泻骤。這個算法在 lnnoDB存儲引擎下稱為midpoint insertion strategy。在默認配置下梧奢, 該位置在LRU列表長度的5/8處狱掂。midpoint位置可由參數(shù)innodb _old_ blocks _pct控制
? ??????那為什么不采用樸素的LRU算法 ,直接將讀取的頁放入到LRU列表的首部呢亲轨?這是因為若直接將讀取到的頁放入到LRU的首部趋惨, 那么某些SQL操作可能會使緩沖池中的頁被刷新出,從而影響緩沖池的效率惦蚊。常見的這類操作為索引或數(shù)據(jù)的掃描操作器虾。這類操作需要訪問表中的許多頁,甚至是全部的頁蹦锋,而這些頁通常來說又僅在這次查詢操作中需要曾撤,并不是活躍的熱點數(shù)據(jù)。 如果頁被放入LRU列表的首部晕粪,那么非臣废ぃ可能將所需要的熱點數(shù)據(jù)頁從 LRU列表中移除,而在 下 次需要讀取該頁時,InnoDB存儲引擎需要再次訪問磁盤装悲。
????????為了解決這個問題昏鹃,lnnoDB存儲引擎引入了另一個參數(shù)來進一步管理LRU列表,這個參數(shù)是innodb _old_ bloks_time诀诊,用于表示頁讀取到Mid位置后需要等待多久才會被加入到LRU列表的熱端洞渤。
? ??????LRU列表用來管理已經(jīng)讀取的頁, 但當數(shù)據(jù)庫剛啟動時属瓣,LRU列表是空的载迄, 即沒有任何的頁。 這時頁都存放在Free列表中抡蛙。 當需要從緩沖池中分頁時护昧, 首先從Free列表中查找是否有可用的空閑頁, 若有則將該頁從Free列表中刪除粗截, 放入到LRU列表中惋耙。否則, 根據(jù)LRU算法熊昌, 淘汰LRU列表末尾的頁绽榛,將該內(nèi)存空間分配給新的頁。 當頁從 LRU列表的old部分加人到new部分 時婿屹,稱此時發(fā)生的操作為page made young, 而因為innodb_old_ blocks_ time的設置而導致頁沒有從old部分移動到new部分的操作稱為page not made young灭美。 可以通過命令SHOW ENGINE INNODB STATUS來觀察LRU列表及Free列表的使用情況和運行狀態(tài)。
? ??????從InnoDB 1.2版本開始昂利, 還可以通過表 INNODB_BUFFER_POOL_STATS來觀察緩沖池的運行狀態(tài)届腐。此外, 還可以通過表INNODB_BUFFER_PAGE_LRU來觀察每個LRU列表中每個頁的具體信息页眯。
? ??????在LRU列表中的頁被修改后梯捕, 稱該頁為臟頁(dirty page) , 即緩沖池中的頁和磁盤上的頁的數(shù)據(jù)產(chǎn)生了不一致厢呵。 這時數(shù)據(jù)庫會通過CHECKPOINT機制將臟頁刷新回磁盤窝撵,而Flush列表中的頁即為臟頁列表。需要注意的是襟铭,臟頁既存在于LRU列表中碌奉, 也存在于Flush列表中。LRU列表用來管理緩沖池中頁的可用性寒砖,F(xiàn)lush列表用來管理將頁刷新回磁盤赐劣,二者互不影響。
? ??????同 LRU列表一樣哩都, Flush列表也可以通過命令SHOW ENGINE INNODB STATUS 來查看魁兼。
3. 重做日志緩沖
? ?????? InnoDB 存儲引擎的內(nèi)存區(qū)域除了有緩沖池外, 還有重做日志緩沖 (redologbuffer)漠嵌。 InnoDB 存儲引擎首先將重做日志信息先放入到這個緩沖區(qū)咐汞, 然后按一定頻率將其刷新到重做日志文件盖呼。 重做日志緩沖一般不需要設置得很大, 因為一般情況下每一秒鐘會將重做日志緩沖刷新到日志文件衣盾, 因此用戶只需要保證每秒產(chǎn)生的事務量在這個緩沖大小之內(nèi)即可嗡综。該值可由配置參數(shù)innodb_log_ buffer_ size 控制盛撑, 默認為8MB。
????????在通常悄況下蟹瘾, 8MB 的重做日志緩沖池足以滿足絕大部分的應用, 因為重做日志在下列三種情況下會將重做日志緩沖中的內(nèi)容刷新到外部磁盤的重做日志文件中掠手。
1.Master Thread 每一秒將重做日志緩沖刷新到重做日志文件憾朴;
2.每個事務提交時會將重做日志緩沖刷新到重做日志文件。
3.當重做日志緩沖池剩余空間小于1/2 時惨撇, 重做日志緩沖刷新到重做日志文件伊脓。
4. 額外的內(nèi)存池
? ??????額外的內(nèi)存池通常被DBA 忽略, 他們認為該值并不十分重要魁衙, 事實恰恰相反报腔, 該值同樣十分重要。在InnoDB 存儲引擎中剖淀, 對內(nèi)存的管理是通過一種稱為內(nèi)存堆Cheap)的方式進行的纯蛾。在對一些數(shù)據(jù)結構本身的內(nèi)存進行分配時, 需要從額外的內(nèi)存池中進行申請纵隔, 當該區(qū)域的內(nèi)存不夠時翻诉, 會從緩沖池中進行申請。例如捌刮, 分配了緩沖池(innodb_buffer_ pool), 但是每個緩沖池中的幀緩沖 (framebuffer)還有對應的緩沖控制對象(buffer control block), 這些對象記錄了一些諸如LRU碰煌、鎖、等待等信息绅作, 而這個對象的內(nèi)存需要從額外內(nèi)存池中申請芦圾。因此, 在申請了很大的InnoDB 緩沖池時俄认, 也應考慮相應地增加這個值个少。
2.4 Checkpoint 技術
?????????前面已經(jīng)講到了, 緩沖池的設計目的為了協(xié)調(diào)CPU速度與磁盤速度的鴻溝眯杏。 因此頁的操作首先都是在緩沖池中完成的夜焦。 如果一條DML語句, 如Update或Deiete改變了頁 中的記錄岂贩, 那么此時頁是臟的茫经, 即緩沖池中的頁的版本要比磁盤的新。 數(shù)據(jù)庫需要將新版本的頁從緩沖池刷新到磁盤。
? ??????倘若每次一個頁發(fā)生變化卸伞, 就將新頁的版本刷新到磁盤褥紫, 那么這個開銷是非常大的。 若熱點數(shù)據(jù)集中在某幾個頁中瞪慧, 那么數(shù)據(jù)庫的性能將變得非常差髓考。 同時, 如果在從緩沖池將頁的新版本刷新到磁盤時發(fā)生了宥機弃酌, 那么數(shù)據(jù)就不能恢復了氨菇。 為了避免發(fā)生數(shù)據(jù)丟失的問題, 當前事務數(shù)據(jù)庫系統(tǒng)普遍都采用了Write Ahead Log策略妓湘, 即當事務提交時查蓉, 先寫重做日志, 再修改頁榜贴。 當由于發(fā)生巖機而導致數(shù)據(jù)丟失時豌研, 通過重做日志來完成數(shù)據(jù)的恢復。 這也是事務 ACID中D (Durability持久性)的要求唬党。
Checkpoint(檢查點)技術的目的是解決以下幾個問題:
1.縮短數(shù)據(jù)庫的恢復時間鹃共;
2.緩沖池不夠用時,將臟頁刷新到磁盤驶拱;
3.重做日志不可用時霜浴,刷新臟頁。
? ??????當數(shù)據(jù)庫發(fā)生宕機時蓝纲,數(shù)據(jù)庫不需要重做所有的日志阴孟,因為Checkpoint之前的頁都已經(jīng)刷新回磁盤。故數(shù)據(jù)庫只需對Checkpoint后的重做日志進行恢復税迷。這樣就大大縮短了恢復的時間永丝。
? ??????此外,當緩沖池不夠用時箭养,根據(jù)LRU算法會溢出最近最少使用的頁慕嚷,若此頁為臟頁,那么需要強制執(zhí)行Checkpoint,將臟頁也就是頁的新版本刷回磁盤露懒。
? ??????重做日志出現(xiàn)不可用的情況是因為當前事務數(shù)據(jù)庫系統(tǒng)對重做日志的設計都是循環(huán)使用的闯冷,并不是讓其無限增大的砂心,這從成本及管理上都是比較困難的懈词。重做日志可以被重用的部分是指這些重做日志已經(jīng)不再需要,即當數(shù)據(jù)庫發(fā)生宕機時辩诞,數(shù)據(jù)庫恢復操作不需要這部分的重做日志坎弯,因此這部分就可以被覆蓋重用。若此時重做日志還需要使用,那么必須強制產(chǎn)生Checkpoint,將緩沖池中的頁至少刷新到當前重做日志的位置抠忘。
? ??????對于lnnoDB存儲引擎而言撩炊,其是通過LSN(Log Sequence Number)來標記版本的。而LSN是8字節(jié)的數(shù)字崎脉,其單位是字節(jié)拧咳。每個頁有LSN,重做日志中也有LSN, Checkpoint也有LSN∏糇疲可以通過命令SHOWENGINE INNODB STATUS來觀察骆膝。
? ??????Checkpoint所做的事情無外乎是將緩沖池中的臟頁刷回到磁盤。不同之處在于每次刷新多少頁到磁盤灶体,每次從哪里取臟頁阅签,以及什么時間觸發(fā)Checkpoint。在InnoDB存儲 引擎內(nèi)部蝎抽,有兩種Checkpoint,分別為:
1.Sharp Checkpoint
2. Fuzzy Checkpoint
? ??????Sharp Checkpoint發(fā)生在數(shù)據(jù)庫關閉時將所有的臟頁都刷新回磁盤政钟,這是默認的工作方式,即參數(shù)innodb_fast_ shutdown=1樟结。
? ??????是若數(shù)據(jù)庫在運行時也使用 SharpCheckpoint, 那么數(shù)據(jù)庫的可用性就會受到很大的影響养交。故在InnoDB存儲引擎內(nèi)部使用FuzzyCheckpoint進行頁的刷新,即只刷新一 部分臟頁瓢宦,而不是刷新所有的臟頁回磁盤层坠。在InnoDB存儲引擎中可能發(fā)生如下幾種情況的Fuzzy Checkpoint:
1 Master Thread Checkpoint
2 FLUSH_LRU_LIST Checkpoint
3 Async/Sync Flush Checkpoint
4 Dirty Page too much Checkpoint
? ??????對于Master Thread (2.5 節(jié)會詳細介紹各個版本中Master Thread的實現(xiàn))中發(fā)生的Checkpoint, 差不多以每秒或每十秒的速度從緩沖池的臟頁列表中刷新一定比例的頁回 磁盤。這個過程是異步的刁笙,即此時InnoDB存儲引擎可以進行其他的操作破花,用戶查詢線程不會阻塞。
????????FLUSH_ LRU _LIST Checkpoint是因為InnoDB存儲引擎需要保證LRU列表中需要有差不多100個空閑頁可供使用疲吸。在InnoDB1.1.x版本之前座每,需要檢查LRU列表中是否有足夠的可用空間操作發(fā)生在用戶查詢線程中,顯然這會阻塞用戶的查詢操作摘悴。倘若沒有100個可用空閑頁峭梳,那么InnoDB存儲引擎會將 LRU列表尾端的頁移除。如果這些頁中有臟頁蹂喻,那么需要進行Checkpoint, 而這些頁是來自LRU列表的葱椭,因此稱為FLUSH_ LRU _LIST Checkpoint。
????????而從MySQL 5.6版本口四,也就是lnnoDB1.2.x版本開始孵运,這個檢查被放在了一個單獨的PageCleaner線程中進行,并且用戶可以通過參數(shù)innodb _ lru _scan_ depth控制LRU列表中可用頁的數(shù)量蔓彩,該值默認為1024治笨。
? ??????Async/Sync Flush Checkpoint指的是重做日志文件不可用的情況驳概, 這時需要強制將一些頁刷新回磁盤, 而此時臟頁是從臟頁列表中選取的 旷赖。Async/SyncFlush Checkpoint是為了保證重做日志的循環(huán)使用的可用性顺又。 從lnnoDB1.2.x版本開始 也就是MySQL 5.6版本, 這部分的刷新操作同樣放入到了單獨的PageCleaner線程中進行等孵。
? ??????最后一種Checkpoint的情況是Dirty Page too much, 即臟頁的數(shù)量太多稚照, 導致lnnoDB存儲引擎強制進行Checkpoint。 其目的總的來說還是為了保證緩沖池中有足夠可用的頁 俯萌。 其可由參數(shù)innodb_max_ dirty _pages _pct控制锐锣。
? ??????innodb _max_ dirty _pages _pct值為75表示, 當緩沖池中臟頁的數(shù)量占據(jù)75%時绳瘟,強制進行Checkpoint, 刷新一部分的臟頁到磁盤雕憔。 在lnnoDB 1.0.x版本之前, 該參數(shù)默認值為90, 之后的版本都為75糖声。
2.5 Master Thread 工作方式
????????在2.3節(jié)中我們知道了斤彼,InnoDB存儲引擎的主要工作都是在一個單獨的后臺線程Master Thread中完成的,這一節(jié)將具體解釋該線程的具體實現(xiàn)及該線程可能存在的問題蘸泻。
2.5.1 lnnoDB 1.0.x版本之前的Master Thread
? ??????Master Thread具有最高的線程優(yōu)先級別琉苇。 其內(nèi)部由多個循環(huán)(loop)組成: 主循環(huán)(loop)、 后臺循環(huán)(backgroup loop)悦施、 刷新循環(huán)(flush loop)并扇、 暫停循環(huán)(suspend loop)。 Master Thread會根據(jù)數(shù)據(jù)庫運行的狀態(tài)在loop抡诞、 background loop穷蛹、 flush loop和suspendloop中進行切換。
? ??????Loop被稱為主循環(huán)昼汗,因為大多數(shù)的操作是在這個循環(huán)中肴熏,其中有兩大部分的操作-每秒鐘的操作和每 10秒的操作。
? ??????可以看到顷窒,loop循環(huán)通過thread sleep來實現(xiàn)蛙吏,這意味著所謂的每秒一次或每10秒一次的操作是不精確的。在負載很大的情況下可能會有延遲(delay), 只能說大概在這個頻率下鞋吉。當然鸦做,InnoDB源代碼 中還通過了其他的方法來盡量保證這個頻率每秒一次的操作包括:
1.日志緩沖刷新到磁盤,即使這個事務還沒有提交(總是)谓着;
2.合并插入緩沖(可能)泼诱;
3.至多刷新100個InnoDB的緩沖池中的臟頁到磁盤(可能);
4.如果當前沒有用戶活動漆魔,則切換到backgro und loop (可能)坷檩。
? ??????即使某個事務還沒有提交,InnoDB存儲引擎仍然每秒會將重做日志緩沖中的內(nèi)容刷新到重做日志文件改抡。這一點是必須要知道的矢炼,因為這可以很好地解釋為什么再大的事務提交(commit)的時間也是很短的。
? ??????合并插入緩沖(InsertBuffer)并不是每秒都會發(fā)生的阿纤。InnoDB存儲引擎會判斷當前一秒內(nèi)發(fā)生的IO次數(shù)是否小于5次句灌,如果小于5次,InnoDB認為當前的IO壓力很小欠拾,可以執(zhí)行合并插入緩沖的操作胰锌。
? ??????同樣, 刷新100個臟頁也不是每秒都會發(fā)生的藐窄。InnoDB存儲引擎通過判斷當前緩沖池中臟頁的比例(buf_get_ modified_ ratio _pct)是否超過了配置文件中innodb _max_ dirty _pages _pct 這個參數(shù)(默認為90, 代表90%), 如果超過了這個闊值资昧,InnoDB存儲引擎認為需要做磁盤同步的操作, 將100個臟頁寫人磁盤中荆忍。
接著來看每10秒的操作格带, 包括如下內(nèi)容:
1.刷新100個臟頁到磁盤(可能的情況下);
2. 合并至多5個插入緩沖(總是)刹枉;
3. 將日志緩沖刷新到磁盤(總是)叽唱;
4.刪除無用的Undo頁(總是)微宝;
5. 刷新100個或者10個臟頁到磁盤(總是)棺亭。
? ??????在以上的過程中蟋软,InnoDB存儲引擎會先判斷過去10秒之內(nèi)磁盤的IO操作是否小于200次棺耍, 如果是,InnoDB存儲引擎認為當前有足夠的磁盤IO操作能力, 因此將100個臟頁刷新到磁盤害幅。 接著消恍,InnoDB存儲引擎會合并插入緩沖。 不同于每秒一次操作時可能發(fā)生的合并插人緩沖操作以现, 這次的合并插人緩沖操作總會在這個階段進行狠怨。 之后约啊, InnoDB存儲引擎會再進行一次將日志緩沖刷新到磁盤的操作。 這和每秒一次時發(fā)生的操作是一樣的佣赖。
????????接著InnoDB存儲 引擎會進行 一步執(zhí)行full purge操 作恰矩, 即刪除 無用的Undo 頁。 對表進行update憎蛤、 delete這類操作時外傅, 原先的行被標記為刪除, 但是因為一致性讀(consistentread)的關系俩檬, 需要保留這些行版本的信息萎胰。 但是在full purge過程中, InnoDB存儲引擎會判斷當前事務系統(tǒng)中已被刪除的行是否可以刪除棚辽, 比如有時候可能還有查詢操作需要讀取之前版本的undo信息技竟, 如果可以刪除,lnnoDB會立即將其刪除屈藐。
? ??????從源代碼中可以發(fā)現(xiàn)灵奖,InnoDB存儲引擎在執(zhí)行fullpurge操作時,每次最多嘗試回收6個undo頁估盘。
????????然后瓷患,InnoDB存儲引擎會判斷緩沖池中臟頁的比例Cbuf_get_modified_ratio_pct),如果有超過70%的臟頁,則刷新100個臟頁到磁盤遣妥,如果臟頁的比例小于70%,則只需刷新10%的臟頁到磁盤擅编。
? ? ? ??接著來看backgroundloop, 若當前沒有用戶活動(數(shù)據(jù)庫空閑時)或者數(shù)據(jù)庫關閉(shutdown), 就會切換到這個循環(huán)。backgroundloop會執(zhí)行以下操作:
1 刪除無用的Undo頁(總是)箫踩;
2 合并20個插人緩沖(總是)爱态;
3 跳回到主循環(huán)(總是);
4.不斷刷新100個頁直到符合條件(可能境钟,跳轉到flushloop中完成)锦担。
????????若flushloop中也沒有什么事情可以做了,InnoDB存儲引擎會切換到suspendloop, 將MasterThread掛起慨削,等待事件的發(fā)生洞渔。若用戶啟用(enable)了InnoDB存儲 引擎,卻沒有使用任何InnoDB存儲引擎的表缚态,那么MasterThread總是處于掛起的狀態(tài)磁椒。
2.5.2 lnnoDB1 .2.x版本之前的Master Thread
? ??????無論何時,InnoDB存儲引擎最大只會刷新100個臟頁到磁盤玫芦, 合并20個插入緩沖浆熔。 如果是在寫入密集的應用 程序中,每秒可能會產(chǎn)生大于100個的臟頁 桥帆,如果是產(chǎn)生大于20個插入緩沖的情況医增,Master Thread似乎會 “ 忙不過來"'或者說它總是做得很慢慎皱。即使磁盤能 在1秒內(nèi)處理多于100個頁的寫入和20個插入緩沖的合并,但是由于hard coding, Master Thread也只會選擇刷新100個臟頁 和合并20個插入緩沖叶骨。同時茫多,當發(fā)生右機需要恢復時,由于很多數(shù)據(jù)還沒有刷新回磁盤邓萨,會導致恢復的時間可能需要很久地梨,尤其是對于insert buff er來說菊卷。
????????InnoDB存儲引擎的開發(fā)團隊參考了Google的patch, 提 供了類似的方法來修正該問題缔恳。因此lnnoDB Plugin (從lnnoDB1.0.x版本開始)提供了參數(shù)innodb_ io _capacity, 用來表示磁盤IO的吞吐量, 默認值 為200洁闰。 對于刷新到磁盤頁的數(shù)量歉甚,會按照innodb_io _ capacity 的百分比來進行控制。 規(guī)則如下:
1.在合并插入緩沖時扑眉, 合并插入緩沖的數(shù)世為innodb_ io _ capacity值的5%;
2.在從緩沖區(qū)刷新臟頁時纸泄,刷新臟頁的數(shù)最為innodb_ io _ capacity。
????????另一個問題是腰素,參數(shù)innodb_max_ dirty pages _pct 默認值的問題聘裁, 在InnoDB 1.0. x版本之前, 該值的默認為90, 意味著臟頁占緩沖池的90%弓千。但是該值“ 太大” 了衡便,因為InnoDB存儲引擎在每秒刷新緩沖池和flush loop時會判斷這個值,如果該值大于innodb _max_ dirty _pages _pct, 才刷新100個臟頁洋访,如果有很大的內(nèi)存镣陕, 或者數(shù)據(jù)庫服務器的壓力很大,這時刷新臟頁的速度反而會降低姻政。 同樣呆抑, 在數(shù)據(jù)庫的恢復階段可能需要更多的時間。
? ??????而從InnoDB1.0.x版本開始汁展,innodb_max_ dirty _pages _pct默認值變?yōu)榱?5, 和Google測試的80比較接近鹊碍。 這樣既可以加快刷新臟頁的頻率, 又能保證了磁盤IO的負載食绿。
? ??????InnoDB 1.0.x版本帶來的另一個參數(shù)是 innodb_adaptive_ flushing (自適應地刷新)妹萨, 該值影響每秒刷新臟頁的數(shù)批。 原來的刷新規(guī)則是: 臟頁在緩沖池所占的比例小于 innodb _max_ dirty _pages _pct時炫欺, 不刷新臟頁乎完; 大于innodb_max_ dirty _pages _pct時, 刷新100個臟頁品洛。 隨著innodb_adaptive_ flushing參數(shù)的引入树姨,InnoDB存儲引擎會通過一個名為buf_ flush _get_ desired_ flush _rate的函數(shù)來判斷需要刷新臟頁最合適的數(shù)量摩桶。 粗略地翻閱源代碼后發(fā)現(xiàn)buf_ flush _get_ desired_ flush _rate通過判斷產(chǎn)生重做日志Credo log)的速度來決定最合適的刷新臟頁數(shù)拯。 因此帽揪, 當臟頁的比例小于innodb_max_ dirty _pages_ pct時硝清, 也會刷新一定量的臟頁。
????????還有一個改變是: 之前每次進行full purge操作時转晰,最多回收20個Undo頁芦拿, 從 InnoDB 1.0.x版本開始引入了參數(shù) innodb_purge_ batch_ size, 該參數(shù)可以控制每次full purge回收的Undo頁的數(shù)妞。 該參數(shù)的默認值為20, 并可以動態(tài)地對其進行修改查邢。
2.5.3 lnnoDB1 .2.x版本的Master Thread
????????在InnoDB1.2.x版本中再次對MasterThread進行了優(yōu)化蔗崎,由此也可以看出MasterThread對性能所起到的關鍵作用。對于刷新臟頁的操作扰藕,從MasterThread 線程分離到一個單獨的PageCleaner Thread, 從而減輕了MasterThread的工作缓苛,同時進一步提高了系統(tǒng)的并發(fā)性。
2.6 lnnoDB關鍵特性
InnoDB存儲引擎的關鍵特性包括:
1.插入緩沖(InsertBuffer)
2.兩次寫(DoubleWrite)
3.自適應哈希索引(AdaptiveHash Index)
4.異步IO(Async IO)
5.刷新鄰接頁(FlushNeighbor Page)
2.6.1 插入緩沖
1. Insert Buffer
? ??????Insert Buffer 可能是 InnoDB 存儲引擎關鍵特性中最令人激動與興奮的一個功能邓深。 不過這個名字可能會讓人認為插入緩沖是緩沖池中的一個組成部分未桥。 其實不然, lnnoDB 緩 沖池中有 Insert Buffer 信息固然不錯芥备, 但是 Insert Buffer 和數(shù)據(jù)頁一樣冬耿, 也是物理頁的一個組成部分。
????????在 InnoDB 存儲引擎中萌壳, 主鍵是行唯一的標識符亦镶。 通常應用程序中行記錄的插入順序是按照主鍵遞增的順序進行。因此讶凉,插入聚集索引一般是順序的染乌,不需要磁盤的隨機讀取。
? ??????但是不可能每張表上只有一個聚集索引懂讯, 更多情況下荷憋, 一張表上有多個非聚集的輔助索引 (secondary index)。 比如褐望, 用戶需要按照 b 這個字段進行查找勒庄, 并且 b 這個字段不是唯一的, 即表是按如下的 SQL 語句定義的:
????????在這樣的情況下產(chǎn)生了了一個非聚集的且不是唯一的索引瘫里。在進行插入操作時实蔽, 數(shù)據(jù)頁的存放還是按主鍵a進行順序存放的,但是對于非聚集索引葉子節(jié)點的插入不再是有序的了谨读,這時就需要離散地訪問非聚集索引頁局装,由于隨機讀取的存在而導致了插入操作性能下降。當然這并不是索引這個B字段上索引的錯誤,而是因為B+樹的特性決定了非聚集索引插入的離散性铐尚。
????????需要注意的是拨脉,在某些情況下,輔助索引的插人依然是順序的宣增,或者說是比較順序的玫膀,比如用戶購買表中的時間字段。在通常情況下爹脾,用戶購買時間是一個輔助索引帖旨,用來根據(jù)時間條件 進行查詢。但是在插入時根據(jù)時間的遞增而插入的灵妨,因此插入也是“較為”順序的解阅。
? ??????InnoDB 存儲引擎開創(chuàng)性地設計了 Insert Buffer, 對于非聚集索引的插入或更新操作,不是每一次直接插入到索引頁中闷串,而是先判斷插入的非聚集索引頁是否在緩沖池中瓮钥,若在筋量,則直接插入烹吵;若不在,則先放入到一個 Insert Buffer 對象中桨武,好似欺騙數(shù)據(jù)庫這個非聚集的索引已經(jīng)插到葉子節(jié)點肋拔,而實際并沒有,只是存放在另 一個位置呀酸。然后再以一定的頻率和悄況進行Insert Buffer 和輔助索引頁子節(jié)點的merge (合并)操作凉蜂,這時通常能將多個插入合并到一個操作中(因為在一個索引頁中)缨该,這就大大提高了對于非聚集索引插入的性能奄妨。
????????然而Insert Buffer 的使用需要同時滿足以下兩個條件:
1.索引是輔助索引(secondary index);
2.索引不是唯一(unique) 的味榛。
????????當滿足以上兩個條件時池充,InnoDB 存儲引擎會使用Insert Buffer, 這樣就能提高插入操作的性能了珍德。不過考慮這樣一種情況:應用程序進行大量的插入操作吊宋,這些都涉及了不唯一的非聚集索引澳盐,也就是使用了Insert Buffer曙寡。若此時MySQL 數(shù)據(jù)庫發(fā)生了宕機倾哺,這時勢必有大掀的Insert Buffer 并沒有合并到實際的非聚集索引中去轧邪。因此這時恢復可能需要很長的時間,在極端情況下甚至需要幾個小時羞海。
????????輔助索引不能是唯一的忌愚,因為在插入緩沖時,數(shù)據(jù)庫并不去查找索引頁來判斷插入的記錄的唯一性却邓。如果去查找肯定又會有離散讀取的情況發(fā)生硕糊,從而導致Insert Buffer失去了意義。
????????用戶可以通過命令SHOW ENGINE INNODB STATUS 來查看插入緩沖的信息
2. Change Buffer
? ??????InnoDB從1.0.x版本開始引入了 Change Buffer, 可將其視為Insert Buffer的升級。從這個版本開始简十,lnnoDB存儲引擎可以對DML操作——INSERT衙耕、DELETE、UPDATE都進行緩沖勺远,他們分別是: Insert Buffer橙喘、Delete Buffer、Purge buffer胶逢。
? ??????當然和之前Insert Buffer 一樣厅瞎,Change Buffer適用的對象依然是非唯一的輔助索引。
? ? ? ?對一條記錄進行 UPDATE操作可能分為兩個過程:
1.將記錄標記為己刪除初坠;
2.真正將記錄刪除和簸。
? ??????因此Delete Buffer對應UPDATE操作的第一個過程,即將記錄標記為刪除碟刺。PurgeBuffer對應UPDATE操作的第二個過程锁保,即將記錄真正的 刪除。同時半沽,lnnoDB存儲引 擎提供了參數(shù)innodb_change_ buffering, 用來開啟各種Buffer的選項爽柒。該參數(shù)可選的值為: inserts、deletes者填、purges浩村、changes、all占哟、none心墅。inserts、deletes榨乎、purges就是前面討論過的三種情況怎燥。changes表示啟用 inserts和deletes, all表示啟用所有,none表示都不啟用 蜜暑。該參數(shù)默認值為all铐姚。
????????從InnoDB 1.2.x版本開始,可以通過參數(shù)innodb_change_ buff er_ max_ size來控制 Change Buffer最大使用內(nèi)存的數(shù)量史煎。
? ??????innodb _change_ buffer_ max_ size值 默認為25, 表示最多使用1/4的緩沖池內(nèi)存空間谦屑。而需要注意的是,該參數(shù)的最大有效值為50篇梭。
3. Insert Buffer 的內(nèi)部實現(xiàn)
????????通過前一個小節(jié)讀者應該已經(jīng)知道了 Insert Buffer 的使用場景氢橙, 即非唯一輔助索引的插入操作。 但是對于 Insert Buffer 具體是什么恬偷, 以及內(nèi)部怎么實現(xiàn)可能依然模糊悍手, 這 正是本節(jié)所要闡述的內(nèi)容。
????????可能令絕大部分用戶感到吃驚的是, Insert Buffer 的數(shù)據(jù)結構是一棵 B+ 樹坦康。 在MySQL 4.1 之前的版本中每張表有一棵 InsertBuffer B+ 樹竣付。 而在現(xiàn)在的版本中, 全局只有一棵 Insert Buffer B+ 樹滞欠, 負責對所有的表的輔助索引進行 Insert Buffer古胆。 而這棵 B+ 樹存放在共享表空間中, 默認也就是 ibdatal 中筛璧。 因此逸绎, 試圖通過獨立表空間 ibd 文件恢復表中數(shù)據(jù)時, 往往會導致 CHECK TABLE 失敗夭谤。 這是因為表的輔助索引中的數(shù)據(jù)可能還在 Insert Buffer 中棺牧, 也就是共享表空間中, 所以通過 ibd 文件進行恢復后朗儒, 還需要進行REPAIR TABLE操作來重建表上所有的輔助索引颊乘。
????????Insert Buffer是一棵 B+ 樹, 因此其也由葉節(jié)點和非葉節(jié)點組成醉锄。 非葉節(jié)點存放的是查詢的 search key (鍵值)乏悄, 其構造如圖 2-3 所示。
? ? ? ? ?search key 一共占用9 個字節(jié)榆鼠, 其中 space 表示待插入記錄所在表的表空間 id, 在 lnnoDB 存儲引擎中纲爸, 每個表有一個唯一的 space id, 可以通過 space id 查詢得知是哪張表亥鸠。 space 占用 4 字節(jié)妆够。 marker 占用1 字節(jié), 它是用來兼容老版本的 Insert Buffer负蚊。 offset 表示頁所在的偏移抵神妹, 占用4 字節(jié)。
? ??????當一個輔助索引要插入到頁(space,offset)時家妆,如果這個頁不在緩存池中鸵荠,那lnnoDB存儲引擎首先根據(jù)上述規(guī)則構造一個searchkey, 接下來查詢InsertBuffer這棵B+樹,然后再將這條記錄插人到InsertBuffer B+樹的葉子節(jié)點中伤极。
? ??????對于插入到 Insert Buffer B+ 樹葉子節(jié)點的記錄(如圖 2-4 所示)蛹找,并不是直接將待插入的記錄插入,而是需要根據(jù)如下的規(guī)則進行構造:
????????space哨坪、 marker庸疾、 page_no 字段和之前非葉節(jié)點中的含義相同,一共占用9字節(jié)当编。第4 個字段 metadata 占用4 字節(jié)届慈,其存儲的內(nèi)容如表 2-2 所示。
? ??????IBUF _REC_OFFSET_COUNT是保存兩個字節(jié)的整數(shù),用來排序每個記錄進入 Insert Buffer的順序金顿。因為從InnoDBl.0.x開始支持ChangeBuffer, 所以這個值同樣記錄進入InsertBuffer的順序臊泌。通過這個順序回放(replay)才能得到記錄的正確值。
????????從InsertBuffer葉子節(jié)點的第5列開始揍拆,就是實際插入記錄的各個字段了渠概。因此較之原插入記錄,InsertBuffer B+樹的葉子節(jié)點記錄需要額外13字節(jié)的開銷嫂拴。
????????因為啟用 Insert Buffer 索引后高氮,輔助索引頁C space, page_ no) 中的記錄可能被插入到 Insert Buffer B+ 樹中,所以為了保證每次 Merge Insert Buffer 頁必須成功顷牌,還需要有一個特殊的頁用來標記每個輔助索引頁的可用空間剪芍。這個頁的類型為InsertBufferBitmap.
? ??????每個InsertBuffer Bitmap頁用來追蹤16384個輔助索引頁,也就是256個區(qū) (Extent)窟蓝。每個InsertBuffer Bitmap頁都在16384個頁的第二個頁中罪裹。關于InsertBuff er Bitmap頁的作用會在下一小節(jié)中詳細介紹。
? ??????每個輔助索引頁在InsertBuffer Bitmap頁中占用4位(bit),由表2-3中的三個部分組成运挫。
4. Merge Insert Buffer
? ??????通過前面的小節(jié)讀者應該已經(jīng)知道了Insert/Change Buffer是一棵B+樹状共。 若需要實現(xiàn)插入記錄的輔助索引頁不在緩沖池中, 那么需要將輔助索引記錄首先插入到這棵B+ 樹中谁帕。 但是Insert Buffer中的記錄何時合并(merge)到真正的輔助索引中呢峡继?這是本小節(jié)需要關注的重點。
概括地說匈挖, MergeInsert Buffer的操作可能發(fā)生在以下幾種情況下:
1 輔助索引頁被讀取到緩沖池時:
2 Insert Buffer Bitmap頁追蹤到該輔助索引頁已無可用空間時碾牌;
3 Master Thread。
? ??????第一種情況為當輔助索引頁被讀取到緩沖池中時儡循, 例如這在執(zhí)行正常的SELECT查詢操作舶吗, 這時需要檢查InsertBuffer Bitmap頁, 然后確認該輔助索引頁是否有記錄存放于InsertBuffer B+樹中择膝。 若有誓琼, 則將Insert Buffer B+樹中該頁的記錄插入到該輔助索引 頁中。 可以看到對該頁多次的記錄操作通過一次操作合并到了原有的輔助索引頁中肴捉, 因此性能會有大幅提高腹侣。
? ? ? ? Insert Buffer Bitmap頁用來追蹤每個輔助索引頁的可用空間, 并至少有1/32頁的空間齿穗。 若插入輔助索引記錄時檢測到插人記錄后可用空間會小于1/32頁傲隶, 則會強制進行一個合并操作, 即強制讀取輔助索引頁缤灵, 將Insert Buffer B+樹中該頁的記錄及待插入的記錄插人到輔助索引頁中伦籍。 這就是上述所說的第二種情況蓝晒。
????????還有一種情況, 之前在分析Master Thread 時曾講到帖鸦, 在Master Thread線程中每秒或每10秒會進行一次MergeInsert Buffer的操作芝薇, 不同之處在千每次進行merge操作的頁的數(shù)量不同。
? ??????在MasterThread中作儿,執(zhí)行merge操作的不止是一個頁洛二,而是根據(jù)srv_innodb_io_capacity的百分比來決定真正要合并多少個附注索引頁。但InnoDB存儲引擎又是根據(jù)怎樣的算法來得知需要合并的輔助索引頁呢攻锰?
? ??????在Insert Buffer B+樹中晾嘶, 輔助索引頁根據(jù)(space, offset)都已排序好, 故可以根據(jù)(space, offset) 的排序順序進行頁的選擇娶吞。 然而垒迂, 對于Insert Buffer頁的選擇, lnnoDB存儲引擎并非采用這個方式妒蛇, 它隨機地選擇Insert Buffer B+樹的一個頁机断, 讀取該頁中的 space及之后所需要數(shù)扯的頁。 該算法在復雜情況下應有更好的公平性绣夺。 同時吏奸, 若進行merge時, 要進行merge的表已經(jīng)被刪除陶耍, 此時可以直接丟棄已經(jīng)被Insert/Change Buffer的數(shù)據(jù)記錄奋蔚。
2.6.2 兩次寫
? ??????如果說Insert Buffer帶給lnnoDB存儲引擎的是性能上的提升, 那么doublewrite(兩 次寫)帶給InnoDB存儲引擎的是數(shù)據(jù)頁的可靠性烈钞。
????????當發(fā)生數(shù)據(jù)庫宕機時泊碑, 可能lnnoDB存儲引擎正在寫入某個頁到表中, 而這個頁只寫了一部分棵磷, 比如16KB 的頁蛾狗, 只寫了前4KB, 之后就發(fā)生了巖機, 這種情況被稱為部分寫失效(partial page write)仪媒。 在InnoDB存儲引擎未使用doublewrite技術前, 曾經(jīng)出現(xiàn)過因為部分寫失效而導致數(shù)據(jù)丟失的情況谢鹊。
? ??????doublewrite由兩部分組成算吩, 一部分是內(nèi)存中的doublewrite buffer, 大小為2MB , 另一部分是物理磁盤上共享表空間中連續(xù)的128個頁, 即2個區(qū)(extent) , 大小同樣為 2MB佃扼。 在對緩沖池的臟頁進行刷新時偎巢, 并不直接寫磁盤, 而是會通過 memcpy函數(shù)將臟頁先復制到內(nèi)存中的doublewritebuffer, 之后通過doublewrite buffer再分兩次兼耀, 每次 1MB 順序地寫入共享表空間的物理磁盤上压昼, 然后馬上調(diào)用fsync函數(shù)求冷, 同步磁盤, 避免緩沖寫帶來的問題窍霞。 在這個過程中匠题, 因為doublewrite頁是連續(xù)的, 因此這個過程是順序?qū)懙牡穑_銷并不是很大韭山。在完成 doublewrite 頁的寫入后,再將 doublewrite buffer 中的頁寫入各個表空間文件中冷溃,此時的寫入則是離散的钱磅。可以通過以下命令觀察到 doublewrite 運行的情況
2.6.3 自適應哈希索引
? ??????哈希(hash) 是一種非乘普恚快的查找方法盖淡, 在一般情況下這種查找的時間復雜度為O(1), 即一般僅需要一次查找就能定位數(shù)據(jù)。而B+樹的查找次數(shù)凿歼, 取決于B+樹的高度禁舷, 在生產(chǎn)環(huán)境中, B+樹的高度一般為3-4層毅往, 故需要3-4次的查詢牵咙。
????????InnoDB存儲引擎會監(jiān)控對表上各索引頁的查詢。如果觀察到建立哈希索引可以帶來速度提升攀唯, 則建立哈希索引洁桌, 稱之為自適應哈希索引(A daptiveH ash Index, AHi)。AHI是通過緩沖池的B+樹頁構造而來侯嘀, 因此建立的速度很快另凌, 而且不需要對整張表構建哈希索引。InnoDB存儲引擎會自動根據(jù)訪問的頻率和模式來自動地為某些熱點頁建立哈希索引戒幔。
????????AHI有一個要求吠谢, 即對這個頁的連續(xù)訪問模式必須是一樣的。例如對于(a, b) 這樣的聯(lián)合索引頁诗茎,其訪問模式可以是以下情況:
WHERE a=xxx
WHERE a=xxx and b=xxx
? ??????訪問模式一樣指的是查詢的條件一樣工坊,若交替進行上述兩種查詢,那么InonDB存 儲引擎不會對該頁構造AHi敢订。此外AHi還有如下的要求:
1.以該模式訪問了100次
2.頁通過該模式訪問了N次王污,其中N=頁中記錄*1/16
????????根據(jù)lnnoDB存儲引擎官方的文檔顯示,啟用AHi后楚午,讀取和寫入速度可以提高2倍昭齐,輔助索引的連接操作性能可以提高5倍。毫無疑問矾柜,AHi是非常好的優(yōu)化模式阱驾,其設計思想是數(shù)據(jù)庫自優(yōu)化的(self-tuning),即無需OBA對數(shù)據(jù)庫進行人為調(diào)整就谜。通過命令SHOWENGINE INNODB STATUS可以看到當前AHi的使用狀況。
2.6.4 異步IO
????????為了提高磁盤操作性能里覆, 當前的數(shù)據(jù)庫 系統(tǒng)都采用異步IO (Asynchronous IO, AIO) 的方式來處理磁盤操作丧荐。lnnoDB存儲引擎亦是如此 。
????????與AIO對應的是 Sync IO, 即每進行一次IO操作租谈, 需要等待此次操作結束才能繼續(xù)接下來的操作篮奄。 但是如果用戶 發(fā)出的是一條索引掃描的查詢, 那么這條SQL 查詢語句可能需要掃描多個索引頁 割去,也就是需要進行多次的IO操作窟却。 在每掃描一個頁并等待其完成后再進行下一次的掃描 ,這是沒有必要的 呻逆。用戶可以在 發(fā)出一個IO請求后立即再 發(fā)出另一個IO請求夸赫, 當全部IO請求發(fā)送完畢后, 等待所有IO操作的完成咖城, 這就是AIO茬腿。
????????AIO的另一個優(yōu)勢是可以進行IO Merge操作, 也就是將多個IO合并為1個IO, 這樣可以提高IOPS的性能宜雀。例如用戶需要訪問頁的(space, page_ no)為:(8, 6)切平、(8, 7), (8, 8)每個頁的大小為 16KB, 那么同步IO需要進行3次IO操作。 而AIO會判斷到這三個頁是連續(xù)的(顯然可以通過(space, page_ no)得知)辐董。 因此AIO底層會發(fā)送一個IO請求悴品, 從(8, 6) 開始 , 讀取48KB的頁 简烘。
2.6.5 刷新鄰接頁
? ??????InnoDB存儲引擎還提供了Flush Neighbor Page (刷新鄰接頁)的特性苔严。其工作原理為:當刷新一個臟頁時,InnoDB存儲引擎會檢測該頁所在區(qū)( extent)的所有頁孤澎,如果是臟頁届氢,那么一起進行刷新。這樣做的好處顯而易見覆旭,通過AIO可以將多個IO寫入操作合并為一個IO操作退子,故該工作機制在傳統(tǒng)機械磁盤下有著顯著的優(yōu)勢。但是需要考 慮到下面兩個問題:
1.是不是可能將不怎么臟的頁進行 了寫入姐扮,而 該頁之后又會很快變成臟頁絮供?
2.固態(tài)硬盤有著較高的IOPS, 是否還需要這個特性?
2.7 啟動茶敏、 關閉與恢復
? ??????InnoDB是MySQL數(shù)據(jù)庫的存儲引擎之一,因此InnoDB存儲引擎的啟動和關閉缚俏, 更準確的是指在MySQL實例的啟動過程中對lnnoDB存儲引擎的處理過程惊搏。
? ??????在關閉時贮乳,參數(shù)innodb_ fast_ shutdown影響著表的存儲引擎為lnnoDB的4行為,該參數(shù)可取值為0恬惯、1向拆、2,默認值為1。
1.0表示在MySQL數(shù)據(jù)庫關閉時酪耳,InnoDB需要完成所有的fullpurge和mergeinsert buffer, 并且將所有的臟頁刷新回磁盤浓恳。這需要一些時間,有時甚至需要幾個小時來完成碗暗。如果在進行InnoDB升級時颈将,必須將這個參數(shù)調(diào)為0,然后再關閉數(shù)據(jù)庫。
2.1是參數(shù)innodb_fast_ shutdown的默認值言疗,表示不需要完成上述的fullpurge和merge insert buff er操作晴圾,但是在緩沖池中的一些數(shù)據(jù)臟頁還是會刷新回磁盤。
3.2表示不完成fullpurge和mergeinsert buffer操作噪奄,也不將緩沖池中的數(shù)據(jù)臟頁寫回磁盤死姚,而是將日志都寫入日志文件。這樣不會有任何事務的丟失勤篮,但是下次MySQL數(shù)據(jù)庫啟動時都毒,會進行恢復操作(recovery)。
????????當正常關閉MySQL數(shù)據(jù)庫時碰缔,下次的啟動應該會非痴司ⅲ“正常”手负。但是如果沒有正常地關閉數(shù)據(jù)庫涤垫,如用kill命令關閉數(shù)據(jù)庫,在MySQL數(shù)據(jù)庫運行中重啟了服務器竟终,或者在關閉數(shù)據(jù)庫時蝠猬,將參數(shù)innodb_ fast_ shutdown設為了2時,下次MySQL數(shù)據(jù)庫啟動時都會對InnoDB存儲引擎的表進行恢復操作统捶。
????????參數(shù)innodb_force_ recovery影響了整個InnoDB存儲引擎恢復的狀況榆芦。該參數(shù)值默認為0,代表當發(fā)生需要恢復時,進行所有的恢復操作喘鸟,當不能進行有效恢復時匆绣,如數(shù)據(jù)頁發(fā)生了corruption,MySQL數(shù)據(jù)庫可能發(fā)生宥機(crash),并把錯誤寫入錯誤日志中去。
????????但是什黑,在某些情況下崎淳,可能并不需要進行完整的恢復操作,因為用戶自已知道怎么進行恢復愕把。比如在對一個表進行altertable操作時發(fā)生意外了拣凹,數(shù)據(jù)庫重啟時會對 InnoDB表進行回滾操作森爽,對于一個大表來說這需要很長時間,可能是幾個小時嚣镜。這時用戶可以自行進行恢復爬迟,如可以把表刪除,從備份中重新導入數(shù)據(jù)到表菊匿,可能這些操作的速度要遠遠快千回滾操作付呕。
????????參數(shù)innodb_ force _recovery還可以設置為6個非零值:1~6。大的數(shù)字表示包含了 前面所有小數(shù)字表示的影響跌捆。具體情況如下:
1.(SRV _FORCE_IGNORE_CORRUPT): 忽略檢查到的corrupt頁徽职。
2. 2(SRV _FORCE_NO _BACKGROUND): 阻止MasterThread線程的運行,如MasterThread線程需要進行fullpurge操作疹蛉,而這會導致crash活箕。
3. 3(SRV _FORCE_NO_TRX_UNDO): 不進行事務的回滾操作。
4.(SRV FORCE_NO_IBUF MERGE):不進行插入緩沖的合并操作可款。
5.5(SRV FORCE_NO_UNDO_LOG_SCAN): 不查看撤銷日志(Undo Log), lnnoDB存儲引擎會將未提交的事務視為已提交育韩。
6.6(SRV FORCE_NO_LOG_REDO): 不進行前滾的操作。
????????需要注意的是闺鲸, 在設置了參數(shù)innodb_force_ recovery 大于0 后筋讨, 用戶可以對表進行select 、create 和drop 操作摸恍, 但insert 悉罕、update 和delete 這類DML 操作是不允許的。
2.8 小結
????????本章對 InnoDB 存儲引擎及其體系結構進行了概述立镶, 先給出了 InnoDB 存儲引擎的歷史壁袄、 InnoDB 存儲引擎的體系結構(包括后臺線程和內(nèi)存結構) ; 之后又詳細介紹了 InnoDB 存儲引擎的關鍵特性媚媒, 這些特性使 InnoDB 存儲引擎變得更具 “魅力 ”嗜逻; 最后介紹了啟動和關閉 MySQL 時一些配置文件參數(shù)對 InnoDB 存儲引擎的影響。
????????通過本章的鋪墊缭召, 讀者在學習后面的內(nèi)容時就會對 InnoDB 引擎理解得更深入和更全面栈顷。 第 3 章開始介紹 MySQL 的文件, 包括 MyS'QL 本身的文件和與 InnoDB 存儲引擎本身有關的文件嵌巷。 之后本書將介紹基千 InnoDB 存儲引擎的表萄凤, 并揭示內(nèi)部的存儲構造。