binlog的寫入機(jī)制
事務(wù)執(zhí)行過程中,先把日志寫到 binlog cache念秧,事務(wù)提交的時候,再把 binlog cache?
寫到 binlog 文件中。
系統(tǒng)給 binlog cache 分配了一片內(nèi)存胡诗,每個線程一個,參數(shù) binlog_cache_size 用于控
制單個線程內(nèi) binlog cache 所占內(nèi)存的大小。如果超過了這個參數(shù)規(guī)定的大小煌恢,就要暫
存到磁盤骇陈。
事務(wù)提交的時候,執(zhí)行器把 binlog cache 里的完整事務(wù)寫入到 binlog 中瑰抵,并清空
binlog cache你雌。
每個線程有自己 binlog cache,但是共用同一份 binlog 文件二汛。
write過程:
指的就是指把日志寫入到文件系統(tǒng)的 page cache婿崭,并沒有把數(shù)據(jù)持久化
到磁盤,所以速度比較快习贫。
fsync過程:
才是將數(shù)據(jù)持久化到磁盤的操作逛球。一般情況下,我們認(rèn)為 fsync 才占磁
盤的 IOPS苫昌。
write 和 fsync 的時機(jī)颤绕,是由參數(shù) sync_binlog 控制的:
1. sync_binlog=0 的時候,表示每次提交事務(wù)都只 write祟身,不 fsync奥务;
2. sync_binlog=1 的時候,表示每次提交事務(wù)都會執(zhí)行 fsync袜硫;
3. sync_binlog=N(N>1) 的時候氯葬,表示每次提交事務(wù)都 write,但累積 N 個事務(wù)后才
fsync婉陷。
因此帚称,在出現(xiàn) IO 瓶頸的場景里,將 sync_binlog 設(shè)置成一個比較大的值秽澳,可以提升性
能闯睹。在實際的業(yè)務(wù)場景中,考慮到丟失日志量的可控性担神,一般不建議將這個參數(shù)設(shè)成 0楼吃,
比較常見的是將其設(shè)置為 100~1000 中的某個數(shù)值。
但是妄讯,將 sync_binlog 設(shè)置為 N孩锡,對應(yīng)的風(fēng)險是:如果主機(jī)發(fā)生異常重啟,會丟失最近 N
個事務(wù)的 binlog 日志亥贸。
binlog的日志格式
1.? statement
記錄到 binlog 里的是語句原文躬窜,因此可能會出現(xiàn)這樣一種情
況:在主庫執(zhí)行這條 SQL 語句的時候,用的是索引 a炕置;而在備庫執(zhí)行這條 SQL 語句的時
候荣挨,卻使用了索引 t_modified溜族。出現(xiàn)同樣一條sql語句,選擇索引不一致的情況垦沉。
2.?row
3.?mixed(前兩種的混合)
mixed 格式的 binlog 現(xiàn)在已經(jīng)用得不多
redo log 的寫入機(jī)制
事務(wù)在執(zhí)行過程中,生成的 redo log 是要先寫到 redo log buffer 的仍劈。
redo log buffer 里面的內(nèi)容厕倍,不需要每次生成后都要直接持久化到磁盤。
如果事務(wù)執(zhí)行期間 MySQL 發(fā)生異常重啟贩疙,那這部分日志就丟了讹弯。由于事務(wù)并沒有提交,
所以這時日志丟了也不會有損失这溅。
事務(wù)還沒提交的時候组民,redo log buffer 中的部分日志有可能被持久化到磁盤。
redo log 可能存在三種狀態(tài):
1. 存在 redo log buffer 中悲靴,物理上是在 MySQL 進(jìn)程內(nèi)存中臭胜,就是圖中的紅色部分;
2. 寫到磁盤 (write)癞尚,但是沒有持久化(fsync)耸三,物理上是在文件系統(tǒng)的 page cache 里
面,也就是圖中的黃色部分浇揩;
3. 持久化到磁盤仪壮,對應(yīng)的是 hard disk,也就是圖中的綠色部分胳徽。
日志寫到 redo log buffer 是很快的积锅,wirte 到 page cache 也差不多,但是持久化到磁盤
的速度就慢多了养盗。
為了控制 redo log 的寫入策略缚陷,InnoDB 提供了 innodb_flush_log_at_trx_commit 參
數(shù),它有三種可能取值:
1. 設(shè)置為 0 的時候爪瓜,表示每次事務(wù)提交時都只是把 redo log 留在 redo log buffer 中 ;
2. 設(shè)置為 1 的時候蹬跃,表示每次事務(wù)提交時都將 redo log 直接持久化到磁盤;
3. 設(shè)置為 2 的時候铆铆,表示每次事務(wù)提交時都只是把 redo log 寫到 page cache蝶缀。
InnoDB 有一個后臺線程,每隔 1 秒薄货,就會把 redo log buffer 中的日志翁都,調(diào)用 write 寫
到文件系統(tǒng)的 page cache,然后調(diào)用 fsync 持久化到磁盤谅猾。
事務(wù)執(zhí)行中間過程的 redo log 也是直接寫在 redo log buffer 中的柄慰,這些 redo
log 也會被后臺線程一起持久化到磁盤鳍悠。也就是說,一個沒有提交的事務(wù)的 redo log坐搔,也
是可能已經(jīng)持久化到磁盤的藏研。
實際上,除了后臺線程每秒一次的輪詢操作外概行,還有兩種場景會讓一個沒有提交的事務(wù)的
redo log 寫入到磁盤中蠢挡。
1.?redo log buffer 占用的空間即將達(dá)到 innodb_log_buffer_size 一半的時
候,后臺線程會主動寫盤凳忙。注意业踏,由于這個事務(wù)并沒有提交,所以這個寫盤動作只是
write涧卵,而沒有調(diào)用 fsync勤家,也就是只留在了文件系統(tǒng)的 page cache。
2. 另一種是柳恐,并行的事務(wù)提交的時候伐脖,順帶將這個事務(wù)的 redo log buffer 持久化到磁
盤。
通常我們說 MySQL 的“雙 1”配置乐设,指的就是 sync_binlog 和innodb_flush_log_at_trx_commit?
都設(shè)置成 1晓殊。
也就是說,一個事務(wù)完整提交前伤提,需要等待兩次刷盤巫俺,一次是 redo log(prepare 階段),
一次是 binlog肿男。
這意味著我從 MySQL 看到的 TPS 是每秒兩萬的話介汹,每秒就會寫四萬次磁盤。
但是舶沛,我用工具測試出來嘹承,磁盤能力也就兩萬左右,怎么能實現(xiàn)兩萬的
TPS如庭?
組提交(group commit)機(jī)制
WAL 機(jī)制是減少磁盤寫叹卷,可是每次提交事務(wù)都要寫 redo log和 binlog,這磁盤讀寫次數(shù)
也沒變少呀坪它?
??WAL 機(jī)制主要得益于兩個方面:
1. redo log 和 binlog 都是順序?qū)懼柚瘢疟P的順序?qū)懕入S機(jī)寫速度要快;
2. 組提交機(jī)制往毡,可以大幅度降低磁盤的 IOPS 消耗蒙揣。
如果你的 MySQL 現(xiàn)在出現(xiàn)了性能瓶頸,而且瓶頸在 IO 上开瞭,可以通過哪些方法來提升性能呢懒震?
針對這個問題罩息,可以考慮以下三種方法:
1.?設(shè)置 binlog_group_commit_sync_delay 和
binlog_group_commit_sync_no_delay_count 參數(shù),減少 binlog 的寫盤次數(shù)个扰。
2.?將 sync_binlog 設(shè)置為大于 1 的值(比較常見是 100~1000)瓷炮。這樣做的風(fēng)險是,主
機(jī)掉電時會丟 binlog 日志递宅。
3. 將 innodb_flush_log_at_trx_commit 設(shè)置為 2崭别。這樣做的風(fēng)險是,主機(jī)掉電的時候會
丟數(shù)據(jù)恐锣。
不建議你把 innodb_flush_log_at_trx_commit 設(shè)置成 0。因為把這個參數(shù)設(shè)置成 0舞痰,
表示 redo log 只保存在內(nèi)存中土榴,這樣的話 MySQL 本身異常重啟也會丟數(shù)據(jù),風(fēng)險太
大响牛。而 redo log 寫到文件系統(tǒng)的 page cache 的速度也是很快的玷禽,所以將這個參數(shù)設(shè)置
成 2 跟設(shè)置成 0 其實性能差不多,但這樣做 MySQL 異常重啟時就不會丟數(shù)據(jù)了呀打,相比之
下風(fēng)險會更小矢赁。
一般情況下,把生產(chǎn)庫改成“非雙 1”配置贬丛,是設(shè)置
innodb_flush_logs_at_trx_commit=2撩银、sync_binlog=1000
在什么時候會把線上生產(chǎn)庫設(shè)置成“非雙 1”?
場景:
1. 業(yè)務(wù)高峰期豺憔。一般如果有預(yù)知的高峰期额获,DBA 會有預(yù)案,把主庫設(shè)置成“非雙 1”恭应。
2.?備庫延遲抄邀,為了讓備庫盡快趕上主庫。@永恒記憶和 @Second Sight 提到了這個場
景昼榛。
3.?用備份恢復(fù)主庫的副本境肾,應(yīng)用 binlog 的過程。
4.?批量導(dǎo)入數(shù)據(jù)的時候胆屿。