WAL機(jī)制
? ???在Mysql中經(jīng)常會(huì)聽(tīng)到WAL機(jī)制(Write-Ahead-Logging),主要講了數(shù)據(jù)在操作的時(shí)候先進(jìn)行寫(xiě)日志怔鳖,然后再將數(shù)據(jù)寫(xiě)入磁盤(pán)的過(guò)程。之前提到過(guò)Mysql中的兩個(gè)重要的日志文件redo log和bin log颜武。
? ??? redo log:Mysql分配給redoLog固定大小區(qū)域記錄日志意狠,該日志采用循環(huán)寫(xiě)從頭寫(xiě)到尾然后在從頭,同時(shí)還有一個(gè)記錄擦除日志位置的標(biāo)記checkpoint地技。當(dāng)寫(xiě)入點(diǎn)和checkpoint在同一個(gè)位置袁翁,則停止寫(xiě)日志柴底。如果數(shù)據(jù)庫(kù)異常后婿脸,提交后的記錄不會(huì)丟失稱(chēng)為crash-safe
? ???bin log:稱(chēng)為歸檔日志粱胜,是追加寫(xiě)不會(huì)覆蓋之前的日志。所以可以根據(jù)binlog日志可以完成主從備份狐树、數(shù)據(jù)訂閱焙压、恢復(fù)數(shù)據(jù)等工作。
? ???之前也提到mysql執(zhí)行一條update語(yǔ)句遵從兩階段提交過(guò)程抑钟,整個(gè)流程如下
1.先通過(guò)引擎找到對(duì)應(yīng)的行數(shù)據(jù)
2.對(duì)行數(shù)據(jù)進(jìn)行修改并調(diào)用引擎接口修改這條數(shù)據(jù)
3.redo log記錄更新流程此時(shí)處于prepare狀態(tài)涯曲,告知mysql執(zhí)行器完成操作
4.執(zhí)行器生成binlog并寫(xiě)入磁盤(pán)
5.執(zhí)行器調(diào)用引擎提交事務(wù)接口,redo log狀態(tài)變?yōu)閏ommit狀態(tài)完成整個(gè)更新操作在塔。
Redo log進(jìn)行flush操作
? ???Mysql在記錄redo log的時(shí)候會(huì)先將數(shù)據(jù)寫(xiě)入到內(nèi)存中幻件,然后通過(guò)flush將內(nèi)存中的數(shù)據(jù)寫(xiě)入磁盤(pán)中。再次期間會(huì)產(chǎn)生臟數(shù)據(jù)頁(yè)導(dǎo)致內(nèi)存和磁盤(pán)的數(shù)據(jù)不一致蛔溃。這時(shí)候mysql就需要刷臟數(shù)據(jù)頁(yè)绰沥。
Mysql什么時(shí)候執(zhí)行flush操作?
1.當(dāng)記錄redo log的內(nèi)存滿了贺待,會(huì)停止寫(xiě)入redo log操作徽曲。
2.寫(xiě)入日志太多,發(fā)現(xiàn)分配的內(nèi)存不夠麸塞,這時(shí)候需要淘汰一部分?jǐn)?shù)據(jù)頁(yè)秃臣。
3.mysql空閑時(shí)會(huì)進(jìn)行flush操作
? ???在flush過(guò)程中如果存在讀入的數(shù)據(jù)頁(yè)沒(méi)有內(nèi)存的時(shí)候,需要到緩沖池中申請(qǐng)數(shù)據(jù)頁(yè)哪工。當(dāng)數(shù)據(jù)頁(yè)不足首先將最久不使用的數(shù)據(jù)從內(nèi)存中淘汰奥此,如果是臟數(shù)據(jù)頁(yè)還得先進(jìn)行刷盤(pán)才能復(fù)用弧哎。可是在刷臟頁(yè)過(guò)程中得院,若淘汰的數(shù)據(jù)頁(yè)太多或者日志寫(xiě)滿會(huì)導(dǎo)致mysql性能降低傻铣。所以InnoDB會(huì)有刷臟頁(yè)的控制策略
InnoDB刷盤(pán)影響因素:臟頁(yè)的比重、redo log寫(xiě)盤(pán)速度
? ???InnoDB_max_dirty_pages_pct臟頁(yè)比重上線默認(rèn)為75%祥绞,合理的設(shè)置innoDb_io_capacity的值不要超過(guò)上限非洲。
? ???同時(shí)在刷盤(pán)的過(guò)程中,mysql會(huì)將旁邊的數(shù)據(jù)頁(yè)也會(huì)刷入蜕径。InnoDB通過(guò)innodb_flush_neighbors參數(shù)用來(lái)控制這個(gè)行為两踏。1表示會(huì)刷入相鄰的數(shù)據(jù)2.表示只刷自己。如果磁盤(pán)寫(xiě)性能高最好設(shè)置為0兜喻,減少sql響應(yīng)時(shí)間梦染。
? ???如果mysql在執(zhí)行操作過(guò)程中出現(xiàn)抖動(dòng)可能由于mysql在flush操作。
? ???Mysql在執(zhí)行Binlog和redoLog過(guò)程有時(shí)什么樣子呢朴皆?
? ???binlog寫(xiě)入機(jī)制
? ???執(zhí)行事務(wù)過(guò)程帕识,先將日志寫(xiě)入binlog cache,事務(wù)提交時(shí)再將cache寫(xiě)入到binlog中遂铡。對(duì)于cache系統(tǒng)會(huì)分配一片內(nèi)存肮疗,每個(gè)線程一個(gè),參數(shù)binlog_cache_size用于控制單個(gè)線程內(nèi)binlog cache所占內(nèi)存的大小扒接。超過(guò)這個(gè)參數(shù)則暫存在磁盤(pán)中伪货。
事務(wù)提交時(shí),執(zhí)行器將binlog cache完整事務(wù)寫(xiě)入binlog中钾怔,并清空binlog cache碱呼。
整個(gè)寫(xiě)入過(guò)程分為兩步 write和fsync
write:指把日志寫(xiě)入到文件系統(tǒng)page cache,并沒(méi)有寫(xiě)入磁盤(pán)中宗侦,速度快
fsync:將數(shù)據(jù)持久化到磁盤(pán)愚臀,該過(guò)程占用磁盤(pán)IOPS。
sync_binlog參數(shù)
等于0:表示每次提交事務(wù)只write不fsync
等于1:表示每次提交事務(wù)都執(zhí)行fsync
等于n:表示事務(wù)在write后矾利,會(huì)累積N個(gè)事務(wù)后才fsync姑裂。
? ???為了提升性能,實(shí)際場(chǎng)景考慮丟失日志量的可控性會(huì)設(shè)置到100-1000之間的值梦皮。風(fēng)險(xiǎn):主機(jī)發(fā)生異常重啟炭分,會(huì)丟失N個(gè)事務(wù)binlog日志。
? ???redoLog寫(xiě)入機(jī)制
? ???首先確定redo log在寫(xiě)入的時(shí)經(jīng)過(guò)redo log buffer剑肯、FSPage cache捧毛、hard disk
redo log buffer:物理上是Mysql進(jìn)程內(nèi)存
FSPage cache:寫(xiě)入磁盤(pán)但沒(méi)有持久化,物理上是在文件系統(tǒng)page cache中hard disk 持久化到磁盤(pán)
? ??? 為了控制redo log寫(xiě)入策略,innodb_flush_log_at_trx_commit參數(shù)
等于0呀忧,表示事務(wù)提交只將redo log留在redo log buffer
等于1师痕,表示每次事務(wù)提交都將redo log持久化到磁盤(pán)中
等于2,每次事務(wù)提交只把redo log寫(xiě)入page cache中而账。
? ???InnoDB后臺(tái)線程胰坟,每秒把redo logbuffer中日志,調(diào)用write寫(xiě)入到文件系統(tǒng)page cache泞辐,然后調(diào)用fsync持久化到磁盤(pán)笔横。
? ??? 所以在事務(wù)執(zhí)行過(guò)程中redo log直接寫(xiě)入redo log buffer,然后隔一秒調(diào)用write寫(xiě)入到page cache中咐吼,最后持久化到磁盤(pán)中吹缔。所以說(shuō)沒(méi)有提交事務(wù)的redo log也有可能持久化到磁盤(pán)中。
? ???還有場(chǎng)景會(huì)將redo log buffer寫(xiě)入磁盤(pán)
1.redo log buffer占用空間達(dá)到innoDB_log_buffer_size一半锯茄,后臺(tái)線程主動(dòng)寫(xiě)入
2.并行事務(wù)提交時(shí)順帶將這個(gè)redo log持久化到磁盤(pán)中厢塘。
? ???innoDB_flush_log_at_trx_commit設(shè)置為1,事務(wù)B將redo log buffer里日志全部持久化到磁盤(pán)肌幽。會(huì)帶上事務(wù)A里的日志晚碾。
? ???雙1配置:sync_binlog和innoDb_flush_log_at_trx_commit都設(shè)置為1,一個(gè)事務(wù)完整提交需要兩次刷盤(pán)喂急,一次redo log(prepare)一次binlog格嘁。
? ???但是Mysql測(cè)試TPS每秒2萬(wàn),刷盤(pán)得四萬(wàn)次煮岁?
? ???日志邏輯序列號(hào)(LSN)用來(lái)對(duì)應(yīng)redo log寫(xiě)入點(diǎn)讥蔽。LSN記錄InnoDB的數(shù)據(jù)頁(yè)涣易,保證數(shù)據(jù)頁(yè)不會(huì)被多次執(zhí)行重復(fù)redo log画机。
redo log組提交:
? ???比如有三個(gè)事務(wù),先到達(dá)的會(huì)選為組leader新症。當(dāng)組成員達(dá)到3個(gè)步氏,事務(wù)1會(huì)帶著3個(gè)事務(wù)一起持久化,事務(wù)2和3直接返回徒爹。
? ???所以一次組提交過(guò)程組成員越多荚醒,fsync越晚調(diào)用,節(jié)約IOPS效果越好隆嗅。
mysql有兩個(gè)參數(shù)
binlog_group_commit_sync_delay:表示延遲多久調(diào)用fsync
binlog_grouo_commit_syn_no_delay_count:表示事務(wù)累積多少次調(diào)用fsync
這兩個(gè)條件只要滿足其一就會(huì)調(diào)用fsync界阁。
? ???mysql出現(xiàn)性能瓶頸,瓶頸為IO上胖喳,可以有哪些方式提升泡躯?
1.設(shè)置binlog_group_commit_sync_delay和binlog_grouo_commit_syn_no_delay_count減少binlog寫(xiě)盤(pán)次數(shù),增加語(yǔ)句響應(yīng)時(shí)間,不會(huì)丟數(shù)據(jù)
2.sync_binlog設(shè)置大于1(常見(jiàn)為100-1000)但有風(fēng)險(xiǎn)主機(jī)異常會(huì)丟失binlog日志较剃。
3.innoDb_flush_log_at_trx_commit設(shè)置2咕别,主機(jī)掉電會(huì)丟數(shù)據(jù) innoDb_flush_log_at_trx_commit為0時(shí),如果mysql本身異常重啟也會(huì)丟數(shù)據(jù)写穴。
最終crash-safe保證
? ???客戶端收到事務(wù)成功惰拱,事務(wù)一定持久化了
? ???客戶端收到事務(wù)失敗,則一定失敗
? ???客戶端收到異常啊送,需要重連數(shù)據(jù)庫(kù)連接偿短,數(shù)據(jù)只要保證內(nèi)部(數(shù)據(jù)和日志、主庫(kù)和備庫(kù)一致)就可以