1.1線程
InnoDB引擎是多線程模型,因此后臺有很多線程:
- Master Thread:非常核心的一個線程嫉拐,主要將緩存池中的數(shù)據(jù)異步刷新到磁盤哩都,包括臟頁刷新,合并插入緩存(insert buffer)婉徘,undo頁的回收茅逮。
- IO Thread:InnoDB使用大量AIO(Async io)來處理寫請求,而IO Thread主要工作就是負責這些IO請求的回調判哥,InnoDB1.0版本前有4個IO Thread,分別是write,read,insert buffer,log io thread,從1.0.x版本開始碉考,read thread和write thread分別增大到4個塌计,可以根據(jù)參數(shù)innodb_read_io_threads和innodb_write_io_threads來設置讀寫線程,并且讀線程ID要小于寫線程
- Purge Thread:事務提交后侯谁,其所使用的undolog可能不再使用锌仅,因為需要Purge Thread回收已經(jīng)使用的undo頁,1.1版本前墙贱,purge僅在Master Thread中完成热芹,1.1版本后,單獨提出來Purge Thread
- Page Cleaner thread:作用將臟頁刷新放入單獨線程中惨撇。
1.2內存
1.2.1緩沖池
InnoDB基于磁盤儲存伊脓,將數(shù)據(jù)按照頁的方式管理,必然cpu和磁盤速度之間會有差異魁衙,于是緩沖池技術就是應對這種情況而誕生的报腔。
其實緩沖池就是一塊內存區(qū)域,在數(shù)據(jù)庫中進行讀取頁的操作剖淀,首先將磁盤讀到的頁存放到緩沖池中纯蛾,下次再讀取會判斷該頁是否在緩沖池,如果在就稱該頁在緩沖池被命中纵隔,否則讀取磁盤翻诉。
對于數(shù)據(jù)庫頁的修改,首先修改緩沖池的頁捌刮,再以一定的評率刷新到磁盤碰煌,這里是通過一種Checkpoint機制刷新到磁盤中。緩沖池配置通過innodb_buffer_pool_size來設置绅作。
從 InnoDB 1.0.x 版本開始拄查,允許有多個緩沖池實例。每個頁根據(jù)哈希值平均分配到不同緩沖池實例中棚蓄。這樣做的好處是減少數(shù)據(jù)庫內部的資源競爭堕扶,增加數(shù)據(jù)庫的并發(fā)處理能力
1.2.2.LRU List碍脏、Free List、Flush List
數(shù)據(jù)庫中的緩沖池是通過 LRU ( Latest Recent U sed稍算,最近最少使用〉 算法來進行管理的 典尾。即最頻繁使用的頁在 LRU 列表的前端,而最少使用的頁在 LRU 列表的尾端糊探。當緩沖池不能存放新讀取到的頁時 钾埂,將首先釋放LRU 列表中尾端的頁。
在 InnoDB 存儲引擎中科平,緩沖池中頁的大小默認為 16KB褥紫,同樣使用 LRU 算法但是做了優(yōu)化,LRU 列表中還加入了midpoint 位置瞪慧。新讀取到的頁髓考,雖然是最新訪問的頁,但并不是直接放入到 LRU 列表的首部弃酌,而是放入到 LRU 列表的midpoint
氨菇,默認值是5/8,參數(shù) innodb_old_blocks_pct控制,midpoint 之后的列表稱為 old 列表妓湘,之前的列表稱為 new 列表查蓉。
那為什么不采用樸素的 LRU 算法,直接將讀取的頁放入到 LRU 列表的首部呢榜贴?這 是因為若直接將讀取到的頁放入到 LRU 的首部豌研,那么某些 SQL 操作可能會使緩沖池中 的頁被刷新出,從而影響緩沖池 的效率唬党。常見的這類操作為索引或數(shù)據(jù)的掃描操作聂沙。這 類操作需要訪問表中的許多頁,甚至是全部的頁初嘹,而這些頁通常來說又僅在這次查詢操 作中需要及汉,并不是活躍的熱點數(shù)據(jù)。如果頁被放人 LRU 列表的首部屯烦,那么非晨浪妫可能將所需要的熱點數(shù)據(jù)頁從 LRU 列表中移除,而在下一次需要讀取該頁時驻龟,InnoDB 存儲引擎需要再次訪問磁盤温眉。
為了解決這個 問題,lnnoDB 存儲引擎引人了另一個參數(shù)來進一步管理 LRU 列表翁狐, 這個參數(shù)是 innodb_old_blocks_time 类溢,用于表示頁讀取到 mid 位置后需要等待多久才會被 加人到 LRU 列表的熱端。
需要注意的是因為緩沖池中的頁還可能會被分配給自適應哈希索引、Lock 信息闯冷、Insert Buffer 等頁砂心,而這部分頁不需要 LRU 算法進行維護,因此不存在于 LRU 列表中蛇耀”绲可以結合上面的圖查看。
前面提到的緩沖池命中率也就是Buffer pool hit rate纺涤,如果這個值太低译暂,一般都是90%以上,就需要考慮下是否是全表掃描引起的LRU列表被污染的問題撩炊,比如唯一索引修改為普通索引外永,導致sql語句走了全表掃描。
innodb從1.0.x版本開始支持壓縮頁的功能拧咳,即將原本16KB的頁壓縮為1,2,4,8KB伯顶,對于非16KB的頁,是通過unzip_LRU列表管理呛踊,要注意的是LRU中的頁包含了unzip_LRU列表中頁。
在 LRU 列表中的頁被修改后啦撮,稱該頁為臟頁 ( dirty page )谭网,即緩沖池中的頁和磁盤 上的頁的數(shù)據(jù)產(chǎn)生了不一致。這時數(shù)據(jù)庫會通過 CHECKPOINT 機制將臟頁刷新回磁盤赃春, 而 Flush 列表中的頁即為臟頁列表愉择。需要注意的是,臟頁既存在于 LRU 列表中织中,也存在于 Flush 列表中锥涕。LRU 列表用來管理緩沖池中頁的可用性,F(xiàn)lush 列表用來管理將頁刷新回磁盤狭吼,二者互不影響层坠。
1.2.3redo log buffer(重做日志緩沖)
InnoDB 存儲引擎的內存區(qū)域除了有緩沖池外,還有redo log buffer刁笙。lnnoDB 存儲引擎首先將重做日志信息先放人到這個緩沖區(qū)破花,然后刷新到重做日志文件。重做日志緩沖一般不需要設置得很大疲吸,因為一般情況下每一秒鐘會將重做日志緩沖刷新到日志文件座每,因此用戶只需要保證每秒產(chǎn)生的事務量在這個緩沖大小之內即可 。該值可由配置參數(shù) innodb_log_buffer_size 控制摘悴。
redo log buffer會在下面三種情況被刷新到磁盤的重做日志文件中:
- Master Thread 每一秒將重做日志緩沖刷新到重做日志文件
- 每個事務提交時會將重做日志緩沖刷新到重做日志文件
- 當重做日志緩沖池剩余空間小于 1/2 時峭梳,重做日志緩沖刷新到重做日志文件
1.3Checkpoint
倘若每次一個頁發(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 持久性〉 的要求待榔。
我們需要明白如下幾點:
- 緩沖池不可能緩存數(shù)據(jù)庫中所有數(shù)據(jù),也許數(shù)據(jù)庫在重啟后的最開始一段時間能做到流济。
- 重做日志不可以無限增大
因此 Checkpoint (檢查點〉 技術的目的是解決以下幾個問題 - 縮短數(shù)據(jù)庫的恢復時間
- 緩沖池不夠用時锐锣,將臟頁刷新到磁盤
- 重做日志不可用時,刷新臟頁
當數(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 扮超,將緩沖池中的頁至少刷新到當前重做日志的位置。
需要注意的關于redo log有這樣幾點
- 日志緩沖刷新到磁盤,即使這個事務還沒有提交(總是)出刷,這也是為什么很大的事務的提交時間也是很短的璧疗。
- 合并插入緩沖(可能)(這也導致了mysql server層面的binlog機制和這個redo log可能數(shù)據(jù)會對不上,即使是二階段提交協(xié)議下)
2.innodb更多特性
- Change Buffer(之前是Insert Buffer)
- 兩次寫 ( Double Write )
- 自適應哈希索引 ( Adaptive Hash Index )
- 異步 IO (Async IO)
- 刷新鄰接頁 ( Flush Neighbor Page)
2.1Change Buffer
lnnoDB 存儲引擎可以對DML 操作:INSERT馁龟、DELETE 崩侠、UPDATE 都進行緩沖,他們分別是:Insert Buffer坷檩、Delete Buffer却音、Purge Buffer。
關于Delete Buffer矢炼、Purge Buffer這里提一句:當你在刪除某條記錄時系瓢,分為兩步:第一步是Delete Buffer將記錄標記為刪除,Purge Buffer將記錄真正刪除句灌。
Change Buffer 是干什么的了夷陋?Change Buffer是一種應用在非唯一普通索引頁(non-unique secondary index page)不在緩沖池中,對頁進行了寫操作胰锌,并不會立刻將磁盤頁加載到緩沖池骗绕,而僅僅記錄緩沖變更(buffer changes),等未來數(shù)據(jù)被讀取時资昧,再將數(shù)據(jù)合并(merge)恢復到緩沖池中的技術酬土。寫緩沖的目的是降低寫操作的磁盤IO,提升數(shù)據(jù)庫性能榛搔。
那么還有一個問題:為何只適合非唯一普通索引诺凡?
很簡單东揣,如果索引設置了唯一(unique)屬性践惑,在進行修改操作時,InnoDB必須進行唯一性檢查嘶卧。也就是說尔觉,索引頁即使不在緩沖池,磁盤上的頁讀取無法避免芥吟。
所以不適合開啟innodb的寫緩沖機制的場景是:
- 數(shù)據(jù)庫都是唯一索引
- 寫入一個數(shù)據(jù)侦铜,需要馬上讀取它
相反適合開啟Change buffer的場景是: - 數(shù)據(jù)庫大部分索引是非唯一索引
- 寫多讀少(比如賬單流水,日志記錄)
2.2Double Write
關于IO的最小單位:
- 數(shù)據(jù)庫IO的最小單位是16K(MySQL默認钟鸵,oracle是8K)
- 文件系統(tǒng)IO的最小單位是4K(也有1K的)
- 盤IO的最小單位是512字節(jié)
doublewrite:兩次寫提高innodb的可靠性钉稍,用來解決部分寫失敗(partial page write頁斷裂)。
比如這樣的場景:當發(fā)生數(shù)據(jù)庫著機時棺耍,可能lnnoDB 存儲引擎正在寫人某個頁到表中贡未,而這個頁只寫了一部分,比如 16KB 的頁,只寫了前 4KB 俊卤,之后就發(fā)生了宕機嫩挤,這種情況被稱為部分寫失效 (partial page write )(redo log重做日志記錄的是對頁的物理修改,如果頁本身已經(jīng)損壞消恍,重做日志也無能為力)
doublewrite由兩部分組成岂昭,一部分為內存中的doublewrite buffer,其大小為2MB狠怨,另一部分是磁盤上共享表空間(ibdata x)中連續(xù)的128個頁约啊,即2個區(qū)(extent),大小也是2M取董。
- 當一系列機制觸發(fā)數(shù)據(jù)緩沖池中的臟頁刷新時棍苹,并不直接寫入磁盤數(shù)據(jù)文件中,而是先拷貝至內存中的doublewrite buffer中茵汰;
- 接著從兩次寫緩沖區(qū)分兩次寫入磁盤共享表空間中(連續(xù)存儲枢里,順序寫,性能很高)蹂午,每次寫1MB栏豺;
-
待第二步完成后,再將doublewrite buffer中的臟頁數(shù)據(jù)寫入實際的各個表空間文件(離散寫)豆胸;(臟頁數(shù)據(jù)固化后奥洼,即進行標記對應doublewrite數(shù)據(jù)可覆蓋)
如果操作系統(tǒng)在將頁寫入磁盤的過程中發(fā)生崩潰,在恢復過程中晚胡,innodb存儲引擎可以從共享表空間的doublewrite中找到該頁的一個最近的副本灵奖,將其復制到表空間文件,再應用redo log估盘,就完成了恢復過程瓷患。因為有副本所以也不擔心表空間中數(shù)據(jù)頁是否損壞。(redolog寫入的單位就是512字節(jié)遣妥,也就是磁盤IO的最小單位擅编,所以無所謂數(shù)據(jù)損壞,所以不需要doublewrite的支持)
2.3自適應哈希索引
哈希是一種非丑锊龋快的查找方法爱态,在一般情況下這種查找的時 間復雜度為 0(1), 一般僅需要一次查找就能定位數(shù)據(jù) 。而 B+ 樹的查找次數(shù)境钟,取決于 B+ 樹的高度锦担,在生產(chǎn)環(huán)境中,B+樹的高度一般為 3慨削、4 層洞渔,故需要 3鱼的、4 次的查詢。
InnoDB 存儲引擎會監(jiān)控對表上各索引頁的查詢痘煤。如果觀察到建立哈希索引可以帶 來速度提升 凑阶,則建立哈希索引,稱之為自適應哈希索引 (Adaptive Hash Index, AHi)衷快。
2.4異步IO
如其明宙橱,這里不多說。
2.5刷新鄰接頁
innoDB 存儲引擎還提供了Flush Neighbor Page (刷新鄰接頁〉 的特性蘸拔。其工作原理刷新一個臟頁時师郑,InnoDB 存儲引擎會檢測該頁所在區(qū)的所有頁,如果是臟頁调窍,那么一起進行刷新宝冕。這種做法在機械硬盤時代是由顯著的優(yōu)勢的。但也會出現(xiàn)下面問題:
- 會將不怎么臟的頁頻繁寫入邓萨,該頁又會很快變成臟頁
- 固態(tài)硬盤有著很高的io能力地梨,這個參數(shù)意義不大
本文主要是自己看書和專欄文章來寫,其實更多算是一種筆記缔恳。以后方便記憶和復習宝剖。