先说说redo log吧?
事务提交后,必须将事务对数据页的修改刷(fsync)到磁盘上,才能保证事务的ACID特性。
这个刷盘,是一个随机写,随机写性能较低,如果每次事务提交都刷盘,会极大影响数据库的性能
随机写性能差,有什么优化方法呢?
(1)先写日志(write log first),将随机写优化为顺序写;
(2)将每次写优化为批量写;
将对数据的修改先顺序写到日志里,这个日志就是redo log。
假如某一时刻,数据库崩溃,还没来得及将数据页刷盘,数据库重启时,会重做redo log里的内容,以保证已提交事务对数据的影响被刷到磁盘上。
既然redo log能保证事务的ACID特性,那为什么还会出现“数据库崩了,丢数据”的问题呢?
先看innodb的三层架构见《InnoDB简要架构》
优化为批量写有两个维度
数据先写入Log Buffer里,然后统一WriteRedoLog
数据先写入OS cache里,然后统一flush
redo log最终落盘的步骤如何?
首先,事务提交的时候,会写入Log Buffer,这里调用的是MySQL自己的函数WriteRedoLog;
接着,只有当MySQL发起系统调用写文件write时,Log Buffer里的数据,才会写到OS cache。注意,MySQL系统调用完write之后,就认为文件已经写完,如果不flush,什么时候落盘,是操作系统决定的;
最后,由操作系统(当然,MySQL也可以主动flush)将OS cache里的数据,最终fsync到磁盘上;
这个副作用,就是可能丢失数据:
(1)事务提交时,将redo log写入Log Buffer,就会认为事务提交成功;(2)如果写入Log Buffer的数据,write入OS cache之前,数据库崩溃,就会出现数据丢失
(3)如果写入OS cache的数据,fsync入磁盘之前,操作系统崩溃,也可能出现数据丢失;
MySQL有一个参数能够控制事务提交时,刷redo log的策略。
策略一:最佳性能
innodb_flush_log_at_trx_commit=0
-
每隔一秒,才将Log Buffer中的数据批量写入OS cache,同时MySQL主动fsync。
mysql或linux宕机会丢失1秒数据,os cache基本无用
策略二:强一致
innodb_flush_log_at_trx_commit=1
-
每次事务提交,都将Log Buffer中的数据write入OS cache,同时MySQL主动fsync。
最多丢失一条未提交的事务,但是性能差
策略三:折衷
innodb_flush_log_at_trx_commit=2
-
每次事务提交,都将Log Buffer中的数据write入OS cache;每隔一秒,MySQL主动将OS cache中的数据批量fsync。
linux宕机会丢失1秒数据,但是linux比mysql宕机几率小很多
本文使用 文章同步助手 同步