简单来说就是依靠redo log和binlog保证持久化到磁盘后就可以保证,异常重启数据可以正常恢复;这里主要说下这两个log的写入机制。
binlog的写入机制:
一个事务执行的过程中,会先把日志写入到binlog cache中,事务提交的时候再把binlog cache中的日志写到binlog中。系统给binlog cache分配了一片内存,每个线程一个,其大小由参数(binlog_cache_size)控制,超出这个大小就要暂存到磁盘,事务提交时写入binlog,再清除binlog cache中的数据;虽然每个线程都有自己的binlog cache但是都共用一份binlog文件
binlog写入磁盘,其实分为两个步骤:1、先write到page cache 2、再fsync持久化到磁盘;真正占用IO的还是fsync,所以由参数sync_binlog控制写入时机,具体:
-
sync_binlog=0的时候,表示每次提交事务都只write,不fsync;
-
sync_binlog=1的时候,表示每次提交事务都会执行fsync;
-
sync_binlog=N(N>1)的时候,表示每次提交事务都write,但累积N个事务后才fsync。
常见的是将参数值定义为10-100中的值,也会有丢失近N个事务的binlog
redo log的写入机制:
redo log同样事务执行过程中,会先把redo log写入到redo_log buffer中,会在特定的时间将redo_log buffer中的日志写入到磁盘中,InnoDB提供了参数innodb_flush_log_at_trx_commit,控制redo_log的写入策略:
-
设置为0的时候,表示每次事务提交时都只是把redo log留在redo log buffer中;
-
设置为1的时候,表示每次事务提交时都将redo log直接持久化到磁盘;
-
设置为2的时候,表示每次事务提交时都只是把redo log写到page cache。
InnoDB有一个后台线程每隔1s就会调用write把redo_log buffer中的日志写到page cache再调用fsync把日志持久化到磁盘,所以会出现某些事务未提交也会被持久化到磁盘。
还有两种场景也会出现未提交事务被持久化到磁盘:
1、当redo_log buffer的大小达到innodb_log_buffer_size 一半时,此种情况只write 不fsync
2、当两个并行的事务,一个事务提交时,另一个也有可能被持久化到磁盘,当然具体依赖于innodb_flush_log_at_trx_commit参数的设置
根据两阶段提交(后面会说下两阶段提交)时序上redo_log先prepare 再写binlog最后再把redo_log commit。
组提交:
如果把两种日志都配成“双1”即:sync_binlog和innodb_flush_log_at_trx_commit都设置成1,就是说一次事务提交需要刷两次盘(一次是redo_log的perpare阶段,一次是binlog)实则不然,这里就用到了组提交;日志逻辑序号LSN就对应redo_log的一个个写入点,每次写入length个redo_log其长度就加length;最先到达的事务被选为组长,组长写入磁盘的时候会带上组中最大的LSN(LSN会写到数据页中来确保数据页不会多次重复的执行redo_log),剩余小于最大LSN的事务都会直接返回;故而一个组中组员越多越节约IO。
为了让fsync带的组员更多,Mysql在两阶段提交中有过这样“拖时间”的优化:
--两阶段提交:1、写redo_log,处于prepare阶段 2、写binlog 3、提交事务,处于commit阶段
--“拉长”两阶段提交:1、redo_log prepare write 2、binlog write 3、redo_log prepare fsync 4、binlog fsync 5、redo_log commit,write(提交事务)如此一来如果前面事务写完第四步binlog fsync也可以组提交,一般binlog的组提交效果没有那么好是因为第三步比较快;当然也可以用参数来提升组提交的效果:
-
binlog_group_commit_sync_delay参数,表示延迟多少微秒后才调用fsync;
-
binlog_group_commit_sync_no_delay_count参数,表示累积多少次以后才调用fsync。
这两者满足任意一个都会触发fsync
这就可以很好的解释了虽然事务的提交每次都要redo_log和binlog磁盘的读写次数依然没有变少:
-
redo log 和 binlog都是顺序写,磁盘的顺序写比随机写速度要快;
-
组提交机制,可以大幅度降低磁盘的IOPS消耗。
如此一来,如果分析到数据库的出现了性能的瓶颈,并且瓶颈在IO上,可以通过以下三种方法解决:
-
设置 binlog_group_commit_sync_delay 和 binlog_group_commit_sync_no_delay_count参数,减少binlog的写盘次数。这个方法是基于“额外的故意等待”来实现的,因此可能会增加语句的响应时间,但没有丢失数据的风险。
-
将sync_binlog 设置为大于1的值(比较常见是100~1000)。这样做的风险是,主机掉电时会丢binlog日志。
-
将innodb_flush_log_at_trx_commit设置为2。这样做的风险是,主机掉电的时候会丢数据。
分析完几篇日志的文章统一总结吧。
参考林晓彬《Mysql45讲》