1.undo log
undo log是InnoDB存储引擎层的日志,实现了事务的原子性,主要用于事务回滚和MVCC。在事务没有提交之前,InnoDB会先记录更新前的数据,记录在undo log中,回滚时利用undo log来回滚。
2.redo log
redo log也是InnoDB存储引擎层的日志,属于物理日志,记录了某个数据页做了什么修改,实现了事务的持久性,主要用于掉电等故障恢复。比如,某个事务提交了,脏页数据还没有刷盘,但是MySQL机器断电了,脏页的数据就丢失了,MySQL重启后通过redo log来把已经提交事务的数据恢复回来。
3.binlog
binlog是Server层的日志,主要用于数据备份和主从复制。在完成一条更新操作后,Server层会生成一条binlog日志,等之后事务提交的时候,会把该事务执行过程中产生的所有binlog统一写入binlog文件。binlog文件是记录了所有数据库表结构变更和表数据修改的日志,不会记录查询类的操作。
每条修改数据的SQL都会被记录到binlog中,主从复制中Slave端再根据SQL语句重现。
缺点:STATEMENT有动态函数的问题,比如用了uuid或者now这些函数,主库上执行的结果并不是你在从库中执行的结果,这种随时在变的函数会导致复制的数据不一致。
记录行数据最终被修改成什么样了,不会出现STATEMENT下动态函数出现的问题。
缺点:但是ROW的缺点是每条行变化结果都会被记录。
比如批量执行update语句,更新多少行就会产生多少条记录,使binlog文件过大,而在STATEMENT格式下只会记录一条update语句。
包含了STATEMENT和ROW格式,会自动根据情况选择使用哪种格式。
1.适用对象不同
binlog是MySQL的Server层实现的日志,所有存储引擎都可以使用
redo log是InnoDB存储引擎层的日志
2.文件格式不同
binlog有3种格式类型,分别是STATEMENT(默认格式)、ROW、MIXED
redo log是物理日志,记录的是某个数据页做了什么修改,比如XXX表空间中的YYY数据页ZZZ偏移量的地方做了AAA的更新。
3.写入方式不同
binlog是追加写,写满了一个文件,就创建一个新的文件继续写,不覆盖以前的日志,保存的是全量的日志
redo log是循环写,日志的空间大小是固定的,写满就会从头开始写,保存未被刷入磁盘的脏页日志。
4.用途不同
binlog用于备份恢复、主从复制
redo log用于掉电等故障恢复
binlog是Sever层的日志,不会记录InnoDB存储引擎层中有哪些数据页没有被刷盘,redo log是InnoDB层的日志,可以记录哪些脏页没有被刷盘,崩溃恢复的时候,恢复的粒度更细,可以精确到需要恢复的数据页,而bin log保存的是全量日志,没有办法做到这一点,所以崩溃恢复用的是redo log。
binlog虽然可以恢复,但是恢复性能极差。
事务执行过程中更新数据,并不是在事务提交的时候,就把修改的数据刷入磁盘,而是修改Buffer Pool中的数据页,并标记为脏页,后面找合适的时间刷盘。
此时,如果事务提交了,还没有刷盘,数据库发生了宕机,那么就会导致事务修改的数据丢失了。
所以MySQL引入了redo log,redo log保存的内容是物理日志,主要是记录InnoDB对某个数据页的修改操作,当事务提交时,redo log会先刷入磁盘,因为redo log保存了数据页的修改操作,即使脏页数据没有刷盘时数据库发生了党纪,重启后MySQL通过重放redo log,就能恢复未刷盘的脏页,保证了数据的持久化。
写入redo log的方式使用了追加操作,所以磁盘操作就是顺序写(多个redo log记录在同一个文件中,一起写入磁盘);而写入数据需要先找到写入位置,然后才写入磁盘,所以磁盘操作是随机写(Buffer poll中修改的多个数据可能在磁盘的不同位置,每次写入一个都需要寻找一次位置,开销很大)。
磁盘的顺序写
比随机写
高效的多,因此redo log写入磁盘的开销会更小。
为什么顺序写比随机写高效?
因为你在写作业的时候,按顺序一页一页往下写,肯定比每一个字都去找对应页写快的多。
所以说,这是WAL技术的另一个优点:MySQL的写操作从磁盘上的随机写变成了顺序写,提升了语句的执行性能。因为MySQL的写操作不是直接更新的磁盘上,而是先写到日志中,等合适的时间再去更新到磁盘上。
总之,redo log不仅实现了事务的持久性,保证MySQL在任何时间段突然崩溃,重启后之前提交的记录都不丢失,还把写操作从随机写变成了顺序写,提升MySQL写入磁盘的性能。
单独执行一个更新语句时,InnoDB会自己启动一个事务,在执行更新语句的过程中,生成的redo log先写入到redo log buffer中,然后等待事务提交,再把缓存在redo log buffer中的redo log 按组的方式顺序写到磁盘。
上面的这种刷盘时机是在事务提交的时候,这个是默认的行为。除此之外,还有两种策略,由参数innodb_flush_log_at_trx_commit
控制,取值可为0、1、2,默认是1:
参数设置为0:每次事务提交时,还是将redo log留在redo log buffer中,该模式下在事务提交时不会主动触发写入磁盘的操作。
参数设置为1:每次事务提交时,都将缓存在redo log buffer中的redo log直接持久化到磁盘,这样可以保证MySQL异常重启后数据不会丢失。
参数设置为2:每次事务提交时,都是缓存在redo log buffer中的redo log写到redo log文件,注意写入到redo log文件不代表写入磁盘,因为操作系统的文件系统中有个PageCache,PageCache是专门来缓存文件数据的,所以写入redo log文件相当于写入操作系统的文件缓存。
那参数为0、2时,什么时候把数据刷到磁盘上?
0:InnoDB的后台线程每隔1秒,会把缓存缓存在redo log buffer中的redo log,通过write()
写入操作系统的PageCache,然后调用fsync()
持久化到磁盘。
所以参数为0,MySQL进程崩溃会导致上一秒的事务数据全部丢失。
2:InnoDB的后台线程每隔1秒,调用fsync()
将缓存在操作系统中PageCache里的redo log持久化到磁盘。
所以参数为2,比参数为0较为安全,因为MySQL进程崩溃不会丢失数据,只有在操作系统崩溃或系统断电时,上一秒所有事务才可能丢失。
三种策略的安全性和写入性能比较:
安全性:1 > 2 > 0
性能:0 > 2 > 1
所以安全性和性能是不可兼得的。
事务提交后,redo log和binlog都有持久化到磁盘,但是这是两个独立的逻辑,可能出现半成功状态,这样两个日志之间的逻辑就不一致。
比如,id=1这行数据的字段,name原本是张三。然后执行update user set name='李四' where id=1;
后,如果持久化redo log和binlog日志时出现了半成功状态,那么:
所以,在持久化redo log和binlog时,出现半成功状态,就会导致主从库的数据不一致问题。redo log影响主库,binlog影响从库数据,所以redo log和binlog需要保持一致,才能保证主从库的一致。
两阶段提交把事务拆分为两个阶段,准备阶段和提交阶段。
总的来说,两阶段提交是把binlog刷入磁盘时机作为事务提交成功的标志的。
如果binlog还没被刷入磁盘,MySQL发生了崩溃,MySQL重启就要回滚事务。
如果binlog刷入磁盘,即使redo log没有设置commit状态,MySQL发生崩溃,MySQL重启后就会提交事务。