MySQL
的两阶段提交 + WAL
技术(Write-Ahead Logging
,先写日志再写盘),这两个联合在一起保障了数据不会失落。
binlog 的写入机制
1、事务执行过程中,先把日志写到 binlog cache,事务提交的时候,再把 binlog cache 写到 binlog 文件中。
2、一个事务的 binlog 是不能被拆开的,因而不管这个事务多大,也要确保一次性写入。
3、零碎给每个线程调配了一片 binlog cache 内存,参数 binlog_cache_size 用于管制单个线程内 binlog cache 所占内存的大小。如果超过了这个大小,就要暂存到磁盘。
4、事务提交的时候,执行器把 binlog cache 里的残缺事务写入到 binlog 中,并清空 binlog cache。
5、每个线程有本人 binlog cache,然而共用同一份 binlog 文件。
6、下图中的 write, 将日志写入到文件系统的 page cache,在内存中,所以速度很快;fsync 将数据长久化到磁盘,占用磁盘 IOPS。
参数 sync_binlog
sync_binlog = 0, 示意每次提交事务都只 write,不 fsync。(不举荐)
sync_binlog = 1, 示意每次提交事务都会执行 fsync(这个配置是最平安的,不会丢binlog
日志)
sync_binlog = N(N > 1), 示意每次提交事务都只 write,但累计 N 个事务后才 fsync。
IO 瓶颈的场景中,将 sync_binlog 设置成一个较大的值,能够晋升性能。然而对应危险就是,如果主机异样重启,会失落最近 N 个事务的 binlog 日志。
redo log 的写入机制
事务在执行过程中,生成的 redo log 是要先写到 redo log buffer 的。
其次,写入(write
)文件系统的 page cache
。
最初,长久化(fsync
)到磁盘 disk
innodb_flush_log_at_trx_commit 参数管制 redo log 的写入策略
innodb_flush_log_at_trx_commit = 0,示意每次提交事务都只是把 redo log 留在 redo log buffer 中,redo log buffer 是所有线程共用的。(上图红色局部)(危险大,期待每秒
write
+fsync
到disk
)innodb_flush_log_at_trx_commit = 1,示意每次提交事务时都将 redo log 间接长久化到磁盘。(上图绿色局部)(最平安)
innodb_flush_log_at_trx_commit = 2,示意每次提交事务时都只是把 redo log 写到 page cache。(上图黄色局部)
每秒刷盘机制
InnoDB 的后盾线程每隔 1 秒。就会把 redo log buffer 中的日志调用 write 写到文件系统的 page cache,而后调用 fsync 长久化到磁盘。
PS: 事务执行两头过程的 redo log 也是间接写在 redo log buffer 中的,这些 redo log 也会被后盾线程一起长久化到磁盘。也就是说,一个没有提交的事务的 redo log,也是可能曾经被长久化到磁盘的。
一个没有提交的事务的 redo log 写入到磁盘的三种场景
- 后盾线程每秒一次的轮询操作
- redo log buffer 占用空间达到 innodb_log_buffer_size(缓冲池大小,默认是 8MB)一半的时候,会被动触发
write
到文件系统的page cache
。(留神,因为事务没有提交,这个写盘仅会写入到 page cache) - 并行的事务提交的时候,顺带将这个事务的 redo log buffer 长久化到磁盘。innodb_flush_log_at_trx_commit = 1 时,把 redo log buffer 里的日志全副长久化到磁盘。
只有在 sync_binlog 和 innodb_flush_log_at_trx_commit 都等于 1 的状况下,能力保证数据不失落。
即
- 写 redo log 时,每次事务提交时,都将所有
redo log
fsync
到磁盘 - 写 binlog 时,每次事务提交时,
binlog
都会执行fsync
到磁盘。
组提交机制(group commit)
目标:节约磁盘 IOPS(Input/Output Operations Per Second)。进步 MySQL TPS
日志逻辑序列号(log sequence number,LSN):LSN 是枯燥递增的,用来对应 redo log 的一个个写入点。每次写入长度为 length 的 redo log,LSN 的值就会加上 length。
LSN 也会写到 InnoDB 的数据页中,来确保数据页不会被屡次执行反复的 redo log。
上图是三个并发事务 (trx1, trx2, trx3) 在 prepare 阶段,都写完 redo log buffer,长久化到磁盘的过程,对应的 LSN 别离是 50、120 和 160。
- trx1 是第一个达到的,会被选为这组的 leader;
- 等 trx1 要开始写盘的时候,这个组外面曾经有了三个事务,这时候 LSN 也变成了 160;
- trx1 去写盘的时候,带的就是 LSN=160,因而等 trx1 返回时,所有 LSN 小于等于 160 的 redo log,都曾经被长久化到磁盘;
- 这时候 trx2 和 trx3 就能够间接返回了。
在并发更新场景下,第一个事务写完 redo log buffer 当前,接下来这个 fsync 越晚调用,组员可能越多,节约 IOPS 的成果就越好。为了让一次 fsync 带的组员更多,MySQL 有一个很乏味的优化:拖时间。
两阶段提交的细化过程如下图:
第 4 步把 binlog fsync 到磁盘时,如果有多个事务的 binlog 曾经写完了,也是一起长久化的,这样也能够缩小 IOPS 的耗费。然而个别第 3 步执行的很快,导致 binlog write 和 fsync 距离很短,binlog 组提交的成果不如 redo log 的组提交成果好。
晋升 binlog 组提交的成果,能够通过设置 binlog_group_commit_sync_delay 和 binlog_group_commit_sync_no_delay_count,这两个参数是或的关系。满足一个条件就会 fsync。
binlog_group_commit_sync_delay:示意提早多少微秒后才调用 fsync; 默认为 0
binlog_group_commit_sync_no_delay_count:示意累积多少次当前才调用 fsync。默认为 0
这两个参数是基于额定的成心期待来实现的,会阻止客户端的响应。没有失落数据的危险。
WAL 机制次要得益于两个方面:
1、redo log 和 binlog 都是程序写,磁盘的程序写比随机写速度要快;
2、组提交机制,能够大幅度降低磁盘的 IOPS 耗费。
MySQL 呈现 IO 性能瓶颈
1、设置 binlog_group_commit_sync_delay 和 binlog_group_commit_sync_no_delay_count 参数,缩小 binlog 的写盘次数。
2、将 sync_binlog 设置为大于 1 的值(比拟常见是 100~1000)。这样做的危险是,主机掉电时会丢 binlog 日志。
3、将 innodb_flush_log_at_trx_commit 设置为 2。这样做的危险是,主机掉电的时候会丢数据。
数据库的 crash-safe
实际上数据库的 crash-safe 保障的是:
1、如果客户端收到事务胜利的音讯,事务就肯定长久化了;
2、如果客户端收到事务失败(比方主键抵触、回滚等)的音讯,事务就肯定失败了;
3、如果客户端收到“执行异样”的音讯,利用须要重连后通过查问以后状态来持续后续的逻辑。此时数据库只须要保障外部(数据和日志之间,主库和备库之间)统一就能够了。