目录
MySQL中的三种log
一、什么是binlog
binlog其实在日常的开发中是听得很多的,因为很多时候数据的更新就依赖着binlog。
举个很简单的例子:我们的数据是保存在数据库里边的嘛,现在我们对某个商品的某个字段的内容改了(数据库变更),而用户检索的出来数据是走搜索引擎的。为了让用户能搜到最新的数据,我们需要把引擎的数据也改掉。
一句话:数据库的变更,搜索引擎的数据也需要变更。
于是,我们就会监听binlog的变更,如果binlog有变更了,那我们就需要将变更写到对应的数据源。
什么是
binlog?
binlog记录了数据库表结构和表数据变更,比如update/delete/insert/truncate/create。它不会记录select(因为这没有对表没有进行变更)
binlog长什么样?
binlog我们可以简单理解为:存储着每条变更的SQL语句(当然从下面的图看来看,不止SQL,还有XID「事务Id」等等)
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zHut7CUg-1653367696010)(C:/Users/86158/AppData/Roaming/Typora/typora-user-images/image-20220523202025377.png)]](https://img-blog.csdnimg.cn/17176738f52e4c86b5d0936299e36bb3.png)
binlog一般用来做什么
主要有两个作用:复制和恢复数据
- MySQL在公司使用的时候往往都是一主多从结构的,从服务器需要与主服务器的数据保持一致,这就是通过
binlog来实现的 - 数据库的数据被干掉了,我们可以通过
binlog来对数据进行恢复。
因为binlog记录了数据库表的变更,所以我们可以用binlog进行复制(主从复制)和恢复数据。
二、什么是redo log
假设我们有一条sql语句:
update user_table set name='java3y' where id = '3'
MySQL执行这条SQL语句,肯定是先把id=3的这条记录查出来,然后将name字段给改掉。这没问题吧?
实际上Mysql的基本存储结构是页(记录都存在页里边),所以MySQL是先把这条记录所在的页找到,然后把该页加载到内存中,将对应记录进行修改。
现在就可能存在一个问题:如果在内存中把数据改了,还没来得及落磁盘,而此时的数据库挂了怎么办?显然这次更改就丢了。
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7YEbVhUl-1653367696011)(C:/Users/86158/AppData/Roaming/Typora/typora-user-images/image-20220523203112574.png)]](https://img-blog.csdnimg.cn/ff4ba521b441490cacbe3f96796e94d2.png)
如果每个请求都需要将数据立马落磁盘之后,那速度会很慢,MySQL可能也顶不住。所以MySQL是怎么做的呢?
MySQL引入了redo log,内存写完了,然后会写一份redo log,这份redo log记载着这次在某个页上做了什么修改。
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KrQzYCob-1653367696012)(C:/Users/86158/AppData/Roaming/Typora/typora-user-images/image-20220524120834046.png)]](https://img-blog.csdnimg.cn/65a2de8cef2845579a5a86084768781f.png)
其实写redo log的时候,也会有buffer,是先写buffer,再真正落到磁盘中的。至于从buffer什么时候落磁盘,会有配置供我们配置。
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Xoxvn9Bk-1653367696012)(C:/Users/86158/AppData/Roaming/Typora/typora-user-images/image-20220524121206150.png)]](https://img-blog.csdnimg.cn/6b5217b779224d13832fad4ba3bb5428.png)
写redo log也是需要写磁盘的,但它的好处就是顺序IO(我们都知道顺序IO比随机IO快非常多)。
所以,redo log的存在为了:当我们修改的时候,写完内存了,但数据还没真正写到磁盘的时候。此时我们的数据库挂了,我们可以根据redo log来对数据进行恢复。因为redo log是顺序IO,所以写入的速度很快,并且redo log记载的是物理变化(xxxx页做了xxx修改),文件的体积很小,恢复速度很快。
三、binlog和redo log
看到这里,你可能会想:binlog和redo log 这俩也太像了吧,都是用作”恢复“的。
其实他俩除了"恢复"这块是相似的,很多都不一样,下面看我列一下。
存储的内容
binlog记载的是update/delete/insert这样的SQL语句,而redo log记载的是物理修改的内容(xxxx页修改了xxx)。
所以在搜索资料的时候会有这样的说法:redo log 记录的是数据的物理变化,binlog 记录的是数据的逻辑变化
功能
redo log的作用是为持久化而生的。写完内存,如果数据库挂了,那我们可以通过redo log来恢复内存还没来得及刷到磁盘的数据,将redo log加载到内存里边,那内存就能恢复到挂掉之前的数据了。
binlog的作用是复制和恢复而生的。
- 主从服务器需要保持数据的一致性,通过
binlog来同步数据。 - 如果整个数据库的数据都被删除了,
binlog存储着所有的数据变更情况,那么可以通过binlog来对数据进行恢复。
又看到这里,你会想:”如果整个数据库的数据都被删除了,那我可以用redo log的记录来恢复吗?“不能
因为功能的不同,redo log 存储的是物理数据的变更,如果我们内存的数据已经刷到了磁盘了,那redo log的数据就无效了。所以redo log不会存储着历史所有数据的变更,文件的内容会被覆盖的。
binlog和redo log 写入的细节
redo log是MySQL的InnoDB引擎所产生的。
binlog无论MySQL用什么引擎,都会有的。
InnoDB是有事务的,事务的四大特性之一:持久性就是靠redo log来实现的(如果写入内存成功,但数据还没真正刷到磁盘,如果此时的数据库挂了,我们可以靠redo log来恢复内存的数据,这就实现了持久性)。
上面也提到,在修改的数据的时候,binlog会记载着变更的内容,redo log也会记载着变更的内容。(只不过一个存储的是物理变化,一个存储的是逻辑变化)。那他们的写入顺序是什么样的呢?
redo log事务开始的时候,就开始记录每次的变更信息,而binlog是在事务提交的时候才记录。
于是新有的问题又出现了:我写其中的某一个log,失败了,那会怎么办?现在我们的前提是先写redo log,再写binlog,我们来看看:
- 如果写
redo log失败了,那我们就认为这次事务有问题,回滚,不再写binlog。 - 如果写
redo log成功了,写binlog,写binlog写一半了,但失败了怎么办?我们还是会对这次的事务回滚,将无效的binlog给删除(因为binlog会影响从库的数据,所以需要做删除操作) - 如果写
redo log和binlog都成功了,那这次算是事务才会真正成功。
简单来说:MySQL需要保证redo log和binlog的数据是一致的,如果不一致,那就乱套了。
如果
redo log写失败了,而binlog写成功了。那假设内存的数据还没来得及落磁盘,机器就挂掉了。那主从服务器的数据就不一致了。(从服务器通过binlog得到最新的数据,而主服务器由于redo log没有记载,没法恢复数据)如果
redo log写成功了,而binlog写失败了。那从服务器就拿不到最新的数据了。
MySQL通过两阶段提交来保证redo log和binlog的数据是一致的。
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r9ubOsxl-1653367696012)(C:/Users/86158/AppData/Roaming/Typora/typora-user-images/image-20220524123609570.png)]](https://img-blog.csdnimg.cn/2a9ed80d74a34a3db319e76a24bc2e39.png)
过程:
- 阶段1:InnoDB
redo log写盘,InnoDB 事务进入prepare状态 - 阶段2:
binlog写盘,InooDB 事务进入commit状态 - 每个事务
binlog的末尾,会记录一个XID event,标志着事务是否提交成功,也就是说,恢复过程中,binlog最后一个 XID event 之后的内容都应该被 purge。
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qdqzH9aU-1653367696013)(C:/Users/86158/AppData/Roaming/Typora/typora-user-images/image-20220524123932485.png)]](https://img-blog.csdnimg.cn/b16f6fd8c3e54c26a697566a64329fd6.png)
四、什么是undo log
undo log有什么用?
undo log主要有两个作用:回滚和多版本控制(MVCC)
在数据修改的时候,不仅记录了redo log,还记录undo log,如果因为某些原因导致事务失败或回滚了,可以用undo log进行回滚
undo log主要存储的也是逻辑日志,比如我们要insert一条数据了,那undo log会记录的一条对应的delete日志。我们要update一条记录时,它会记录一条对应相反的update记录。
这也应该容易理解,毕竟回滚嘛,跟需要修改的操作相反就好,这样就能达到回滚的目的。因为支持回滚操作,所以我们就能保证:“一个事务包含多个操作,这些操作要么全部执行,要么全都不执行”。【原子性】
因为undo log存储着修改之前的数据,相当于一个前版本,MVCC实现的是读写不阻塞,读的时候只要返回前一个版本的数据就行了。
redo log 重做日志(MySQL 存储引擎 InnoDB 的事务日志)
我们知道 MySQL 数据存在磁盘中,每次读写数据需做磁盘 IO,并发场景下性能差。为此 MySQL 引入缓存 Buffer Pool 做优化。其包含磁盘中部分数据页(page)的映射,来缓解数据库的磁盘压力。
当从数据库读数据时,首先从缓存中读,缓存中没有,则从磁盘读后放入缓存;当向数据库写数据时,先向缓存中写,此时缓存中的数据页数据会变更,该数据页叫脏页,Buffer Pool 中修改完数据后会按照设定的策略再定期刷到磁盘中去,这个过程叫刷脏页。
那么问题来了,如果 Buffer Pool 中修改的数据还没有及时的刷到磁盘,MySQL 宕机重启,就会导致数据丢失,无法保证事务的持久性,怎么办?
redo log 解决了这个问题。就是说数据库在修改数据时,会把更新记录先写到 redo log 中,再去修改 Buffer Pool 中的数据,当提交事务时,调用 fsync 把 redo log 刷入磁盘。至于缓存中更新的数据文件何时刷入磁盘,则由后台线程异步处理。
注意:此时 redo log 的事务状态是 prepare,还未真正提交成功,要等 bin log 日志写入磁盘完成后才会变为 commit,事务才算真正提交成功。
redo log 的写入方式?
redo log 采用大小固定,循环写入的方式,当写满后,会重新从头开始循环写,类似一个环状。这样设计原因是 redo log 记录的是数据页上的修改,如果 Buffer Pool 中数据页已经刷到磁盘,这些记录就失效了,新日志会将这些失效的记录覆盖擦除。
注意:redo log 满了,在擦除之前,要确保这些要被擦除记录都已经刷到磁盘中了。在擦除旧记录释放新空间期间,不能再接收新的更新请求,此时 MySQL 性能会下降。因此高并发情况下,合理调整 redo log 大小很重要。
crash-safe 能力是什么?
Innodb 引擎有 crash-safe 能力,即事务提交过程中任何阶段,MySQL 宕机重启后都能保证事务的完整性,已提交的数据不会丢失。这种能力是通过redo log保证的,MySQL 宕机重启,系统将自动检查 redo log,将修改还未写入磁盘的数据从 redo log 恢复到 MySQL 中。
undo log 回滚日志(MySQL 存储引擎 InnoDB 的事务日志)
undo log 记录的是数据修改之前的状态,属于逻辑日志,起到回滚的作用,是保证事务原子性的关键。
举个栗子:假如更新 ID=1 记录的 name 字段,name 原始数据为小王,现改 name 为小张,事务执行 update X set name = 小张 where id =1 语句时,先在 undo log 中记录一条相反逻辑的 update X set name = 小王 where id =1 记录,这样当某些原因导致事务失败,就可借助 undo log 将数据回滚到事务执行前的状态。
那么问题来了:同一个事务的一条记录被多次修改,难道每次都要把数据修改前的状态写 undo log 吗?
不会,因为 undo log 只记录事务开始前数据的原始版本,当再次对这行数据修改时,产生的修改记录会写到 redo log。undo log 负责回滚,redo log负责前滚。
啥是回滚和前滚?
(1)回滚
未提交的事务,即事务未执行 commit。但事务内修改的脏页中,有一部分已刷盘。此时数据库宕机重启,需要回滚来将先前那部分已经刷盘的脏块从磁盘上撤销。
(2)前滚
未完全提交的事务,即事务已经执行 commit,但该事务内修改的脏页中只有一部分数据被刷盘,另一部分还在 buffer pool,此时数据库宕机重启,就要用前滚来将未来得及刷盘的数据从 redo log 中恢复出来并刷盘。
bin log 归档日志(数据库 Server 层二进制逻辑日志、和什么引擎无关)
bin log 记录了用户对数据库所有 sql 操作(不包含查询语句,因为这类操作对数据本身没有修改)。之所以可以称为归档日志,是因为它不会像 redo log 那样循环擦除之前的记录,而是会一直记录日志。一个 bin log 文件默认最大容量1G(可通过 max_binlog_size 参数修改),单个日志超过最大值则会新创建一个文件继续写。
注意:日志可能是基于事务来记录的,而事务不应该跨文件记录,如果 binlog 日志文件达到了最大值但刚好事务还没有提交,此时则不会创建新文件记录,而是继续增大日志。因此 max_binlog_size 的值和实际的 binlog 文件大小不一定相等。
经过上述介绍,binlog 主要用就是主从同步和数据库基于时间点的还原。
那么问题来了,可以没有 binlog 吗(有了 redo log 为啥还需要 bin log)?
需要分场景来看:
主从模式下,binlog 是必须的,因为从库的数据同步需要依赖 binlog;
单机模式下,不考虑数据库基于时间点的还原,binlog 就不是必须的,因为有 redo log 就可以保证 crash-safe 能力了;
redo log 的记录修改落盘后,日志会被覆盖掉,无法用于数据恢复等操作,redo log 是 innodb 引擎层实现的,并不是所有引擎都有;
redo log 与 bin log 的区别?
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-31f3BE3v-1653371876412)(C:/Users/86158/AppData/Roaming/Typora/typora-user-images/image-20220524135519868.png)]](https://img-blog.csdnimg.cn/80823d565e164f76a3d58d034508325e.png)
什么是 redo log 两阶段提交,为什么要这么做?
更新内存后引擎层写 redo log 将状态改成 prepare 为提交第一阶段,Server 层写 bin log,将状态改成 commit 为提交第二阶段。 两阶段提交目的是确保 binl og 和 redo log 数据一致性。
如果不是两阶段提交可能会出现什么情况?
1)假设先写 redo log 再写 bin log,即 redo log 没有 prepare 阶段,写完直接置为commit,然后再写 bin log。如果写完 redo log 后还没写完 bin log 数据库宕机了,重启后系统自动用 redo log 恢复,此时会造成磁盘上数据页数据比 bin log 上的记录数据多,数据不一致。
2)假设先写 bin log 再写 redo log,如果写完 bin log 没写完 redo log 数据库宕机了,那么 bin log 上的记录就会比磁盘上数据页的记录多一些,下次用 bin log 恢复数据,恢复后的数据和原来的数据不一致。
描述一下 redo log 容灾恢复过程?
如果 redo log 是完整(commit 状态)的,直接用 redo log 恢复;
如果 redo log 是预提交 prepare 但不是 commit 状态,此时要去判断 binlog 是否完整,如果完整(commit)那就提交 redo log,再用 redo log 恢复,不完整就回滚事务。