MySQL三大日志

常见的mysql日志有二进制日志 binlog(归档日志)和重做日志 redo log(事务日志)和 undo log(回滚日志)。

redo log

MySQL 中数据是以页为单位,查询一条记录会从硬盘把一页的数据加载出来,加载出来的数据叫数据页,会放入到 Buffer Pool 中。

后续的查询都是先从 Buffer Pool 中找,没有命中再去硬盘加载,减少硬盘 IO 开销,提升性能。

更新表数据的时候,也是先更新Buffer Pool 的数据,如果没有则先把数据读到Buffer Pool然后会把“在某个数据页上做了什么修改”记录到redo log buffer,接着刷盘到 redo log 文件里。


那么什么时候会进行刷盘呢?

为什么需要redo log

buffer pool基于内存,提高了mysql性能,但是内存的数据没有持久化到磁盘,mysql宕机后会数据丢失,为了防止断电导致数据丢失的问题,当有一条记录需要更新的时候,InnoDB引擎就会先更新内存(同时标记为脏页),然后将本次对这个页的修改以redo log的形式记录下
来,这个时候更新就算完成了。

后续,InnoDB引擎会在适当的时候,由后台线程将缓存在Buffer Pool的脏页刷新到磁盘
里,这就是WAL(Write-Ahead Logging)技术。WAL技术指的是,MySQL的写操作并不是立刻写到磁盘上,而是先写日志,然后在合适的时间再写到磁盘上。

刷盘时机

  • 事务提交:当事务提交时,log buffer 里的 redo log 会被刷新到磁盘(可以通过innodb_flush_log_at_trx_commit参数控制,后文会提到)。
  • log buffer 空间不足时:log buffer 中缓存的 redo log 已经占满了 log buffer 总容量的大约一半左右,就需要把这些日志刷新到磁盘上。
  • Checkpoint(检查点):InnoDB 定期会执行检查点操作,将内存中的脏数据(已修改但尚未写入磁盘的数据)刷新到磁盘,并且会将相应的重做日志一同刷新,以确保数据的一致性。
  • 后台刷新线程:InnoDB 启动了一个后台线程,负责周期性(每隔 1 秒)地将脏页(已修改但尚未写入磁盘的数据页)fsync刷新到磁盘,并将相关的重做日志一同刷新。
  • 正常关闭服务器:MySQL 关闭的时候,redo log 都会刷入到磁盘里去。

redo log 要写到磁盘,数据也要写磁盘,为什么要多此一举?

写入redo log的方式使用了追加操作,所以磁盘操作是顺序写,而写入数据需要先找到写入位置,然后才写到磁盘,所以磁盘操作是随机写。磁盘的「顺序写」比「随机写」高效的多,因此redo log写入磁盘的开销更小。

修改数据页后(脏页)为何不直接刷盘,还需要redo log

数据页大小是16kb,刷盘(随机写)要进行io比较耗时,如果仅修改了数据页几byte的数据,没必要刷盘。而写red log 一行记录(顺序写)可能就占几十 Byte,相对来说更优。

redolog采用循环写,边写边擦除日志,只记录未被刷入磁盘的数据 ,已刷入磁盘的数据会从redolog中擦除,而binlog保存的全量日志,如果整个数据库都删除了,可以用binlog恢复数据。

Buffer Pool的脏页刷新到了磁盘中,那么redo log对应的记录也就没用了,这时候擦除这些旧记录,以腾出空间记录新的更新操作。

bin log

redo log 是物理日志,记录内容是在某个数据页上做了什么修改”,属于 InnoDB 存储引擎。

binlog 是逻辑日志记录内容是语句的原始逻辑,类似于“给 ID=2 这一行的 c 字段加 1”,属于MySQL Server 层,会记录所有涉及更新数据的逻辑操作,并且是顺序写

binlog日志有三种格式,由binlog_format指定:

  • statement
    记录SQL原文,这样同步数据时仅需执行SQL即可,但是如果SQL中有now(),那么此时同步获取的时间和原库的时间是不一致的。因此在同步数据时,需要指定为row格式,不仅记录SQL,还包含具体数据,即把now()变成具体时间戳
  • row
    记录SQL和具体数据,因此这种格式会占用更多的空间,恢复与同步时会更消耗 IO 资源,影响执行速度。
  • mixed
    前两者混合

bin log刷盘时机 

事务执行过程中,先把日志write 到binlog cache事务提交的时,再把binlog cache fsync到binlog 文件中。

因为一个事务的 binlog 不能被拆开,无论这个事务多大,也要确保一次性写入,所以系统会给每个线程分配一个binlog cache

可以通过binlog_cache_size参数控制单个线程 binlog cache 大小,如果存储内容超过了这个参数,就要暂存到磁盘(Swap)。

两阶段提交

redo log让 InnoDB 存储引擎拥有了崩溃恢复能力。binlog保证了 MySQL 集群架构的数据一致性。

在执行更新语句过程,会记录 redo log 与 binlog 两块日志,以基本的事务为单位,redo log 在事务执行过程中可以不断写入,而 binlog 只有在提交事务时才写入,所以 redo log 与 binlog 的写入时机不一样。
如果在写完redolog日志后,binlog日志写期间发生异常,就会导致数据不一致。对此innodb采用两阶段提交方案,把redo log写入拆成prepare和commit两阶段

这样binlog写期间发生异常,redolog还处于prepare阶段,回滚事务。
而如果binlog期间无异常,commit期间异常,就不会回滚事务,因为binlog日志已经记录成功,mysql就认为是完整的,会提交事务恢复数据。

undo log回滚日志

undo log保证了事务的原子性,undo log 的信息也会被记录到 redo log 中,因为 undo log 也要实现持久性保护。


每当innoDB引擎对一条记录进行操作(修改、删除、新增)时,要把回滚时需要的信息都记录到undo log里,比如:

  • 在插入一条记录时,要把这条记录的主键值记下来,这样之后回滚时只需要把这个主键值对应的记录删掉就好了;
  • 在删除一条记录时,要把这条记录中的内容都记下来,这样之后回滚时再把由这些内容组成的记录插入到表中就好了;
  • 在更新一条记录时,要把被更新的列的旧值记下来,这祥样之后回滚时再把这些列更新为旧值就好了。

Buffer Pool

MySQL的数据都是存在磁盘中的,那么我们要更新一条记录的时候,得先要从磁盘读取该
记录,然后在内存中修改这条记录。那修改完这条记录是选择直接写回到磁盘,还是选择缓
存起来呢?
当然是缓存起来好,这样下次有查询语句命中了这条记录,直接读取缓存中的记录,就不需
要从磁盘获取数据了。
为此,Innodb存储引擎设计了一个Buffer Pool,来提高数据库的读写性能。当修改数据时,如果数据存在于Buffer Pool中,那直接修改Buffer Pool中数据所在的页,然后将其页设置为脏页(该页的内存数据和磁盘上的数据已经不一致),为了减少磁盘/O,不会立即将脏页写入磁盘,后续由后台线程选择一个合适的时机将脏页写入到磁盘。

总结

MySQL InnoDB 引擎使用 redo log(重做日志) 保证事务的持久性,使用 undo log(回滚日志) 来保证事务的原子性,这两个都是存储引擎层面的。

MySQL 数据库的数据备份、主备、主主、主从都离不开 binlog,需要依靠 binlog 来同步数据,保证数据一致性,是Server层面的,所有存储引擎都可以使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值