type
status
date
slug
summary
tags
category
icon
password
这里写文章的前言:
一个简单的开头,简述这篇文章讨论的问题、目标、人物、背景是什么?并简述你给出的答案。
可以说说你的故事:阻碍、努力、结果成果,意外与转折。
📝 MySql日志
分为 undo log(回滚日志), redo log(重做日志),binlog(归档日志)
- undo log(回滚日志):innodb存储引擎层生成的日志,实现了事务中的原子性,主要用于事务回滚和MVCC
- redo log(重做日志):innodb存储引擎层生成的日志,实现了事务中的持久性,主要用于掉电等故障恢复
- binlog(归档日志):是Server层生成的日志,主要用于数据备份和主从复制
undo log
每次执行事务的过程中,都记录下回滚时需要的信息到一个日志里,那么在事务执行中发生了MYSQL崩溃后,就不用担心无法回滚到事务之前的数据,我们可以通过这个日志回滚到事务之前的数据 undo log 这个机制,它保证了事务的ACID中的原子性 每当innodb引擎对一条记录进行操作(增加,修改,删除)时,需要把回滚时需要的信息都记录到undo log里:
- 在插入一条记录时,把这条记录的主键值记录下来,这样回滚时,只需要把这个主键值对应的记录删掉即可
- 在删除一条记录时,把这条记录的内容都记下来,这样回滚时,再把这些内容组成记录插入到表中
- 更新一条记录时,要把被更新的列的旧值记下来,这样回滚时,再把这些列更新为旧值即可
在回滚时,就读取undo log里的数据,然后做原先相反操作。比如当delete一条记录时,undo log中会把记录中的内容都记下来,然后执行回滚操作的时候,就读取undo log里的数据,然后进行insert操作 undo log 还有一个作用,通过 ReadView + undo log 实现 MVCC(多版本并发控制) 对于 读提交 与 可重复读 隔离级别的事务来说,它们的快照读(普通select语句)是通过ReadView + undo log来实现的,区别在于
- 读提交:每个select都会生成一个新的ReadView,也意味着,事务期间的多次读取同一条数据,前后两次读的数据可能会出现不一致,因为可能这期间另外一个事务修改了该记录,并提交了事务
- 可重复读:事务启动的时候,生成一个ReadView,整个事务期间都在用这个Read View,这样就保证了在事务期间读取到的数据都是事务启动前的记录
这两个隔离级别实现是通过 事务的 ReadView 里的字段,记录中的两个隐藏列 (trx_id 和 roll_pointer)的对比,如果不满足可见性,就会顺着 undo log 版本链里找到满足其可见性的记录,从而控制并发事务访问同一条记录的行为,这就叫 MVCC(多版本并发控制)

redo log
Buffer Pool 提高了独写效率,但是Buffer Pool是基于内存的,而内存总是不可靠的,万一断电重启,还没来得及落盘的脏页数据就会丢失 为了防止断电导致数据丢失的问题,当有一条记录需要更新的时候,innodb引擎就会先更新内存(同时标记为脏页),然后将本次对这个页的修改以 redo log 的形式记录下来,这时候更新算是完成 InnoDB会在适当的时候,由后台线程将缓存在Buffer Pool的脏页刷新到磁盘里,这就是WAL(Write-Ahead Logging)技术 WAL:MYSQL的写操作并不是立马写到磁盘上,而是先写日志,然后在合适的时间再写到磁盘上。 WAL优点:MYSQL的写操作从磁盘的随机写变成了顺序写,提升语句的执行性能。 redo log是物理日志,记录了某个数据页做了什么修改,比如对XXX表空间中的YYY数据页ZZZ偏移量的地方做了AAA更新,每当执行一个事务就会产生这样的一条或者多条物理日志 在提交事务的时候,只要先将 redo log持久化到磁盘,可以不需要等待将缓存在 Buffer Pool 里的脏页数据持久化到磁盘。当系统奔溃的时候,虽然脏页数据没有持久化,但是redo log持久化了,接着MYSQL重启,可以根据 redo log的内容,将所有的数据恢复到最新状态 写入 redo log的方式是追加写,所以磁盘操作是顺序写,而写入数据需要先找到写入位置,然后才写入磁盘,所以磁盘是随机写。 磁盘的顺序写 比 随机写 要高效很多,因此 redo log 写入磁盘的开销更小

redo log buffer
执行一个事务的过程中,产生的redo log也不是直接写入磁盘的,因为这样会产生大量的IO操作,而且磁盘的运行速度远慢于内存。所以 redo log 也有自己的缓存, redo log buffer,每产生一条 redo log时,会先写入到 redo log buffer redo log buffer 默认大小 16 MB,可以通过 innodb_log_buffer_size 参数动态调整大小,增大它的大小可以让 MYSQL处理 大事务 是不必写入磁盘,进而提升写 IO 性能
redo log刷盘机制
缓存在 redo log buffer 里的 redo log 还是在内存中,它是什么时候刷盘的?
- MYSQL正常关闭时
- 当 redo log buffer 中记录的写入量大于 redo log buffer 内存空间的一半时,会触发落盘
- InnoDB的后台线程每隔1秒,将redo log buffer 持久化到磁盘
- 每次事务提交的时都将缓存在 redo log buffer 里的 redo log 直接持久化到磁盘(这个策略可由innodb_flush_log_at_trx_commit 参数控制)
单独执行一个更新语句的时候,InnoDB引擎会自己启动一个事务,在执行更新语句的过程中,生成的redo log先写入到redo log buffer中,然后等事务提交的时候,再将缓存在redo log buffer中的redo log按照顺序写到磁盘 innodb_flush_log_at_trx_commit 含义:
- 设置为0时,表示每次事务提交,还是将 redo log 留在 redo log buffer 中,该模式下在事务提交时不会主动触发写入磁盘的顺序
- 设置为1时,表示每次事务提交时,都将缓存在 redo log buffer 里的redo log直接持久化到磁盘,这样可以保证MYSQL异常重启之后数据不丢失
- 设置为2时,表示每次事务提交时,都只缓存在 redo log buffer 里的 redo log 写到 redo log 文件,写入到 redo log 并不意味着写入到磁盘,因为操作系统的文件中有个Page Cache。Page Cache是专门用来缓存文件数据的,所以写入 redo log 文件意味着写入到操作系统的文件缓存

redo log写满
InnoDB存储引擎有1个重做日志组(redo log Group),重做日志文件组由2个redo log文件组成,这两个redo日志文件名叫: ib_logfile0 和 ib_logfile1,重做日志是以循环写的方式工作的,从头开始写,写到尾部就又回到头部,相当于一个环形,比如 InnoDB存储就会写io_logfile0,当io_logfile0被写满了,会切换到 ib_logfile1,当io_logfile1也被写满时,会切换到ib_logfile0文件
redo log是为了防止Buffer Pool中的脏页丢失而设计的,那么如果随着系统运行,Buffer Pool的脏页刷新到磁盘中,那么 redo log对应的记录也就没用了,这时候我们擦除这些旧记录,腾出空间记录新的更新操作 redo log是循环写的方式,相当于一个环形,InnoDB用write pos表示redo log当前记录写到的位置,用checkpoint表示当前要擦除的位置
- write pos 和 checkpoint 的移动都是顺时针方向
- write pos ~ checkpoint 之间的部分,红色部分,用来记录新的更新记录
- checkpoint ~ write pos 之间的部分,蓝色部分,待落盘的脏数据页记录
当 write pos 追上了 checkpoint , 意味着 redo log 文件满了,这时mysql不能再执行新的更新操作,也就是说mysql会被阻塞,此时会停下来将 Buffer Pool中脏页的数据刷新到磁盘中,然后redo log那些记录可以被擦除,接着对旧的redo log进行擦除,等擦除旧记录腾出了空间,checkpoint就会往后移动,然后mysql恢复正常运行,继续执行新的更新操作

undo log 和 redo log区别
- redo log 记录了此次事务 完成后 的数据状态,记录的是更新之后的值
- undo log 记录了此次事务 开始前 的数据状态,记录的是更新之前的值

binlog
- statement模式: 每一条会修改数据的sql都会记录在binlog中. 不需要记录每一行的变化,减少 binlog 日志量,节省IO,提高性能. 由于sql的执行是有上下文的,因此在保存的时候需要保存相关信息,同时还有一些使用了函数之类的使用的语句无法被复制和记录
- row级别: 不记录sql语句上下文信息,仅保存那条记录被修改.记录单元为每一行的改动,基本是可以全部记录下来的但是由于很多操作,会导致大量的改动(比如alter table),因此这种模式的文件保存的信息太多,日志量太大
- mixed: 这种方案,普通操作使用的 statement记录,当无法使用 stetement的时候,使用row
主从复制原理
MySql主从复制依赖binlog,也就是记录mysql上所有变化并以二进制形式保存在磁盘上。复制过程就是将binlog中的数据从主库传输到从库上 这个过程是异步的,也就是从库上执行事务操作的线程不会等待binlog的线程完成同步 这个过程一般是异步的,也是就主库上执行事务操作的线程不会等待复制binlog的线程完成同步
- 写入binlog: 主库写binlog日志,提交事务,并更新本地存储数据
- 同步binlog: 把binlog复制到所有的从库上,每个从库把binlog写到暂存日志中
- 回放binlog: 回放binlog,并更新存储引擎中的数据

执行流程
- mysql主库在接受客户端提交事务的请求之后,会先写入binlog,再提交事务,更新存储引擎中的数据,事务提交完成后,返回客户端 "操作成功" 的响应
- 从库会创建一个专门的IO线程,连接从库的log dump线程,来接受主库的binlog日志,再把binlog信息写入relay log的中继日志里,再返回给主库"复制成功"的响应
- 从库会创建一个用于回放binlog的线程,去读relay log中继日志,然后放回binlog更新存储引擎中的数据,最终实现主从的数据一致性

🤗 总结归纳
📎 参考文章
有关文章的问题,欢迎您在底部评论区留言,一起交流~