本文主要是介绍MySQL的日志:undo log、redo log、binlog有什么作用,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
目录
从一个update语句说起
undo log
为什么需要undo log
undo log 版本链
undo log 是如何持久化到磁盘?
redo log
为什么需要redo log
redo的组成
redo Log的刷盘策略
redo Log循环写
crash-safe能力
binlog
为什么需要 binlog ?
binlog与redo log的不同
Binlog 的日志格式
Statement
Row
Mixed
binlog 什么时候写到磁盘?
为什么需要两阶段提交?
两阶段提交对异常重启会出现什么现象?
从一个update语句说起
在一个事务中执行下面语句。
update t set age=100 where id<1000 or id>100000;
之后可能会对其有些疑惑:
1.假如我想后撤,退回到update之前,而innodb是支持事务回滚的,即是可以有后悔药吃的,那是如何实现的呢?
2.MySQL的行数据是存储在数据页中的(默认大小是16k)。该update语句需要操作的数据可能分布在多个数据页中,这样就需要对磁盘进行多处读写(即是随机读写),这样 IO 成本、查找成本都很高(随机读写比顺序读写慢很多)。而MySQL的速度却是比较快的,这和我们分析的不一致,这内部是使用了什么办法解决的呢?还有在执行时候,MySQL宕机了,如何保证该记录还存在?
3.MySQL中可以有多个实例,进行读写分离,那就是一个主机器来写,其他的是用来进行读取,那这些实例的数据是如何同步的,如何复制的?
这3个问题就对应到MySQL中的3个重要的日志:undo log(回滚日志)、redo log(重做日志) 、binlog (归档日志)。
- undo log(回滚日志):是 Innodb 存储引擎层生成的日志,实现了事务中的原子性,主要用于事务回滚和 MVCC。
- redo log(重做日志):是 Innodb 存储引擎层生成的日志,实现了事务中的持久性,主要用于掉电等故障恢复;
- binlog (归档日志):是 Server 层生成的日志,主要用于数据备份和主从复制;
undo log
其是在Innodb 存储引擎层生成,其他的存储引擎是没有的。
为什么需要undo log
跟着前面问题1的思路,我想主动回退到事务开始前的数据 或者 在事务执行中,MySQL崩溃了,那要怎么回滚到事务之前的数据呢?
若我们每次在事务执行过程中,都记录下回滚时需要的信息到一个日志里,那么在事务执行过程中发生了 MySQL 崩溃后,就不用担心无法回滚到事务之前的数据,我们可以通过这个日志回滚到事务之前的数据。而且当我们想主动回滚到之前的数据,也可以通过该日志来执行。
这个日志就是 undo log(回滚日志)。这也符合了事务的原子性(Atomicity),事务中的操作要么全部完成,要么都不执行,undo log就可以实现这一特性。
undo log
主要记录了数据的逻辑变化。比如一条 INSERT
语句,对应一条DELETE
的 undo log
;对于每个 UPDATE
语句,对应一条相反的 UPDATE
的 undo log
。这样在发生错误时,就能回滚到事务之前的数据状态。
undo log 还有一个作用,实现 MVCC(多版本并发控制)。
undo log 版本链
行数据中有隐藏字段,trx_id和roll_pointer。通过 roll_pointer 指针将这些 undo log 串成一个链表,这个链表就被称为版本链。
这里就不重点讲解undo log日志中的格式和字段。
上图中的(1):
从图中可以得知此时插入的事务ID是1,此时插入会同时生成一条 undo log ,并且行记录上的 roll_pointer 会指向这条 undo log ,而这条 undo log的类型是TRX_UNDO_INSERT_REC,代表是 insert 生成的,里面还存储了主键值(还有其他值,这里就不做过多介绍)。
所以 InnoDB 可以根据 undo log 里的主键的值,找到这条记录,然后删除该主键对应的行数据来实现回滚的效果。因此可以简单地理解 undolog 里面存储的就是当前操作的反向操作,认为里面存了个delete 30即可。
上图中的(2):
此时事务1提交,然后另一个事务ID为 2的事务执行 update t set age=3 where id=30 ,此时的行记录和 undolog 就如上图所示的(2)。
之前 insert 产生的 undo log没了,insert 的事务提交了之后对应的 undolog 就被回收了,为什么呢?
因为不可能有别的事务会访问比这还要早的版本了,访问插入之前的版本?插入之前的版本都没有这行数据,要如何访问??没得访问的。所以insert事务提交后对应的undo log就回收了。
(看到很多文章写的是insert对其他事务不可见,只对本事务可见,所以提交后就可删除,感觉这理由是不妥的)
update 产生的 undolog,其类型为TRX_UNDO_UPD_EXIST_REC,并且记录上一版本的trx_id和数据。
上图中的(3):
此时事务 2提交,然后另一个 ID 为 3 的事务执行update t set name='a3' where id=30,此时的记录和 undolog 就如上图所示中的(3)。
update 产生的 undolog 不会马上删除,因为可能有别的事务需要访问之前的版本,所以不能删。这样就串成了一个版本链,可以看到该记录本身加上两条 undo log,这条 id 为 30的记录就共有三个版本。
其undo long 版本链是实现 MVCC(多版本并发控制)关键因素之一。MVCC 是通过 行数据隐藏字段+ReadView + undo log 实现的。undo log 为每条记录保存多份历史数据,MySQL 在执行快照读(普通 select 语句)的时候,会根据事务的 Read View 里的信息,顺着 undo log 的版本链找到满足其可见性的记录。具体内容可以查看MySQL的事务隔离是如何实现的?
undo log 是如何持久化到磁盘?
undo log 也是需要持久化保护的,所以写 undo log 也会记录相应的 redo log,通过 redo log 保证持久化。
redo log
redo log
(重做日志)是InnoDB
存储引擎独有的,它让MySQL
拥有了崩溃恢复能力。
为什么需要redo log
事务的四大特性之一:持久性,即是只要事务提交成功,那么对数据库做的修改就被永久保存下来了。那MySQL是如何实现这一特性的呢?最简单的方法是在每次事务提交的时候,将该事务涉及修改的数据页全部刷新到磁盘中。但是这么做会有较严重的性能问题,主要体现在两个方面:
- 修改量与刷新磁盘工作量严重不成比例。因为Innodb是以页为单位进行磁盘交互的,而一个事务很可能只修改一个数据页里面的几个字节,这个时候将完整的数据页刷到磁盘的话,太浪费资源了。
- 随机Io刷新较慢。一个事务是可能涉及修改多个数据页的,并且这些数据页可能在物理上并不连续,使用随机IO写入性能太差。
我们其实没有必要在每次事务提交时就把该事务在内存中修改过的全部数据页刷新到磁盘,只需要把修改了哪些东西记录一下就好。
比如, 第 10号页中偏移量为 20 处的那个字节的值 1 改成 3 。我们只需要记录一下:将第 10号页面的偏移量为 20 处的值 更新为 3。
那我们就把这个修改写到一个文件日志中,而这些写也是顺序写的,在日志的末尾添加。这样就可以解决上面所说的两个问题。
而MySQL为了提高的性能,对于增、删、改这种操作都是在内存中完成的。InnoDB 的策略是尽量使用内存。数据都是存在磁盘中的,当要更新一条记录的时候,得先要从磁盘读取该记录,然后在内存中修改这条记录。Innodb 存储引擎设计了一个缓冲池(Buffer Pool),来提高数据库的读写性能。在 MySQL 启动的时候,InnoDB 会为 Buffer Pool 申请一片连续的内存空间,然后按照默认的16KB
的大小划分出一个个的页, Buffer Pool 中的页就叫做缓存页。
那redo log可以用个比喻来说明:(该比喻来自《MySQL45讲》)
《孔乙己》这篇文章,酒店掌柜有一个粉板,专门用来记录客人的赊账记录。如果赊账的人不多,那么他可以把顾客名和账目写在板上。但如果赊账的人多了,粉板总会有记不下的时候,这个时候掌柜一定还有一个专门记录赊账的账本。
如果有人要赊账或者还账的话,掌柜一般有两种做法:
- 一种做法是直接把账本翻出来,把这次赊的账加上去或者扣除掉;
- 另一种做法是先在粉板上记下这次的账,等打烊以后或空闲时间再把账本翻出来核算。
在生意很忙时,掌柜一定会选择后者,因为前者操作麻烦并且慢。相比之下,还是先在粉板上记一下方便快速。掌柜如果没有粉板的帮助,每次记账都得翻账本,效率会比较低。
同样,在 MySQL 里也有这个问题,如果每一次的更新操作都需要写进磁盘,然后磁盘也要找到对应的那条记录,然后再更新,整个过程 IO 成本、查找成本都很高。为了解决这个问题,MySQL 的设计者就用了类似酒店掌柜粉板的思路来提升更新效率。
掌柜记账的账本是数据文件,记账用的粉板是日志文件(redo log),掌柜的记忆就是内存。
而粉板和账本配合的整个过程,其实就是 MySQL 里经常说到的 WAL 技术,WAL 的全称是 Write-Ahead Logging,它的关键点就是先写日志,再写磁盘,也就是先写粉板,等不忙的时候再写账本。
具体来说,当有一条记录需要更新的时候,InnoDB 引擎就会先把记录写到 redo log(粉板)里面,并更新内存,这个时候更新就算完成了。同时,InnoDB 引擎会在适当的时候,将这个操作记录更新到磁盘里面,而这个更新往往是在系统比较空闲的时候做,这就像打烊以后掌柜做的事。
但如果某天赊账的特别多,粉板写满了,又怎么办呢?这个时候掌柜只好放下手中的活儿,把粉板(redo log)中的一部分赊账记录更新到账本(磁盘数据文件)中,然后把这些记录从粉板上擦掉,为记新账腾出空间。
redo log解决了上面说的两个问题,而且可以保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为crash-safe。
redo的组成
redo log包括两部分:
- 内存中的日志缓冲(redo log buffer)。在服务器启动时就向操作系统申请了一大片称之为r
edo Log Buffer
的连续内存空间。这片内存空间被划分成若干个连续的Redo Log Block
。一个Redo Log Block占用512字节大小。 - 磁盘上的日志文件(redo log file)
mysql每执行一条DML语句,先将记录写入redo log buffer,后续某个时间点再一次性将多个操作记录写到redo log file。
为什么需要先写到内存中的缓冲中,不直接就写到日志文件中呢?可以想象一个场景,事务开启,这过程中执行了多次update,那要是每次update就写到磁盘的日志文件中,那可能性能就损耗,所以可以在内存中存起来,等到事务提交,就一次性刷新到磁盘日志文件中。一个事务中,原子性要求,只有所有的update成功或所有的update失败,所以不会有问题。
需要注意:在计算机的操作系统中用户空间缓冲区的数据是无法直接写入磁盘的,中间必需经过操作系统缓冲区(OS Buffer)。因此,redo log buffer写入redo log file,实际上会先写入OS Buffer,然后再调用fsync()将其刷入到redo log file。
- write指的是MySQL从redo log buffer(或者是Buffer Pool)中将内容写到系统的page cache中,并没有持久化到系统磁盘上。这个速度其实是很快的。
- fsync指的是从系统的cache中将数据持久化到系统磁盘上。这个速度可以认为比较慢,而且也是IOPS升高的真正原因。
那 redo log buffer 是何时写入磁盘呢?
- 事物提交时把它对应的那些redo log写入到磁盘中去(这个动作由相关参数控制)
- 当redo log buffer 使用量达到了参数innndb_log_buffer_size的一半时,会触发落盘。
- 会有一个后台线程,每隔1秒就会将redo log block刷新到磁盘文件中去。
- MySQL关闭时也会将其落盘。
redo Log的刷盘策略
前面讲的只是一种刷盘策略,MySQL给了3种策略的,由参数innodb_flush_log_at_trx_commit控制。
-
设置为1(默认值):当事务提交时,MySQL必须将redolog-buffer中的数据写入到操作系统缓存中 ,并刷新进磁盘中。确保只要commit是成功的,磁盘上就得有对应的redolog日志。这也是最安全的情况。
-
设置为0:每秒写一次日志并将其刷新到磁盘。
-
设置为2:当事务提交时,将redolog-buffer中的数据刷新进OS Cache中,然后依托于操作系统每秒刷新一次的机制将数据同步到磁盘中,会存在丢失的风险。
如果配置是 0 ,那当 MySQL 突然挂了,数据就丢了。当配置是 2,那 MySQL 挂了没事,如果是机器宕机了,那数据也没了,所以只有配置为 1 的时候,才能保证已经提交事务的数据一定被持久化了,但是刷盘效率肯定比不刷盘低,所以一些允许数据丢失的场景下,可以修改配置使得性能提升。
其实 InnoDB 还有个后台线程 Master Thread 在后面刷盘,即使这个事务还未提交,每秒都会把 log buffer 中的内容持久化到磁盘中,因此如果是大事务,那么提交事务时候速度也很快,因为之前的修改已经落盘啦,不会只等提交事务那一刻落盘。
redo Log循环写
redo log file 这个是物理文件,里面存放的是物理日志,可以简单地认为log存储的内容是哪个表空间的哪一页的哪个地方的值是什么。
默认情况下, InnoDB 存储引擎有 1 个重做日志文件组( redo log Group),其由2 个 redo log 文件组成。(假设命名为log file1 ,log file2;文件个数和文件的大小是可以设置的,不同MySQL版本的redo log命名和存储位置有区别)
redo log日志的大小是固定的,是循环写入,从头开始写,写到末尾就又回到开头,相当于一个环形。
write pos 是当前记录的位置,一边写一边后移,写到第 3 号文件末尾后就回到 0 号文件开头。checkpoint 是当前要擦除的位置,也是往后推移并且循环的,擦除记录前要把记录更新到数据文件。
write pos 和 checkpoint 之间的是“粉板”上还空着的部分,可以用来记录新的操作。如果 write pos 追上 checkpoint,表示“粉板”满了,这时候不能再执行新的更新,得停下来先擦掉一些记录,把 checkpoint 推进一下。
把 checkpoint 推进一下,这就对应了循环写入,那 log file1 之前的内容不就被覆盖了嘛?
是的,但是只有那些修改已经应用到真正的记录上的 redo log 才可以被覆盖,也就是对应的数据脏页已经刷盘了的数据。
这不难理解,因为 redo log 的作用就是为了崩溃恢复,那真正的数据已经落盘了就用不到对应的 redo log 了,所以这部分数据可以被覆盖。
crash-safe能力
有了 redo log,InnoDB 就可以保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为crash-safe。
要理解 crash-safe 这个概念,可以想想我们前面赊账记录的例子。只要赊账记录记在了粉板(redo log)上或写在了账本(磁盘数据文件)上,之后即使掌柜忘记了,比如突然停业几天,恢复生意后依然可以通过账本和粉板上的数据明确赊账账目。
binlog
上面我们聊到的粉板 redo log 是 InnoDB 引擎特有的日志,而 Server 层也有自己的日志,称为 binlog(归档日志)。
为什么会有两份日志呢?
因为最开始 MySQL 里并没有 InnoDB 引擎。MySQL 自带的引擎是 MyISAM,但是 MyISAM 没有 crash-safe 的能力,binlog 日志只能用于归档。而 InnoDB 是另一个公司以插件形式引入 MySQL 的,既然只依靠 binlog 是没有 crash-safe 能力的,所以 InnoDB 使用另外一套日志系统——也就是 redo log 来实现 crash-safe 能力。
bin log
用于记录数据库执行的写入性操作,也就是在事务commit
阶段进行记录,以二进制的形式保存于磁盘中。
bin log
是逻辑日志,是以追加的方式进行写入的。
为什么需要 binlog ?
binlog 文件保存的是全量的日志,也就是保存了所有数据变更的情况,理论上只要记录在 binlog 上的数据,都可以恢复,所以如果不小心整个数据库的数据被删除了,得用 binlog 文件恢复数据。
而使用redo log不能恢复整个数据库的数据,因为 redo log 是循环写,是会边写边擦除日志的,只记录未被刷入磁盘的数据的物理日志。
所以需要binlog来实时备份,主从复制。
binlog与redo log的不同
- redo log 是 InnoDB 引擎特有的;binlog 是 MySQL 的 Server 层实现的,所有引擎都可以使用。
- redo log 是物理日志,记录的是“在某个数据页上做了什么修改”;binlog 是逻辑日志,记录的是这个语句的原始逻辑,比如“给 ID=2 这一行的 c 字段加 1 ”,其有三种格式类型:binlog 有 3 种格式类型,分别是 STATEMENT、ROW、 MIXED。
- redo log 是循环写的,空间固定会用完;binlog 是可以追加写入的。“追加写”是指 binlog 文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。
Binlog 的日志格式
上面说到binlog有三种格式类型:
-
STATEMENT:基于SQL语句的复制(statement-based replication)
-
ROW:基于行的复制(row-based replication)
-
MIXED:混合模式复制(mixed-based replication)。
在 MySQL 5.7.7
之前,默认的格式是 STATEMENT
,在 MySQL 5.7.7
及更高版本中,默认值是 ROW
。日志格式通过 binlog-format
指定。
Statement
每一条会修改数据的sql都会记录在binlog中,不需要记录每一行的变化,减少了binlog日志量,节约了IO, 提高了性能。
缺点:由于记录的只是执行语句,为了这些语句能在slave上正确运行,因此还必须记录每条语句在执行的时候的一些相关信息,以保证所有语句能在slave得到和在master端执行的时候相同的结果。另外mysql的复制,像一些特定函数的功能,slave与master要保持一致会有很多相关问题。
STATEMENT 有动态函数的问题,比如使用用了 uuid这些函数,你在主库上执行的结果并不是你在从库执行的结果,这种随时在变的函数会导致复制的数据不一致;简单来说,假如是删除id=1的行,而在主从复制,到从机中操作的,可能就是删除了id=3的。
Row
为了解决上述问题,我们需要指定为row
,记录的内容不再是简单的SQL
语句了,还包含操作的具体数据,记录内容如下。
row格式记录的内容要通过mysqlbinlog工具解析出来。
条件后面的@1、@2、@3 都是该行数据第 1 个~3 个字段的原始值(因为这张表只有 3 个字段)。
这样就能保证同步数据的一致性,通常情况下都是指定为row,这样可以为数据库的恢复与同步带来更好的可靠性。
但是这种格式,需要更大的容量来记录(比如批量 update),比较占用空间,恢复与同步时会更消耗IO资源,影响执行速度。
Mixed
从5.1.8版本开始,MySQL提供了Mixed格式,实际上就是Statement与Row的结合。在Mixed模式下,一般的语句修改使用statment格式保存binlog,如一些函数,statement无法完成主从复制的操作,则采用row格式保存binlog,MySQL会根据执行的每一条具体的sql语句来区分对待记录的日志形式,也就是在Statement和Row之间选择一种。
statement 格式的 binlog 可能会导致主备数据不一致,不要使用。ROW格式虽然占用空间多,但对数据恢复,复制等有很大帮助, 推荐使用ROW格式。
binlog 什么时候写到磁盘?
binlog也是先写到内存中的,该内存部分称为binlog cache。一个事务过程中可能有多个操作,那就可以先把binlog写到内存中,等到事务提交后,就把binlog写到磁盘日志文件中(持久化)。
这部分和redo log一样的,是先把binlog写入到操作系统的缓存中,之后再通过调用fsync()将其刷入到磁盘binlog上。
binlog cache记录着所有未提交的事务在运行期间产生的binlog数据,binlog cache 是在每个线程内空间独立的。如果启用了bin log日志,MySQL 会为每个客户端分配一个二进制日志缓存。如果经常使用大型事务,则可以增加此缓存大小以获得更好的性能。
如果binlog cache空间足够,在事务提交的时候,cache中的内容会被清空,同时这些数据会被写入到日志文件中。
因为bin log内容无论多大在事务提交时都需要一次性写入,所以当 bin log cache放不下的时候,就需要暂存到磁盘(生成一个binlog cache 临时文件,同时清空binlog cache,由参数max_binlog_cache_size控制该文件大小),然后提交被写入到日志文件中。binlog临时文件会被存放到 tmpdir 的目录下。
MySQL提供一个参数 sync_binlog来控制数据库的 binlog 刷到磁盘上的频率,其取值范围:0~N:
- 取值0:每次提交都将binlog 从binlog cache中 写到 OS Buffer,而不fsync到磁盘
- 取值1:每次提交事务都将binlog fsync到磁盘上
- 取值N:每次提交事务都将binlog write到磁盘上,累计N个事务之后,执行fsync
建议设置sync_binlog
为1,这样每次事务commit
时就会把binlog
写入磁盘中,这样也可以保证mysql异常重启之后bin log
日志不会丢失。
为什么需要两阶段提交?
经过前面的分析,事务提交时候,redo log和binlog都需要持久化到磁盘中。但是这两个操作是两个独立的逻辑,那怎么保证他们的数据一致性呢?
他们是先后顺序,假如是redo log先写,要是写完后机器宕机,binlog没有写到,那就出现了数据不一致情况。
假设当前表t的主键字段ID=2 的行,字段 c 的值是 0,再假设执行 update t set c=100 where ID=2;过程中在写完第一个日志后,第二个日志还没有写完期间发生了 crash,会出现什么情况呢?
- 先写 redo log 后写 binlog(这个写是写到磁盘中,已持久化的)。假设在 redo log 写完,binlog 还没有写完的时候,MySQL 进程异常重启。由于我们前面说过的,redo log 写完之后,系统即使崩溃,仍然能够把数据恢复回来,所以恢复后这一行 c 的值是 1。 但是由于 binlog 没写完就 crash 了,这时候 binlog 里面就没有记录这个语句。因此,之后备份日志的时候,存起来的 binlog 里面就没有这条语句。 然后如果用这个 binlog 来恢复临时库,由于这个语句的 binlog 丢失,这个临时库就会少了这一次更新,恢复出来的这一行 c 的值就是 0,与原库的值不同。
- 先写 binlog 后写 redo log。如果在 binlog 写完之后 crash,由于 redo log 还没写,崩溃恢复以后这个事务无效,所以这一行 c 的值是 0。但是 binlog 里面已经记录了“把 c 从 0 改成 1”这个日志。所以,在之后用 binlog 来恢复的时候就多了一个事务出来,恢复出来的这一行 c 的值就是 1,与原库的值不同。
那MySQL是如何处理这个问题的?这就需要用到两阶段提交。
它可以保证多个逻辑操作要不全部成功,要不全部失败,不会出现半成功的状态。两阶段提交把单个事务的提交拆分成了 2 个阶段,分别是 准备(Prepare)阶段 和 提交(Commit)阶段。
这就是说,就要保证redolog和binlog持久化到磁盘,事务才提交成功。
那在MySQL中如何设置呢?这就是我们在MySQL中常说的“双1”配置。
binlog 和 redo log 在内存中都对应的缓存空间,它们持久化到磁盘的时机分别由下面这两个参数控制。我们为了避免日志丢失的风险,会将这两个参数设置为 1:
- 当 sync_binlog = 1 的时候,表示每次提交事务都会将 binlog cache 里的 binlog 直接持久到磁盘;
- 当 innodb_flush_log_at_trx_commit = 1 时,表示每次事务提交时,都将缓存在 redo log buffer 里的 redo log 直接持久化到磁盘。
两阶段提交对异常重启会出现什么现象?
我们来看看在两阶段提交的不同时刻,MySQL 异常重启会出现什么现象?下图中有时刻 1 和时刻 2都有可能发生崩溃:
这两个时刻,redo log都是处于prepare状态的。
在 MySQL 重启后会按顺序扫描 redo log 文件,碰到处于 prepare 状态的 redo log,就拿着 redo log 中的 XID 去 binlog 查看是否存在此XID。(redo log和binlog有一个共同的数据字段,叫 XID)
时刻1:写入binlog
时发生异常。MySQL
根据redo log
日志恢复数据,发现redo log
还处于prepare
阶段,并且没有对应binlog
日志,就会回滚该事务。
时刻2:redo log和binlog都写入成功,只是redolog还是处于prepare。这时并不会回滚事务,虽然redo log
是处于prepare
阶段,但是能通过XID找到对应的binlog
日志,所以MySQL
认为是完整的,就会提交事务恢复数据。
可以看到,对于处于 prepare 阶段的 redo log,即可以提交事务,也可以回滚事务,这取决于是否能在 binlog 中查找到与 redo log 相同的 XID。如果有就提交事务,如果没有就回滚事务。这样就可以保证 redo log 和 binlog 这两份日志的一致性了。
所以说,两阶段提交是以 binlog 写成功为事务提交成功的标识,因为 binlog 写成功了,就意味着能在 binlog 中查找到与 redo log 相同的 XID。
这篇关于MySQL的日志:undo log、redo log、binlog有什么作用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!