JavaGuide知识点整理——MySQL三大日志(binlog
MySQL日志主要包括错误日志,查询日志,慢查询日志,事务日志,二进制日志几大类。其中比较重要的还属于二进制日志binlog(归档日志)和事务日志redo log(重做日志)和undo log(回滚日志)。
这篇文章就聊聊redo log,bin log,两阶段提交,undo log.
redo log(重做日志)
redo log(重做日志)是InnoDB存储引擎独有的,它让MySQL拥有了崩溃恢复的能力。
比如MySQL实例挂了或者宕机了,重启时InnoDB存储引擎会使用redo log恢复数据。保证数据的持久性与完整性。
image.png
MySQL中数据是以页为单位,查询一条记录,会从硬盘把一页的数据加载出来,加载出来的数据叫数据页,会放入到Buffer Pool中。
后续的查询都先从Buffer Pool中找,如果没有命中再去硬盘加载,减少硬盘IO开销,提升性能。
更新表数据的时候,也是如此,发现Buffer Pool里存在要更新的数据,就直接在Buffer Pool里更新。
然后会把某个数据页上做了什么修改 记录到重做日志缓存(redo log buffer)里,接着刷盘到redo log文件里。
重做日志
理想情况下,事务一提交就会进行刷盘操作,但是实际上,刷盘的实际是根据策略来进行的。
刷盘时机
InnoDB存储引擎为redo log的刷盘策略提供了innodb_flush_log_at_trx_commit参数,它支持三种策略:
- 0:设置为0时,每次事务提交不进行刷盘操作
- 1:设置为1时,每次事务提交都进行刷盘操作(默认)
- 2: 设置为2时,每次事务提交都只把redo log buffer内容写入page cache
innodb_flush_log_at_trx_commit 参数为1才会每次事务提交都调用fsync对redo log刷盘。
另外InnoDB存储引擎有一个后台线程,每隔1s就会把redo log buffer中的内容写到系统文件缓存(page cache),然后调用fsync刷盘。
除了定时刷盘,设置的事务提交刷盘,还有一种情况:redo log buffer占用的空间达到innodb_log_buffer_size一半的时候,后台线程会主动刷盘。
注意刷盘策略为1时不会有数据丢失。
刷盘策略为0时,mySQL挂了会丢失一秒数据
刷盘策略为2时,MySQL挂了不会丢失数据,宕机会有1秒数据丢失。
日志文件组
硬盘上存储的redo log日志文件不止一个,而是以一个日志文件组的形式出现的。每个redo日志文件大小都是一样的。
比如可以配置为一组4个文件,每个文件的大小都是1G,整个redo log日志文件组可以记录4G内容。
它采用的是环形数组形式,从头开始写,写到末尾又回到头循环写。如下图:
image.png
在日志文件组中还有两个重要的属性,分别是write pos,checkpoint
- write pos是当前记录的位置,一边写一边后移
- checkpoint是当前要擦除的位置,也是往后移
每次刷盘redo log记录到日志文件组中,write pos位置就会后移更新
每次MySQL加载日志文件组恢复数据时,会清空加载过的redo log记录,并把checkpoint后移更新。
write pos和checkpoint之间还空着的部分可以用来写入写的redo log记录。
如果write pos追上了checkpoint,说明日志文件组满了,这时候不能写入新的redo log记录,MySQL得清空一些记录,把checkpoint推进一下。
redo log小结
只要每次把修改后的数据页直接刷盘就行了,为什么还要用redo log?
实际上数据页大小是16kb,刷盘比较耗时间,可能就修改了几byte的数据,没必要把完整的数据页刷盘。
而且数据页刷盘是随机写,因为一个数据页对应的位置可能在硬盘文件的随机位置,所以性能很差。
如果是写redo log,一行记录可能就几十byte,只包含空间号,数据页号,磁盘文件偏移量,更新值,加上是顺序写,所以刷盘速度很快。
所以用redo log的形式记录修改内容,性能远远超过刷数据页的方式。
binlog
redo log是物理日志,记录内容是在某个数据页上做了什么修改。属于InnoDB存储引擎。
而binlog是逻辑日志,记录内容是语句的原始逻辑,类似于给id是2是这行c字段+1.属于mysql server层。
不管用什么存储引擎,只要发生了表数据更新,都会产生binlog日志。
binlog可以说是MySQL数据的数据备份,主备,主主,主从都离不开的。需要依靠binlog来同步数据,保证数据一致性。
binlog会记录所有涉及更新数据的逻辑操作,并且是顺序写。
记录格式
binlog日志有三种格式,可以通过binlog_format参数指定。
- statement
- row
- mixed
指定statement,记录的内容是sql语句原文。同步数据时,也会执行记录的sql语句。但是问题是如果有采用系统当前时间的sql,会导致时间与之前不一致。
row记录的内容不是简单的sql语句了,还包括操作的具体数据。row格式记录的内容看不到详细信息,需要通过mysqlbinlog工具解析出来。
这样能保证同步数据的一致性,通常情况下也都是指定为给row的,这样可以为数据库的恢复和同步带来更好的可靠性。
但是这种方式需要大量的容量来记录,比较占用空间,恢复和同步时会消耗更多的IO资源,影响执行速度。
所以mixed是折中方案,是MySQL如果判断这条sql可能引起数据不一致会使用row格式,否则用statement格式。
写入机制
binlog的写入机制也非常简单,事务执行过程中,先把日志写到binlog cache中,事务提交的时候,再把binlog cache写到binlog文件中。
因为一个事务的binlog不能被拆开,无论这个事务多大也要一次性写入。所以系统会给每个线程分配一块内存作为binlog cache。
我们可以通过binlog_cache_size参数控制单个线程binlog cache大小,如果存储内容超过了这个参数要暂存到磁盘中。
binlog 日志刷盘流程
上图的write是指日志写入到文件系统的page cache,并没有把数据持久化,所以比较快
fsync才是将数据持久化到磁盘的操作
write和fsync的时机,可以由参数sync_binlog控制,默认是0.
- 设置为0:每次事务提交都write,由系统判断什么时候fsync。虽然性能提升,但是机器宕机会丢失部分数据。
- 设置为1:每次提交都执行fsync
- 设置为n:每次提交都write,累积n个事务才fsync。
在出现IO瓶颈的场景里,将sync_binlog设置成一个比较大的值,可以提升性能。代价是机器宕机丢失最近N个事务的binlog日志。
两阶段提交
redo log(重做日志)让InnoDB存储引擎有了崩溃恢复的能力
binlog(归档日志)保证了MySQL集群架构的数据一致性
虽然他们都属于持久化的保证,但是侧重点不同。而且两者写入时机也不同。redo log会记录每一条sql语句,而binlog只会记录提交了事务的sql。
为了解决两份日志之间的逻辑一致问题,InnoDB存储引擎使用两阶段提交方案。
原理很简单,就是将redo log的写入拆成两个步骤:prepare和commit。这就是两阶段提交。
两阶段提交
在使用两阶段提交后,写入binlog发生异常也不会有影响。因为redo log日志恢复时,发现redo log还处于prepare阶段,并且没有对应的binlog日志,就会回滚事务。
不过哪怕redo log处于pepare阶段,但是在binlog能通过事务id找到对应日志,就不会回滚了。
undo log(回滚日志)
我们知道如果想要保证事务的原子性,就要在异常发生时对已经执行的操作进行回滚。在MySQL中,恢复机制是通过回滚日志实现的。所有事务进行的修改都会先记录到这个回滚日志中,然后再执行相关的操作。如果执行过程中遇到异常,我们直接利用回滚日志中的信息将数据回滚到修改之前的样子。
并且回滚日志会先于数据持久化到磁盘上。这样就保证了哪怕数据库宕机,也能正常回滚。
另外MVCC的实现依赖于:隐藏字段,Read View,undo log。在内部实现中,通过数据行的db_trx_id和Read View来判断数据的可见性,如果不可见则会找历史版本,每个事务 读取到的版本可能是不一样的。在同一个事务中,用户只能看到该事务创建Read View之前已经提交的修改和该事务本身做的修改。
总结
MySQL InnoDB引擎使用了redo log(重做日志)保证了事务的持久性,使用undu log(回滚日志)保障事务的原子性。而数据库的数据备份,主备,主主,主从都离不开binlog。需要依靠binlog来同步数据,保证数据的一致性。
本篇笔记就记到这里,如果稍微帮到你了记得点个喜欢点个关注。我是认为这种基础知识还是挺有用的,而且很多思想和方案都很值得借鉴。也祝大家工作顺顺利利吧!