Mysql主要有三种日志: Redo log、 Undo log、Binlog
- undo log(回滚日志):是 Innodb 存储引擎层生成的日志,实现了事务中的原子性,主要用于事务回滚和 MVCC。
- redo log(重做日志):是 Innodb 存储引擎层生成的日志,实现了事务中的持久性,主要用于掉电等故障恢复;
- binlog (归档日志):是 Server 层生成的日志,主要用于数据备份和主从复制;
Redo,我们的一些写入操作,例如insert update等,会写入到Redo log中持久化,如果数据库宕机了,可以根据Redo log来将这些行为重写写入到数据库中进行相关持久化操作。
为什么需要Redolog
Redo log 是 MySQL 数据库引擎中非常重要的一部分,它主要用于保证数据的一致性和可靠性。具体来说,它的作用有以下几个方面:
数据持久化:在 MySQL 中,所有的修改操作(如 INSERT、UPDATE、DELETE 等)都是在内存中完成的,而不是直接写入磁盘。为了保证数据的持久化,MySQL 引擎会把所有的修改操作记录到 redo log 中,当系统崩溃或重启时,MySQL 引擎可以通过 redo log 中的信息来恢复数据,保证数据的一致性和可靠性。
数据恢复:如果系统崩溃或者意外中断,MySQL 数据库可能会出现数据丢失或者不一致的情况,这时候就需要使用 redo log 来进行数据恢复。MySQL 引擎会根据 redo log 中的信息来恢复数据,保证数据的一致性和可靠性。
数据回滚:如果某个事务执行出错或者用户需要回滚操作,MySQL 引擎可以根据 redo log 中的信息来进行数据回滚,还原数据库到之前的状态。
总之,redo log 是 MySQL 中非常重要的组成部分,它保证了数据的可靠性和一致性,能够在系统崩溃或者其他异常情况下保证数据的完整性。因此,了解 redo log 的作用和原理,以及如何进行优化和调整,对于 MySQL 数据库的管理和优化非常重要。
undo log 是一种用于撤销回退的日志。在事务没提交之前,MySQL 会先记录更新前的数据到 undo log 日志文件里面,当事务回滚时,可以利用 undo log 来进行回滚。如下图:
磁盘的扇区默认是512个字节,设置为512是为了避免跨扇区的情况
BinLog
概述:MySql二进制日志(binlog),也叫变更日志(update log),是Mysql中非常重要的日志。二进制日志中记录了对Mysql数据库执行变更的所有操作,并且记录了语句发生时间、执行时长、操作数据等其他额外信息,但是他不记录select、show等不改变数据的SQL语句。binlog主要用于数据库恢复和主从复制以及审计操作,如果Mysql数据库意外停止,可以使用binlog来查看用户执行了哪些操作,对于数据服务器文件做了哪些修改,然后根据binlog中的记录来恢复数据库服务器
内容:记录了对Mysql数据库执行变更的所有操作,并且记录了语句发生时间、执行时长、操作数据等其他额外信息,但是他不记录select、show等不改变数据的SQL语句。
作用:(1)基于时间的恢复:在备份文件恢复的基础上,通过binlog可以将数据库恢复到某一个时间点。(2)主从复制,在主从复制的模式下,必须在主服务器上开启binlog日志。(3)操作审计:对所有更改数据的操作进行审计。
格式:
(1)statement格式
(2)row模式
(3)mixed模式(前两种的混合)
查看当前正在写入的binlog
查看binlog文件的内容
删除binlog
过期自动删除,查看自动删除的时间的配置:
手动删除binlog:(1)删除所有的binlog文件 reset master
(2)根据编号删除binlog日志文件
(3) 根据创建时间删除
BinLog 和 Redo Log 在一定 程度上都能恢复数据,但是二者有着本质的区别,具体内容如下:
1)BinLog 是 MySQL 本身就拥有的,不管使用何种存储引擎,BinLog 都存在,而Redo Log 是 InnoDB 存储引擎特有的,只有 InnoDB 存储引擎才会输出Redo Log。
2)Bin Log 是一种逻辑日志,记录的是对数据库的所有修改操作,而 Redo Log 是一种物理日志, 记录的是每个数据页的修改。
3)Redo Log 具有幕等性,多次操作的前后状态是一致的, 而 BinLog 不具有幕等性 , 记录的是所有影响数据库的操作。例如插入一条数据后再将其删除,则 Redo Log 前后的状态未发生变化,而 BinLog 就会记录插入操作和删除操作。
4)BinLog 开启事务时,会将每次提交的事务一次性写入内存缓冲区 ,如果未开启事务,则每次成功执行插入、更新和删除语旬时,就会将对应的事务信息写入内存缓冲区,而 Redo Log 是在数据准备修改之前将数据写入缓冲区的 Redo Log 中,然后在缓冲区中修改数据。而且在提交事务时,先将 Redo Log 写入缓冲区,写入完成后再提交事务。
5)Bin Log 只会在事务提交时,一次性写入 BinLog,其日志的记录方式与事务的提交顺序有关,并且一个事务的 BinLog 中间不会插入其他事务的 BinLog。而 Redo Log 记录的是物理页的修改,最后一个提交的事务记录会覆盖之前 所有未提交的事务记录,并且一个事务的 Redo Log 中间会插入其他事务的 Redo Log。
6)BinLog 是追加写入,写完一个日志文件再写下一个日志文件,不会覆盖使用,而Redo Log 是循环写入,日志空间的大小是固定的,会覆盖使用。
7)Bin Log 一般用于主从复制和数据恢复,并且不具备崩溃自动恢复的能力,而 Redo Log 是在服务器发生故障后重启 MySQL,用于恢复事务已提交但未写入数据表的数据。
————————————-
这两个日志有四个区别。
1、适用对象不同:
- binlog 是 MySQL 的 Server 层实现的日志,所有存储引擎都可以使用;
- redo log 是 Innodb 存储引擎实现的日志;
2、文件格式不同:
- binlog 有 3 种格式类型,分别是 STATEMENT(默认格式)、ROW、 MIXED,区别如下:
- STATEMENT:每一条修改数据的 SQL 都会被记录到 binlog 中(相当于记录了逻辑操作,所以针对这种格式, binlog 可以称为逻辑日志),主从复制中 slave 端再根据 SQL 语句重现。但 STATEMENT 有动态函数的问题,比如你用了 uuid 或者 now 这些函数,你在主库上执行的结果并不是你在从库执行的结果,这种随时在变的函数会导致复制的数据不一致;
- ROW:记录行数据最终被修改成什么样了(这种格式的日志,就不能称为逻辑日志了),不会出现 STATEMENT 下动态函数的问题。但 ROW 的缺点是每行数据的变化结果都会被记录,比如执行批量 update 语句,更新多少行数据就会产生多少条记录,使 binlog 文件过大,而在 STATEMENT 格式下只会记录一个 update 语句而已;
- MIXED:包含了 STATEMENT 和 ROW 模式,它会根据不同的情况自动使用 ROW 模式和 STATEMENT 模式;
- redo log 是物理日志,记录的是在某个数据页做了什么修改,比如对 XXX 表空间中的 YYY 数据页 ZZZ 偏移量的地方做了AAA 更新;
3、写入方式不同:
- binlog 是追加写,写满一个文件,就创建一个新的文件继续写,不会覆盖以前的日志,保存的是全量的日志。
- redo log 是循环写,日志空间大小是固定,全部写满就从头开始,保存未被刷入磁盘的脏页日志。
4、用途不同:
- binlog 用于备份恢复、主从复制;
- redo log 用于掉电等故障恢复
如果不小心整个数据库的数据被删除了,能使用 redo log 文件恢复数据吗?
不可以使用 redo log 文件恢复,只能使用 binlog 文件恢复。
因为 redo log 文件是循环写,是会边写边擦除日志的,只记录未被刷入磁盘的数据的物理日志,已经刷入磁盘的数据都会从 redo log 文件里擦除。
binlog 文件保存的是全量的日志,也就是保存了所有数据变更的情况,理论上只要记录在 binlog 上的数据,都可以恢复,所以如果不小心整个数据库的数据被删除了,得用 binlog 文件恢复数据。
引申:
主从复制是怎么实现?
MySQL 的主从复制依赖于 binlog ,也就是记录 MySQL 上的所有变化并以二进制形式保存在磁盘上。复制的过程就是将 binlog 中的数据从主库传输到从库上。
这个过程一般是异步的,也就是主库上执行事务操作的线程不会等待复制 binlog 的线程同步完成。
MySQL 集群的主从复制过程梳理成 3 个阶段:
- 写入 Binlog:主库写 binlog 日志,提交事务,并更新本地存储数据。
- 同步 Binlog:把 binlog 复制到所有从库上,每个从库把 binlog 写到暂存日志中。
- 回放 Binlog:回放 binlog,并更新存储引擎中的数据。
具体详细过程如下:
- MySQL 主库在收到客户端提交事务的请求之后,会先写入 binlog,再提交事务,更新存储引擎中的数据,事务提交完成后,返回给客户端“操作成功”的响应。
- 从库会创建一个专门的 I/O 线程,连接主库的 log dump 线程,来接收主库的 binlog 日志,再把 binlog 信息写入 relay log 的中继日志里,再返回给主库“复制成功”的响应。
- 从库会创建一个用于回放 binlog 的线程,去读 relay log 中继日志,然后回放 binlog 更新存储引擎中的数据,最终实现主从的数据一致性。
在完成主从复制之后,你就可以在写数据时只写主库,在读数据时只读从库,这样即使写请求会锁表或者锁记录,也不会影响读请求的执行。
MySQL 主从复制还有哪些模型?
主要有三种:
- 同步复制:MySQL 主库提交事务的线程要等待所有从库的复制成功响应,才返回客户端结果。这种方式在实际项目中,基本上没法用,原因有两个:一是性能很差,因为要复制到所有节点才返回响应;二是可用性也很差,主库和所有从库任何一个数据库出问题,都会影响业务。
- 异步复制(默认模型):MySQL 主库提交事务的线程并不会等待 binlog 同步到各从库,就返回客户端结果。这种模式一旦主库宕机,数据就会发生丢失。
- 半同步复制:MySQL 5.7 版本之后增加的一种复制方式,介于两者之间,事务线程不用等待所有的从库复制成功响应,只要一部分复制成功响应回来就行,比如一主二从的集群,只要数据成功复制到任意一个从库上,主库的事务线程就可以返回给客户端。这种半同步复制的方式,兼顾了异步复制和同步复制的优点,即使出现主库宕机,至少还有一个从库有最新的数据,不存在数据丢失的风险。
#
从库是不是越多越好?
不是的。
因为从库数量增加,从库连接上来的 I/O 线程也比较多,主库也要创建同样多的 log dump 线程来处理复制的请求,对主库资源消耗比较高,同时还受限于主库的网络带宽。
所以在实际使用中,一个主库一般跟 2~3 个从库(1 套数据库,1 主 2 从 1 备主),这就是一主多从的 MySQL 集群结构。
binlog 什么时候刷盘?
事务执行过程中,先把日志写到 binlog cache(Server 层的 cache),事务提交的时候,再把 binlog cache 写到 binlog 文件中。
一个事务的 binlog 是不能被拆开的,因此无论这个事务有多大(比如有很多条语句),也要保证一次性写入。这是因为有一个线程只能同时有一个事务在执行的设定,所以每当执行一个 begin/start transaction 的时候,就会默认提交上一个事务,这样如果一个事务的 binlog 被拆开的时候,在备库执行就会被当做多个事务分段自行,这样破坏了原子性,是有问题的。
MySQL 给每个线程分配了一片内存用于缓冲 binlog ,该内存叫 binlog cache,参数 binlog_cache_size 用于控制单个线程内 binlog cache 所占内存的大小。如果超过了这个参数规定的大小,就要暂存到磁盘。
什么时候 binlog cache 会写到 binlog 文件?
在事务提交的时候,执行器把 binlog cache 里的完整事务写入到 binlog 文件中,并清空 binlog cache。如下图:
虽然每个线程有自己 binlog cache,但是最终都写到同一个 binlog 文件:
- 图中的 write,指的就是指把日志写入到 binlog 文件,但是并没有把数据持久化到磁盘,因为数据还缓存在文件系统的 page cache 里,write 的写入速度还是比较快的,因为不涉及磁盘 I/O。
- 图中的 fsync,才是将数据持久化到磁盘的操作,这里就会涉及磁盘 I/O,所以频繁的 fsync 会导致磁盘的 I/O 升高。
MySQL提供一个 sync_binlog 参数来控制数据库的 binlog 刷到磁盘上的频率:
- sync_binlog = 0 的时候,表示每次提交事务都只 write,不 fsync,后续交由操作系统决定何时将数据持久化到磁盘;
- sync_binlog = 1 的时候,表示每次提交事务都会 write,然后马上执行 fsync;
- sync_binlog =N(N>1) 的时候,表示每次提交事务都 write,但累积 N 个事务后才 fsync。
在MySQL中系统默认的设置是 sync_binlog = 0,也就是不做任何强制性的磁盘刷新指令,这时候的性能是最好的,但是风险也是最大的。因为一旦主机发生异常重启,还没持久化到磁盘的数据就会丢失。
而当 sync_binlog 设置为 1 的时候,是最安全但是性能损耗最大的设置。因为当设置为 1 的时候,即使主机发生异常重启,最多丢失一个事务的 binlog,而已经持久化到磁盘的数据就不会有影响,不过就是对写入性能影响太大。
如果能容少量事务的 binlog 日志丢失的风险,为了提高写入的性能,一般会 sync_binlog 设置为 100~1000 中的某个数值。
–另外一种解释:
为什么需要 undo log?
我们在执行执行一条“增删改”语句的时候,虽然没有输入 begin 开启事务和 commit 提交事务,但是 MySQL 会隐式开启事务来执行“增删改”语句的,执行完就自动提交事务的,这样就保证了执行完“增删改”语句后,我们可以及时在数据库表看到“增删改”的结果了。
执行一条语句是否自动提交事务,是由autocommit
参数决定的,默认是开启。所以,执行一条 update 语句也是会使用事务的。
那么,考虑一个问题。一个事务在执行过程中,在还没有提交事务之前,如果MySQL 发生了崩溃,要怎么回滚到事务之前的数据呢?
如果我们每次在事务执行过程中,都记录下回滚时需要的信息到一个日志里,那么在事务执行中途发生了 MySQL 崩溃后,就不用担心无法回滚到事务之前的数据,我们可以通过这个日志回滚到事务之前的数据。
实现这一机制就是undo log(回滚日志),它保证了事务的ACID 特性(opens new window)中的原子性(Atomicity)。
undo log 是一种用于撤销回退的日志。在事务没提交之前,MySQL 会先记录更新前的数据到 undo log 日志文件里面,当事务回滚时,可以利用 undo log 来进行回滚。如下图:
每当 InnoDB 引擎对一条记录进行操作(修改、删除、新增)时,要把回滚时需要的信息都记录到 undo log 里,比如:
- 在插入一条记录时,要把这条记录的主键值记下来,这样之后回滚时只需要把这个主键值对应的记录删掉就好了;
- 在删除一条记录时,要把这条记录中的内容都记下来,这样之后回滚时再把由这些内容组成的记录插入到表中就好了;
- 在更新一条记录时,要把被更新的列的旧值记下来,这样之后回滚时再把这些列更新为旧值就好了。
在发生回滚时,就读取 undo log 里的数据,然后做原先相反操作。比如当 delete 一条记录时,undo log 中会把记录中的内容都记下来,然后执行回滚操作的时候,就读取 undo log 里的数据,然后进行 insert 操作。
不同的操作,需要记录的内容也是不同的,所以不同类型的操作(修改、删除、新增)产生的 undo log 的格式也是不同的,具体的每一个操作的 undo log 的格式我就不详细介绍了,感兴趣的可以自己去查查。
一条记录的每一次更新操作产生的 undo log 格式都有一个 roll_pointer 指针和一个 trx_id 事务id:
- 通过 trx_id 可以知道该记录是被哪个事务修改的;
- 通过 roll_pointer 指针可以将这些 undo log 串成一个链表,这个链表就被称为版本链;
版本链如下图:
另外,undo log 还有一个作用,通过 ReadView + undo log 实现 MVCC(多版本并发控制)。
对于「读提交」和「可重复读」隔离级别的事务来说,它们的快照读(普通 select 语句)是通过 Read View + undo log 来实现的,它们的区别在于创建 Read View 的时机不同:
- 「读提交」隔离级别是在每个 select 都会生成一个新的 Read View,也意味着,事务期间的多次读取同一条数据,前后两次读的数据可能会出现不一致,因为可能这期间另外一个事务修改了该记录,并提交了事务。
- 「可重复读」隔离级别是启动事务时生成一个 Read View,然后整个事务期间都在用这个 Read View,这样就保证了在事务期间读到的数据都是事务启动前的记录。
这两个隔离级别实现是通过「事务的 Read View 里的字段」和「记录中的两个隐藏列(trx_id 和 roll_pointer)」的比对,如果不满足可见行,就会顺着 undo log 版本链里找到满足其可见性的记录,从而控制并发事务访问同一个记录时的行为,这就叫 MVCC(多版本并发控制)。具体的实现可以看我这篇文章:事务隔离级别是怎么实现的?(opens new window)
因此,undo log 两大作用:
- 实现事务回滚,保障事务的原子性。事务处理过程中,如果出现了错误或者用户执 行了 ROLLBACK 语句,MySQL 可以利用 undo log 中的历史数据将数据恢复到事务开始之前的状态。
- 实现 MVCC(多版本并发控制)关键因素之一。MVCC 是通过 ReadView + undo log 实现的。undo log 为每条记录保存多份历史数据,MySQL 在执行快照读(普通 select 语句)的时候,会根据事务的 Read View 里的信息,顺着 undo log 的版本链找到满足其可见性的记录
为什么需要 redo log ?
Buffer Pool 是提高了读写效率没错,但是问题来了,Buffer Pool 是基于内存的,而内存总是不可靠,万一断电重启,还没来得及落盘的脏页数据就会丢失。
为了防止断电导致数据丢失的问题,当有一条记录需要更新的时候,InnoDB 引擎就会先更新内存(同时标记为脏页),然后将本次对这个页的修改以 redo log 的形式记录下来,这个时候更新就算完成了。
后续,InnoDB 引擎会在适当的时候,由后台线程将缓存在 Buffer Pool 的脏页刷新到磁盘里,这就是WAL (Write-Ahead Logging)技术。
WAL 技术指的是, MySQL 的写操作并不是立刻写到磁盘上,而是先写日志,然后在合适的时间再写到磁盘上。
过程如下图:
什么是 redo log?
redo log 是物理日志,记录了某个数据页做了什么修改,比如对 XXX 表空间中的 YYY 数据页 ZZZ 偏移量的地方做了AAA 更新,每当执行一个事务就会产生这样的一条或者多条物理日志。
在事务提交时,只要先将 redo log 持久化到磁盘即可,可以不需要等到将缓存在 Buffer Pool 里的脏页数据持久化到磁盘。
当系统崩溃时,虽然脏页数据没有持久化,但是 redo log 已经持久化,接着 MySQL 重启后,可以根据 redo log 的内容,将所有数据恢复到最新的状态。
被修改 Undo 页面,需要记录对应 redo log 吗?
需要的。
开启事务后,InnoDB 层更新记录前,首先要记录相应的 undo log,如果是更新操作,需要把被更新的列的旧值记下来,也就是要生成一条 undo log,undo log 会写入 Buffer Pool 中的 Undo 页面。
不过,在内存修改该 Undo 页面后,需要记录对应的 redo log。
redo log 和 undo log 区别在哪?
这两种日志是属于 InnoDB 存储引擎的日志,它们的区别在于:
- redo log 记录了此次事务「完成后」的数据状态,记录的是更新之后的值;
- undo log 记录了此次事务「开始前」的数据状态,记录的是更新之前的值;
事务提交之前发生了崩溃,重启后会通过 undo log 回滚事务,事务提交之后发生了崩溃,重启后会通过 redo log 恢复事务,如下图:
所以有了 redo log,再通过 WAL 技术,InnoDB 就可以保证即使数据库发生异常重启,之前已提交的记录都不会丢失,这个能力称为crash-safe(崩溃恢复)。可以看出来,redo log 保证了事务四大特性中的持久性。
WAL(Write-Ahead Logging)技术是一种常见的数据持久化技术,通常用于关系型数据库系统中。WAL 技术通过将数据变更操作写入日志文件,来实现数据的持久化,保证数据在发生崩溃等异常情况下的可靠性和一致性。
在 WAL 技术中,当一个事务开始执行时,它的数据变更操作不是直接写入磁盘文件,而是先写入一个日志文件中。这个日志文件称为 WAL 日志。WAL 日志记录了每个事务执行的数据变更操作,包括插入、更新和删除等操作。当事务提交时,系统会先将 WAL 日志文件中的数据刷入磁盘中,然后再将数据写入数据库文件中。这个过程中,WAL 日志起到了缓冲的作用,可以将数据的写入操作缓存到内存中,减少磁盘的访问,从而提高了数据库的性能。
WAL 技术的优点是可以将数据的变更操作记录在日志文件中,保证了数据的可靠性和一致性,并且可以提高数据库的性能。缺点是需要额外的日志文件进行记录,占用了一定的存储空间。同时,WAL 技术的实现也需要一定的技术成本,需要对数据库系统进行改造和优化,以支持 WAL 日志的写入和读取等操作。