对mysql中redologundologbinlog深入理解(代码片段)

敲代码的小小酥 敲代码的小小酥     2022-12-05     328

关键词:

redolog与binlog

redolog是InnoDB引擎中的日志,在其他引擎中没有。binlog是在mysql服务层中的日志,所有的存储引擎都有binlog日志。

那么问题来了,既然有了binlog日志,为何又要有redo log日志呢?

因为在InnoDB引擎中,是支持事务的。事务有持久性的特点。而引入redo log,就是为了保证持久性这个特点的。那么redo log是如何保证持久性的呢?那就先看一下mysql缓冲池的机制。

缓冲池:
Mysql数据库把数据存储到了磁盘上。那么在查询数据,修改数据时,每次都从磁盘中来获取数据,性能是极差的。因此,mysql采用了缓冲池+预读取机制,来减少与磁盘的IO次数。所谓预读取,就是将命中数据相邻的数据,也都一起读取出来,形成一页。然后将这个页的数据,存储到缓冲池中。当对缓冲池中的数据进行增删改查时,就无需再次IO了,直接从内存读取即可。提升了效率。
由此带来的问题就是,我们增删改了缓冲池中的数据,缓冲池数据并不会第一时间刷盘(将缓冲池数据写入磁盘),这就造成了缓冲池中数据与磁盘数据不一致的情况出现,称之为脏页。InnoDB引擎会定时刷一次脏页到磁盘,减少IO次数。
那么当脏页还没刷盘,此时mysql宕机,是不是就造成了数据丢失呢?已经提交的事务,在磁盘是不是就没有被修改呢?是不是就不满足事务持久性原则了呢?因此,redo log应运而生。

WAL:
预写日志机制。详细原理不在这里讲述,可以参考其他资料。大概意思就是,采用顺序IO,将数据的变化,追加到一个日志文件中,然后再将这些数据采用异步或其他方式,采用随机IO,写入最终的磁盘文件中。Mysql的redo和undo就是采用了这种机制。
拿redo日志来说,事务的提交,并不是一下将数据写入到了磁盘中。而是将数据的变化,记录到了redo日志中。redo日志是顺序IO,速度很快。redo日志的格式类似于:

xx 表空间,xx 页,xx 位置,xx 值

这样,写入redo log中的数据,才算事务提交成功,没有写入redo log中的数据,都不算事务提交成功的。当脏页没有刷盘,mysql就宕机时,redo log中记录了提交事务的数据,重启mysql后,从redo log中读取脏页丢失的数据,并恢复,这样,就保证了事务的持久性。

由此可见,redo log是基于解决随机IO影响性能和事务持久性保证两个方面,设计而生的。

两阶段提交:
redo log在InnoDB中工作是按两阶段提交来提交事务的。这跟分布式事务的两阶段提交很像,只不过这是mysql内部的两阶段提交。具体流程如下:

1.服务器收到事务开始的指令,为事务生成一个全局唯一的事务 id。这个事务 id 在记录 binlog 和 redo log 时都会使用。
2.如果缓存池中没有 id=1 所在数据页的数据,从磁盘中找到对应的数据页(注意,这里是一个数据页,不是一条记录),把数据页加载到缓存。
3.修改缓存数据页中 id=1 的数据。
4.记录数据到 redo log buffer、binlog cache。根据 redo log 刷盘的策略,这个过程中 redo log buffer 可能会被刷新到磁盘。
5.服务器收到事务提交的指令。
6.刷新 redo log buffer 到磁盘,并标记该事务的状态为 prepare。此操作称为 redo log prepare。
7.刷新 binlog cache 到磁盘。
8.刷新 redo log buffer 到磁盘,并标记该事务的状态为 commit。此操作称为 redo log commit。
9.向客户端返回事务执行的结果。

可以看到,一个事务写入redo log中时,有两个状态,一个是prepare预提交状态,一个是commit提交状态。为何要设置两个状态呢?因为需要将日志写到binlog日志中,这条事务才算真正提交成功。所以,在先写入redo日志后,设置为prepare状态,等确保写入binlog日志后,再设置为commit状态。以此来保证redo log日志和undo 日志的一致性。

当mysql在第6步和第7步之间宕机时,因为redo log里是prepare状态,而binlog中也没有这条记录,所以,这个事务算提交失败的。当在第7步和第8步之间宕机时,虽然redo log为prepare状态,但是InnoDB会在binlog中查找是否有这条事务,如果有,也视为事务提交成功,执行恢复操作。如果没有,就视为事务提交失败,执行回滚操作。
由上面可知,事务提交成功与否,还是以是否写入binlog为主的。而redo log的作用,就是在mysql重启时,恢复脏页中数据而用的。

redo日志结构:

是一个环形的,可重用的结构。已经写入的事务数据就会被覆盖。这里不做详细阐述,可以参考其他资料。由其结构也可以看出,其就是为恢复数据而用的,而不是备份数据。

binlog日志:
binlog 是 MySQL 服务器层面实现的一种二进制日志,用于记录所有对数据库的更改操作(这种日志被称为逻辑日志)。比如你 update 一条记录,服务器就会记录一条对应的信息到 binlog。但在 InnoDB 中,这个 binlog 是以事务为单位刷新到磁盘的。基于 binlog 的这种特性,一般我们会将 binlog 用于以下几个方面:

数据库增量备份与恢复:在使用备份还原数据后,可以使用 binlog 中记录的内容对备份时间点(简称备份点)后的数据进行恢复。因为 binlog 会还会记录下更改操作的时间,所以 binlog 可以恢复到某一具体时间点的数据。这就为我们删库后提供了除跑路以外的第二个选项:使用 binlog 恢复数据。
主从复制:MySQL 从服务器可以通过订阅 binlog 实现对主服务器的增量复制。
审计:通过对 binlog 中的数据进行审计,判断是否存在安全问题,比如 SQL 注入。

参考文章:MySQL 的 redolog 如何保证数据不丢?其中原理你真的知道吗?

undo log

在执行修改数据之前,都会将修改数据的反向操作,写入undo log中,如:

update user set name = "李四" where id = 1; ---修改之前name=张三

执行这个sql之前,undo log会记录一条相反的update语句:

update user set name = "张三" where id = 1;

这样,当事务执行失败,即没有写入binlog时,就会执行undo log日志,恢复之前数据。

还有一个MVCC作用,这个在之前文章讲过,这里不在讲解。

MySQL - 在一条记录中显示记录对

】MySQL-在一条记录中显示记录对【英文标题】:MySQL-Showrecordpairsinonerecord【发布时间】:2011-08-1102:20:20【问题描述】:我在一张表中有以下数据:Time,Type,Kilometers12:00,1,0.112:30,2,0.214:00,1,0.415:00,2,1.016:00,1,1.216:30,2,1.516:45,1,2.0此数据使... 查看详情

MySQL 中使用 PreparedStatement 对记录进行排序

】MySQL中使用PreparedStatement对记录进行排序【英文标题】:UsingPreparedStatementtosortrecordsinMySQL【发布时间】:2014-09-2618:04:06【问题描述】:我正在尝试编写一个代码,当用户单击“按名称排序”按钮时,我的程序将对我的数据库的... 查看详情

如何对mysql中多个表的字段求和?

】如何对mysql中多个表的字段求和?【英文标题】:Howtosumfieldsacrossmultipletablesinmysql?【发布时间】:2014-10-3103:04:16【问题描述】:我有以下疑问:SELECTadate,sum(b),sum(c),sum(d)FROM((SELECTa.adate,sum(b),sum(c),sum(d)FROMoneWHEREa=\'aa\'GROUPBYa.adate)UN... 查看详情

如何在 MySQL 中使用别名对城市进行建模

】如何在MySQL中使用别名对城市进行建模【英文标题】:HowtomodelcitieswithaliasesinMySQL【发布时间】:2012-02-2915:18:46【问题描述】:一个给定的位置(城市),可以有一个名字和其他可以知道它的别名。我需要在数据库中对此进行建... 查看详情

在 MYSQL 中存储 JSON 数据 - 对性能的影响

】在MYSQL中存储JSON数据-对性能的影响【英文标题】:StoringJSONdatainMYSQL-effectsonperformance【发布时间】:2016-11-1112:06:50【问题描述】:我正在构建一个应用程序,并且在构建我的数据库层时,我有一个问题,我应该创建一个列,以... 查看详情

MongoDB:如何对 MySQL 字段关键字等结果中的记录进行排序 [重复]

】MongoDB:如何对MySQL字段关键字等结果中的记录进行排序[重复]【英文标题】:MongoDB:HowtoordertherecordsinresultslikeMySQLfieldskeyword[duplicate]【发布时间】:2014-10-0218:47:37【问题描述】:我想对结果集中的记录进行排序。我怎样才能像我... 查看详情

MongoDB:如何对 MySQL 字段关键字等结果中的记录进行排序 [重复]

】MongoDB:如何对MySQL字段关键字等结果中的记录进行排序[重复]【英文标题】:MongoDB:HowtoordertherecordsinresultslikeMySQLfieldskeyword[duplicate]【发布时间】:2014-10-0218:47:37【问题描述】:我想对结果集中的记录进行排序。我怎样才能像我... 查看详情

PHP/MySQL:对数据库中的重复事件进行建模,但查询日期范围

】PHP/MySQL:对数据库中的重复事件进行建模,但查询日期范围【英文标题】:PHP/MySQL:Modelrepeatingeventsinadatabasebutqueryfordateranges【发布时间】:2012-11-2722:29:12【问题描述】:我正在开发一个(我打算成为的)简单的PHP/MySQL应用程序... 查看详情

mysql中怎么对varchar类型排序问题

参考技术Aflag和sum是varchar还是int型的如果是int型的那么:1、orderbyfinish,2、orderbysum,3、orderbyiddesc如果是varchar型的那么:1、orderbyto_number(finish),2、orderbyto_number(sum),3、orderbyiddesc本回答被提问者采纳 查看详情

避免在 MySQL 中使用 OR 对简单连接进行全表扫描

】避免在MySQL中使用OR对简单连接进行全表扫描【英文标题】:AvoidfulltablescanonasimplejoinusingORinMySQL【发布时间】:2021-12-0802:25:31【问题描述】:我有这个架构createtabletable1(idintauto_incrementprimarykey,namevarchar(2)null,positionintnull,);createinde... 查看详情

对mysql中redologundologbinlog深入理解(代码片段)

...g是InnoDB引擎中的日志,在其他引擎中没有。binlog是在mysql服务层中的日志,所有的存储引擎都有binlog日志。那么问题来了,既然有了binlog日志,为何又要有redolog日志呢?因为在InnoDB引擎中,是支持事务的... 查看详情

在 mysql where in 子句中对数组使用 implode

】在mysqlwherein子句中对数组使用implode【英文标题】:usingimplodeforarrayinsidemysqlwhereinclause【发布时间】:2017-07-2716:17:05【问题描述】:我试图在mysqlwherein子句中使用数组$result=$myDB->query("SELECTsum(total)astotalFROM".$myDB->prefix("mydata").... 查看详情

在 BASE64 中对每个字符串进行编码以存储在 Mysql 中是不是很好?

】在BASE64中对每个字符串进行编码以存储在Mysql中是不是很好?【英文标题】:IsitgoodtoencodeeachstringinBASE64forstoringinMysql?在BASE64中对每个字符串进行编码以存储在Mysql中是否很好?【发布时间】:2016-02-1115:15:29【问题描述】:在我... 查看详情

按计算距离对 MySQL 结果排序(距离未存储在 DB 中)

】按计算距离对MySQL结果排序(距离未存储在DB中)【英文标题】:SortingMySQLresultsbycalculateddistance(distancenotstoredinDB)【发布时间】:2015-09-1322:19:17【问题描述】:我将“地点”存储在数据库中,我正在使用PHP来访问它们。我想要做... 查看详情

MySQL 5.7,使用存储过程中的变量按列名对表进行排序

】MySQL5.7,使用存储过程中的变量按列名对表进行排序【英文标题】:MySQL5.7,sorttablebycolumnnameusingavariableinStoredprocedure【发布时间】:2021-01-0817:42:58【问题描述】:我有一个包含一些数据的简单表格:DROPTABLEIFEXISTS`MY_TABLE`;CREATETABLE... 查看详情

mysql中聚合函数对null值的处理

(1)max、min、avg和sum直接忽略null,不参与运算。(2)count:count(*):返回的是所有记录的总和,含有null值的记录不会被忽略,也会被计算在内count(column_name):如果这个列名中含有一个值为null,则该条记录会被忽略,此时的返回值为coun... 查看详情

在mysql中存储密码......使用哈希对吗?但是您如何向用户发送忘记的密码?

】在mysql中存储密码......使用哈希对吗?但是您如何向用户发送忘记的密码?【英文标题】:Storingpasswordsinmysql...useahashright?buthowdoyousendtheuseraforgottenpassword?【发布时间】:2012-10-2921:13:24【问题描述】:我一直在研究将用户密码存... 查看详情

如何使用php对mysql数据库中的数据进行加密和解密?

】如何使用php对mysql数据库中的数据进行加密和解密?【英文标题】:Howtoencryptanddecryptdatainthemysqldatabaseusingphp?【发布时间】:2016-07-1317:30:43【问题描述】:当我将内容添加到我的mysql数据库时,我想加密我的php文件中的一些数据... 查看详情