只要 redo log 和 binlog 保證持久化到磁盤(pán)巍实,就能確保 MySQL 異常重啟后,數(shù)據(jù)可以恢復(fù)哩牍。
binlog 的寫(xiě)入機(jī)制
binlog 的寫(xiě)入邏輯比較簡(jiǎn)單:事務(wù)執(zhí)行過(guò)程中棚潦,先把日志寫(xiě)到 binlog cache,事務(wù)提交的時(shí)候膝昆,再把 binlog cache 寫(xiě)到 binlog 文件中丸边。
系統(tǒng)給 binlog cache 分配了一片內(nèi)存,每個(gè)線(xiàn)程一個(gè)外潜,但是共用同一份 binlog 文件原环。
分兩步
- write,指的就是指把日志寫(xiě)入到文件系統(tǒng)的 page cache处窥,并沒(méi)有把數(shù)據(jù)持久化到磁盤(pán)嘱吗,所以速度比較快。
- fsync滔驾,才是將數(shù)據(jù)持久化到磁盤(pán)的操作谒麦。一般情況下,我們認(rèn)為 fsync 才占磁盤(pán)的 IOPS哆致。
write 和 fsync 的時(shí)機(jī)绕德,是由參數(shù) sync_binlog 控制的:
- sync_binlog=0 的時(shí)候,表示每次提交事務(wù)都只 write摊阀,不 fsync耻蛇;
- sync_binlog=1 的時(shí)候,表示每次提交事務(wù)都會(huì)執(zhí)行 fsync胞此;
- sync_binlog=N(N>1) 的時(shí)候臣咖,表示每次提交事務(wù)都 write,但累積 N 個(gè)事務(wù)后才 fsync漱牵。
因此夺蛇,在出現(xiàn) IO 瓶頸的場(chǎng)景里,將 sync_binlog 設(shè)置成一個(gè)比較大的值酣胀,可以提升性能刁赦。
但是娶聘,將 sync_binlog 設(shè)置為 N,對(duì)應(yīng)的風(fēng)險(xiǎn)是:如果主機(jī)發(fā)生異常重啟甚脉,會(huì)丟失最近 N 個(gè)事務(wù)的 binlog 日志丸升。
redo log 的寫(xiě)入機(jī)制
redo log 可能存在的三種狀態(tài)
- 存在 redo log buffer,是在MySQL進(jìn)程內(nèi)存
- 存在文件系統(tǒng)的 page cache 宦焦,寫(xiě)到磁盤(pán) (write)发钝,但是沒(méi)有持久化(fsync)
- 存在hard disk顿涣,已持久化到磁盤(pán)
為了控制 redo log 的寫(xiě)入策略波闹,InnoDB 提供了 innodb_flush_log_at_trx_commit 參數(shù),它有三種可能取值:
- 設(shè)置為 0 的時(shí)候涛碑,表示每次事務(wù)提交時(shí)都只是把 redo log 留在 redo log buffer 中 ;
- 設(shè)置為 1 的時(shí)候精堕,表示每次事務(wù)提交時(shí)都將 redo log 直接持久化到磁盤(pán);
- 設(shè)置為 2 的時(shí)候蒲障,表示每次事務(wù)提交時(shí)都只是把 redo log 寫(xiě)到 page cache歹篓。
如果把 innodb_flush_log_at_trx_commit 設(shè)置成 1,那么 redo log 在 prepare 階段就要持久化一次揉阎,因?yàn)橛幸粋€(gè)崩潰恢復(fù)邏輯是要依賴(lài)于 prepare 的 redo log庄撮,再加上 binlog 來(lái)恢復(fù)的。
兩種場(chǎng)景會(huì)讓redo log 寫(xiě)入到磁盤(pán)中
- redo log buffer 占用的空間即將達(dá)到 innodb_log_buffer_size 一半的時(shí)候毙籽,后臺(tái)線(xiàn)程會(huì)主動(dòng)寫(xiě)盤(pán)洞斯。
- 并行的事務(wù)提交的時(shí)候,順帶將這個(gè)事務(wù)的 redo log buffer 持久化到磁盤(pán)
在并發(fā)更新場(chǎng)景下坑赡,第一個(gè)事務(wù)寫(xiě)完 redo log buffer 以后烙如,接下來(lái)這個(gè) fsync 越晚調(diào)用,組員可能越多毅否,節(jié)約 IOPS 的效果就越好亚铁。
為了讓一次 fsync 帶的組員更多,MySQL 有一個(gè)很有趣的優(yōu)化:拖時(shí)間螟加。在介紹兩階段提交的時(shí)候徘溢,我曾經(jīng)給你畫(huà)了一個(gè)圖,現(xiàn)在我把它截過(guò)來(lái)捆探。
圖中然爆,我把“寫(xiě) binlog”當(dāng)成一個(gè)動(dòng)作。但實(shí)際上徐许,寫(xiě) binlog 是分成兩步的:
- 先把 binlog 從 binlog cache 中寫(xiě)到磁盤(pán)上的 binlog 文件施蜜;
- 調(diào)用 fsync 持久化。
MySQL 為了讓組提交的效果更好雌隅,把 redo log 做 fsync 的時(shí)間拖到了步驟 1 之后翻默。也就是說(shuō)缸沃,上面的圖變成了這樣:
如果有多個(gè)事務(wù)的 binlog 已經(jīng)寫(xiě)完了,也是一起持久化的修械,這樣也可以減少 IOPS 的消耗趾牧。
如何在IO瓶頸上提高性能?
可以考慮以下三種方法:
- 設(shè)置 binlog_group_commit_sync_delay 和 binlog_group_commit_sync_no_delay_count 參數(shù)肯污,減少 binlog 的寫(xiě)盤(pán)次數(shù)翘单。這個(gè)方法是基于“額外的故意等待”來(lái)實(shí)現(xiàn)的,因此可能會(huì)增加語(yǔ)句的響應(yīng)時(shí)間蹦渣,但沒(méi)有丟失數(shù)據(jù)的風(fēng)險(xiǎn)哄芜。
- 將 sync_binlog 設(shè)置為大于 1 的值(比較常見(jiàn)是 100~1000)。這樣做的風(fēng)險(xiǎn)是柬唯,主機(jī)掉電時(shí)會(huì)丟 binlog 日志认臊。
- 將 innodb_flush_log_at_trx_commit 設(shè)置為 2。這樣做的風(fēng)險(xiǎn)是锄奢,主機(jī)掉電的時(shí)候會(huì)丟數(shù)據(jù)失晴。