MySQL基础日志
日志分类
在任何一种数据库中,都会有各种各样的日志,记录着数据库工作的方方面面,以帮助数据库管理员追踪数据库曾经发生过的各种事件。MySQL有多种类型的日志,用于记录数据库的操作和状态。以下是一些常见的MySQL日志:
1、错误日志(Error Log):记录MySQL服务器在启动、运行过程中发生的错误和异常情况,如启动错误、语法错误等。
2、查询日志(Query Log):记录所有执行的查询语句,包括SELECT、INSERT、UPDATE、DELETE等操作。可以用于分析查询性能和调试问题,但需要注意对于高负载的系统,开启查询日志可能会对性能产生影响。
3、慢查询日志(Slow Query Log):记录执行时间超过指定阈值的查询语句。慢查询日志可以帮助你找出执行时间较长的查询,以便进行性能优化。
4、二进制日志(Binary Log):记录所有对数据库的更改操作,包括数据修改、表结构变更等。二进制日志可以用于数据恢复、主从复制等场景。
5、事务日志(Transaction Log):也称为重做日志(Redo Log),记录正在进行的事务的更改操作。事务日志用于保证数据库的ACID特性,并支持崩溃恢复。
错误日志
错误日志是 MySQL 中最重要的日志之一,它记录了当 mysqld 启动和停止时,以及服务器在运行过程中发生任何严重错误时的相关信息。当数据库出现任何故障导致无法正常使用时,可以首先查看此日志。
查看日志位置指令 :
sql
show variables like 'log_error%';
执行结果如下所示:
log_error设置为stderr并且MySQL以守护进程(daemon)方式运行,那么,错误日志将被重定向到系统日志文件(如/var/log/syslog)或其他操作系统特定的日志文件中,而不是直接输出到控制台。
可以通过如下配置,设置错误日志的输出位置:
打开MySQL的配置文件my.cnf。该文件通常位于MySQL安装目录下的/etc或者/etc/mysql子目录中。
我的MySQL配置文件是在/docker/mysql/conf
shell
[mysqld]
log_error = /var/lib/mysql/mysql-error.err
重启mysql进行测试。
查看日志内容 :
sql
tail -f /docker/mysql/data/mysql-error.err
查询日志
查询日志中记录了客户端的所有操作语句【CRUD】,而二进制日志不包含查询数据的SQL语句。
默认情况下, 查询日志是未开启的。如果需要开启查询日志,可以设置以下配置 :
sql
# 该选项用来开启查询日志 , 可选值 : 0 或者 1 ; 0 代表关闭, 1 代表开启
general_log = 1
# 设置日志的文件名 , 如果没有指定, 默认的文件名为 host_name.log
general_log_file=file_name
在mysql 的配置文件 /docker/mysql/conf/my.cnf 中配置如下内容 :
sql
general_log=1
general_log_file=/var/lib/mysql/mysql_query.log
配置完毕之后,在数据库执行以下操作 :
sql
select * from tb_user;
update tb_user set age=32 where name='曹操';
执行完毕之后, 再次来查询日志文件 :
开启查询日志注意事项:
1、开启查询日志会对MySQL的性能产生一定影响,特别是在高负载环境下。因此,在生产环境中建议谨慎使用,并根据需要进行开启和关闭。
2、查询日志可能会记录大量的查询语句,导致日志文件过大。可以通过定期清理或限制日志文件大小来处理这个问题。
3、查询日志可能会包含敏感信息(如密码),因此要确保只有授权的人员可以访问查询日志文件。
慢查询日志
慢查询日志记录了所有执行时间超过参数 long_query_time 设置值,long_query_time 默认为 10 秒,最小为 0, 精度可以到微秒。
日志参数配置
默认情况下,MySQL数据库没有开启慢查询日志,需要我们手动来设置这个参数。当然,如果不是调优需要的话,一般不建议启动该参数,因为开启慢查询日志会或多或少带来一定的性能影响。
慢查询日志默认是关闭的 。可以通过两个参数来控制慢查询日志 :
shell
# 该参数用来控制慢查询日志是否开启, 可取值: 1 和 0 , 1 代表开启, 0 代表关闭
slow_query_log=1
# 该参数用来指定慢查询日志的文件名
slow_query_log_file=/var/lib/mysql/slow_query.log
# 该选项用来配置查询的时间限制, 超过这个时间将认为值慢查询, 将需要进行日志记录, 默认10s
long_query_time=10
重启mysql进行测试。
日志内容读取
和错误日志、查询日志一样,慢查询日志记录的格式也是纯文本,可以被直接读取。
1、查询慢查询是否开启以及日志文件位置
sql
SHOW VARIABLES LIKE '%slow_query_log%';
2、查询long_query_time 的值。
sql
SHOW VARIABLES LIKE '%long_query_time%'; -- 查看值:默认10秒
3、执行查询操作
sql
select * from tb_user ;
由于在查询的执行时间小于10s,因此该查询不会记录到慢查询日志中。
模拟慢查询效果:
sql
select * from tb_user where id = 10 and sleep(11) ; -- 使用SLEEP函数可以让查询暂停指定的时间
查看慢查询日志内容:cat slow_query.log
MySQL事务日志
事务概述
事务 是一组操作的集合,它是一个不可分割的工作单位 ,事务会把所有的操作作为一个整体 一起向系 统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败。
作用:保证数据的一致性
举例:转账例如银行转账操作,张三给李四转账1000元,存在以下三个步骤:
- 查询 张三 账户余额
- 张三 账户 -1000
- 李四 账户 +1000
如果第二步骤到第三步骤出现了异常情况报错,则出现数据不一致问题。因此需要事务来完成,业务逻辑执行之前开启事务,执行 完毕后提交事务。如果执行过程中报错,则回滚事务,把数据恢复到事务开始之前的状态。
默认MySQL的事务是自动提交的,即执行完一条DML语句后,MySQL会立即隐式的提交事务。
在实际的业务开发中,有些业务操作要多次访问数据库。一个业务要发送多条SQL语句给数据库执行。需要将多次访问数据库的操作视为一个整体来执行,要么所有的SQL语句全部执行成功。如果其中有一条SQL语句失败,就进行事务的回滚,所有的SQL语句全部执行失败。
简而言之:事务是一组操作的集合,它是一个不可分割的工作单位。事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败。
事务作用:保证在一个事务中多次操作数据库表中数据时,要么全都成功,要么全都失败。
ACID四大特性
A:原子性(Atomicity)
一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
C:一致性(Consistency)
事务的一致性指的是在一个事务执行之前和执行之后数据库都必须处于一致性状态。
如果事务成功地完成,那么系统中所有变化将正确地应用,系统处于有效状态。
如果在事务中出现错误,那么系统中的所有变化将自动地回滚,系统返回到原始状态。
I:隔离性(Isolation)
指的是在并发环境中,当不同的事务同时操纵相同的数据时,每个事务都有各自的完整数据空间。由并发事务所做的修改必须与任何其他并发事务所做的修改隔离。
事务查看数据更新时,数据所处的状态要么是另一事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看到中间状态的数据。
D:持久性(Durability)
指的是只要事务成功结束,它对数据库所做的更新就必须保存下来。即使发生系统崩溃,重新启动数据库系统后,数据库还能恢复到事务成功结束时的状态。
注意:
1、事务的隔离性由 锁机制 实现。
2、而事务的原子性、一致性和持久性由事务的 redo日志 和undo日志来保证。
- redo log称为重做日志 ,它记录了对数据库进行修改的操作,包括插入、更新和删除等。Redo日志的主要作用是保证数据库的持久性和恢复能力。
- undo log称为回滚日志 ,它记录了对数据库进行修改的操作的逆操作,用于实现事务的回滚和MVCC(多版本并发控制)。记录数据修改前的数据
redo日志
[!NOTE]
redo log是InnoDB存储引擎层的日志,又称重做日志文件,用于记录事务操作的变化,记录的是数据修改之后的值,不管事务是否提交都会记录下来。一个事务生成的redo日志是按顺序写入磁盘的,是顺序IO,在实例和介质失败(media failure)时,redo log文件就能派上用场,如数据库掉电,InnoDB存储引擎会使用redo log恢复到掉电前的时刻,以此来保证数据的完整性。
redo log包括两部分:一个是内存中的日志缓冲(redo log buffer),另一个是磁盘上的日志文件(redo log file)。mysql每执行一条DML语句,先将记录写入redo log buffer,后续某个时间点再一次性将多个操作记录写到redo log file。这种先写日志,再写磁盘的技术就是MySQL里经常说到的WAL(Write-Ahead Logging) 技术。
innodb写数据过程
innodb存储引擎是以页为单位(默认16KB)来管理存储空间的。在真正访问页面之前,需要把磁盘上的页缓存到内存【Buffer Pool】之后在可以访问。所有的变更必须先更新缓存池 中的数据。然后缓存池中的脏页会以一定的频率被刷到磁盘,通过缓存池来优化CPU和磁盘之间的鸿沟,这样就保证了整体的性能不会下降太快。
[!IMPORTANT]
InnoDB存储引擎确实是以页为单位来管理存储空间的,默认情况下,这个页的大小是16KB 。在数据库的实际访问中,确实需要先将磁盘上的页缓存到内存中的Buffer Pool,然后才能进行访问。同时,所有的变更操作也必须先更新缓存池 中的数据,这些被修改但还未刷回磁盘的数据页被称为脏页 ,脏页会按照一定频率刷新到磁盘上,通过这种方式,缓存池优化了CPU与磁盘之间的交互,从而保证了数据库性能。InnoDB存储引擎中的Buffer Pool是用来提高数据库读取性能的重要组件。由于磁盘访问速度远慢于内存访问速度,Buffer Pool能够将频繁访问的数据页缓存在内存中,减少对磁盘的IO操作,从而提高整体的数据库性能。当有数据查询请求时,InnoDB首先会在Buffer Pool中查找是否有对应的数据页,如果没有找到,那么就会从磁盘上加载数据页到Buffer Pool中,这样后续对这个数据页的访问就可以直接在内存中进行,避免了重复的磁盘访问。
当Buffer Pool中的数据页被修改后,这些数据页就变成了所谓的"脏页"。InnoDB不会立即将这些变更写回到磁盘,而是根据一定的机制,如后台线程定期刷新、服务器正常关闭或者redo log写满时,才会将脏页刷新到磁盘上。这种策略减少了磁盘写的频次,提升了整体的数据库性能。
总的来说,InnoDB的Buffer Pool对于提升MySQL数据库的性能至关重要。通过合理配置和有效管理Buffer Pool,可以显著提高数据库的读写效率。同时,理解Buffer Pool的工作原理和配置优化方法,对于任何使用InnoDB存储引擎的MySQL数据库管理员来说都是必要的技能。
redo日志的意义
没有redo log存在的问题
缓冲池可以帮助我们消除CPU和磁盘之间的鸿沟,checkpoint机制可以保证数据的最终落盘,然而由于checkpoint 并不是每次变更的时候就触发的,而是master线程隔一段时间去处理的。所以最坏的情况就是事务提交后,刚写完缓冲池,数据库宕机了,那么这段数据就是丢失的,无法恢复。
[!NOTE]
为了避免数据丢失并保证数据的持久性和一致性,InnoDB存储引擎采用了一种名为Checkpoint的技术。Checkpoint是数据库管理系统中的一个操作,用于将脏页(数据脏页和日志脏页)刷新到磁盘,确保数据的持久性和一致性。同时,通过定期将脏页写回磁盘,Checkpoint缩短了数据库的恢复时间,提高了系统的可用性
事务的持久性怎么保证
事务包含持久性的特性,就是说对于一个已经提交的事务,在事务提交后即使系统发生了崩溃,这个事务对数据库中所做的更改也不能丢失。
方案一
在事务提交完成之前把该事务所修改的所有页面都刷新到磁盘,但是这个简单粗暴的做法有些问题
1、修改量与刷新磁盘工作量严重不成比例
有的时候我们仅仅修改了一个页中的某一个字节,但是我们都知道在innodb中是以页 为单位来进行磁盘io的,也就是说我们在进行事务提交的时候不得不将一个完整的页面从内存刷到磁盘,我们都知道一个页面是16KB,只修改了一个字节就需要刷新16KB的数据到磁盘显然有点大题小做。
2、随机IO刷新较慢
一个事务可能包含很多个语句,即使一条语句可能也会修改很多页面,假如该事务修改的这些页面可能并不相邻,这就意味着在将某个事务修改的Buffer Pool的页刷新到磁盘时,需要进行很多的磁盘IO,随机IO比顺序IO慢,尤其对传统的机械磁盘来说。
方案二
我们只是想让已经提交了的事务对数据库中数据所做的修改永久生效,即使后来系统崩溃,在重启后也能把这种修改恢复出来。所以我们其实没有必要在每次事务提交时就把该事务在内存中修改过的全部页面刷新到磁盘,只需要把修改了哪些东西记录一下就好。比如,某个事务将系统表空间中 第10号 页面中偏移量为 100 处的那个字节的值 1 改成 2 。我们只需要记录一下:将第0号表 空间的10号页面的偏移量为100处的值更新为2。Innodb引擎采用的就是方案二。
Innodb引擎采用的是WAL技术(write-ahead logging) , 这种技术就是先写日志,再写磁盘,只有日志写入成功,才算事务提交成功,这里的日志就是redo log。
redo log可以简单分为以下两个部分:
1、重做日志的缓冲 (redo log buffer) ,保存在内存中,是易失的。redo log buffer 大小,默认 16M ,最大值是4096M,最小值为1M。
sql
show variables like '%innodb_log_buffer_size%';
2、重做日志文件 (redo log file ) ,保存在硬盘中,是持久的。redo log日志文件如下所示:(我的日志文件是在/docker/mysql/data)
整体写数据的流程如下所示:
整体流程说明:
第1步:先将原始数据从磁盘中读入内存中来,修改数据的内存拷贝
第2步:生成一条重做日志并写入redo log buffer,记录的是数据被修改后的值
第3步:当事务commit时,将redo log buffer中的内容刷新到 redo log file,对 redo log file采用追加 写的方式
第4步:定期将内存中修改的数据刷新到磁盘中
当发生宕机且数据未刷新到磁盘的时候,可以通过redo log来恢复,保证了ACID中的D,这就是redo log的作用。
redo log的好处和特点
优点:
1、redo日志降低了刷盘频率
2、redo日志占用的空间非常小
特点:
1、redo日志是顺序写入磁盘的
2、事务执行过程中,redo log不断记录
redo log的刷盘策略
redo log的写入并不是直接写入磁盘的,InnoDB引擎会在写redo log的时候先写redo log buffer,之后以 一定策略将redo log buffer刷入到真正的redo log file 中。这里的策略这就是我们要说的刷盘策略。
注意 :redo log buffer刷盘到redo log file的过程并不是真正的刷到磁盘中去,只是刷入到 文件系统缓存(page cache)中去(这是现代操作系统为了提高文件写入效率做的一个优化),真正的写入会交给系统自己来决定(比如page cache足够大了)。那么对于InnoDB来说就存在一个问题,如果交给系统来同步,同样如果系统宕机,那么数据也丢失了(虽然整个系统宕机的概率还是比较小的)。
针对这种情况,InnoDB给出 innodb_flush_log_at_trx_commit 参数,该参数控制 commit提交事务时,如何将 redo log buffer 中的日志刷新到 redo log file 中。
sql
-- 查看innodb_flush_log_at_trx_commit变量的值
SHOW VARIABLES LIKE '%innodb_flush_log_at_trx_commit%';
它支持三种策略:
1、**设置为0(延迟写策略) **:表示每次事务提交时不进行刷盘操作【不会把redo log buffer中的日志写到文件系统缓存】。(系统默认master thread每隔1s进行一次重做日志的同步,就是调用fsync()写到redo log file)。性能最佳、数据风险较高。
也就是说设置为0的时候,大约是每秒刷新写到磁盘,当系统崩溃的时候,会丢失1秒钟的数据
2、设置为1(实时写,实时刷) :表示每次事务提交时都将进行同步(把redo log buffer中的日志写到文件系统缓存),然后调用fsync()进行刷盘操作( 默认值 )。数据安全性较高(系统崩溃也不会丢失数据)、性能稍差(每次提交都写入磁盘,IO性能差)。
3、设置为2(实时写,延迟刷) :表示每次事务提交时都只把 redo log buffer 内容写入 page cache ,不进行同步。由os自己决定什么时候同步到磁盘文件。**性能较高数据安全性较高。**在发生数据库故障时,可能会丢失最近提交的事务的数据,因为尚未刷新到磁盘上的日志文件中。
undo日志
undo日志简介
事务需要保证 原子性 ,也就是事务中的操作要么全部完成,要么什么也不做。但有时候事务执行到一半 会出现一些情况,比如:
情况一:事务执行过程中可能遇到各种错误,比如 服务器本身的错误 , 操作系统错误,甚至是突 然 断电 导致的错误。
情况二:程序员可以在事务执行过程中手动输入ROLLBACK 语句结束当前事务的执行。
以上情况出现,我们需要把数据改回原先的样子,这个过程称之为 回滚 ,这样就可以造成一个假象:这个事务看起来什么都没做,所以符合 原子性要求。
如何保证原子性呢? 每当我们要对一条记录做改动时(INSERT、DELETE、UPDATE),都需要留一手 -------- 要把改动的东西记下来。比如:
1、你插入一条记录 时,至少要把这条记录的主键值记录 下来,之后回滚的时候只需要把这个主键值对应的记录删除 掉就好了(对于INSER,INNODB存储引擎会完成一个DELETE)。
2、你删除一条记录 时,至少把这条记录的内容都记录 下来,这样之后回滚时再把这些内容组成的记录插入 到表中就好了(对于每个DELETE,INNODB存储引擎会执行一个INSERT)。
3、你修改一条记录 时,至少要把修改这条记录前的旧值都记录 下来,这样之后回滚时把这条记录更新为旧值就好了(对于每个UPDATE,INNODB存储引擎会执行一个反向的UPDATE,将修改之前的行放回去)。
MySQL把这些为了回滚而记录的这些内容称之为撤销日志 或者回滚日志(undo log)
redo log是事务持久性的保证,undo log是事务原子性的保证。在事务中更新数据 的前置操作 其实是要先写入一个undo log。
undo日志作用:
1、回滚数据
2、MVCC
undo log存储结构
回滚段与undo页
InnoDB对undo log的管理采用段的方式,也就是回滚段(rollback segment ) 。每个回滚段记录了 1024 个 undo log segment ,而在每个undo log segment段中进行 undo页 (存储的就是回滚记录)的申请。 在 InnoDB1.1版本之前 (不包括1.1版本),只有一个rollback segment,因此支持同时在线的事务限制为 1024 。虽然对绝大多数的应用来说都已经够用。 从1.1版本开始InnoDB支持最大 128个rollback segment ,故其支持同时在线的事务限制提高到 了 128*1024 。
Undo 页是存放 Undo Log 数据的最小单位,用于事务回滚和多版本并发控制(MVCC) 。
shell
# 通过如下的SQL语句查询回滚段的大小
mysql> SHOW VARIABLES LIKE 'innodb_rollback_segments';
+--------------------------+-------+
| Variable_name | Value |
+--------------------------+-------+
| innodb_rollback_segments | 128 |
+--------------------------+-------+
1 row in set (0.00 sec)
undo页的重用
在MySQL中,undo页的重用是指当事务提交或回滚后,之前使用的undo页可以被新的事务重新利用 来存储新的事务的undo信息。这个过程称为undo页的重用,它可以提高存储空间的利用率和系统性能。
Undo页是用于实现事务的回滚和MVCC(多版本并发控制)机制的关键组成部分。当一个事务执行更新操作时,旧的数据会被写入到undo页中,以便在事务回滚时能够恢复到之前的状态。而在MVCC中,每个事务都可以看到自己开始之前的数据库快照,这些快照通过undo页来实现。
当一个事务提交或回滚后,其对应的undo页就不再需要了 。为了节省空间和提高性能,MySQL会将这些不再需要的undo页标记为可重用状态,并将它们添加到一个undo页的空闲链表中。当新的事务需要分配undo页时,MySQL会首先尝试从空闲链表中获取可重用的undo页,而不是分配新的页。
通过重用undo页,可以减少对磁盘空间的需求,提高系统性能。然而,如果系统中存在长时间运行的读事务或长时间运行的只读事务,可能会导致undo页无法及时重用,从而增加了undo段的大小和磁盘空间的占用。
回滚段与事务
1、每个事务只会使用一个回滚段,一个回滚段在同一时刻可能会服务于多个事务。
2、当一个事务开始的时候,会制定一个回滚段,在事务进行的过程中,当数据被修改时,原始的数据会被复制到回滚段。
3、当事务提交时,InnoDB存储引擎会做以下两件事情:
- 将undo log放入列表中,以供之后的purge(清理)操作
- 判断undo log所在的页是否可以重用,若可以分配给下个事务使用
回滚段中的数据分类
uncommitted undo information
未提交的回滚数据(uncommitted undo information)是指在事务执行过程中所做的修改,但尚未提交的数据。这些数据包括已插入、已更新或已删除的记录。在数据库中,当一个事务开始时,它可能会对数据库中的数据进行修改。这些修改操作会生成相应的undo信息,用于在事务回滚时撤销这些修改。这些undo信息被存储在回滚段(rollback segment)中。
当一个事务还没有提交时,其所做的修改被视为未提交的回滚数据 。这意味着其他事务无法看到这些修改,因为它们尚未被永久保存到数据库中。如果该事务被回滚,那么这些未提交的回滚数据将被撤销,数据库恢复到事务开始之前的状态。
需要注意的是,未提交的回滚数据只存在于回滚段中,并且只对当前正在执行的事务可见。其他事务无法读取或修改这些未提交的数据。只有在事务成功提交后,这些数据才会成为已提交的数据,对其他事务可见。
committed undo information
已经提交但未过期的回滚数据(committed undo information)是指在事务执行过程中所做的修改,并且已经成功提交到数据库中的数据。这些数据包括已插入、已更新或已删除的记录。在数据库中,当一个事务成功提交后,其所做的修改会被永久保存到数据库中。同时,相应的undo信息也会保留在回滚段中,以便在需要时进行回滚操作(可能是并发操作)。
已经提交的回滚数据对其他事务可见 ,可以被读取和修改。这意味着其他事务可以看到并访问这些已提交的数据,而不仅仅局限于当前事务的范围内。需要注意的是,已经提交的回滚数据只有在没有过期的情况下才能被保留 。过期数据是指由于某些原因(如长时间未使用)而被标记为可回收的数据。
expired undo information
事务已经提交并过期的数据(expired undo information)是指在事务执行过程中所做的修改,并且已经成功提交到数据库中,但由于某些原因被标记为可回收的数据。这些数据包括已插入、已更新或已删除的记录。在数据库中,当一个事务成功提交后,其所做的修改会被永久保存到数据库中。同时,相应的undo信息也会保留在回滚段中,以便在需要时进行回滚操作。
然而,由于一些策略或配置的原因,一些已经提交的回滚数据可能会被标记为过期。过期数据通常是由于长时间未使用或其他管理策略而被认为是可以回收的数据。过期的回滚数据可能会被后续的事务覆盖或清理,以释放存储空间或提高性能。这意味着虽然这些数据已经成功提交,但它们可能不再对其他事务可见或可访问。
需要注意的是,过期的回滚数据的处理方式可能因数据库管理系统而异。一些数据库系统会自动清理过期数据,而另一些则需要手动管理和清理。
undo log类型
在InnoDB存储引擎中,undo log分为:insert undo log和update undo log
insert undo log
Insert undo log(插入撤销日志)是数据库中用于记录插入操作 的一种撤销日志。因为insert操作的记录,只对事务本身可见,对其他事务不可见(这是事务的隔离性的要求),因此 undo log可以在事务提交之后删除。
update undo log
Update undo log(更新撤销日志)是数据库中用于记录更新操作(delete、update)的一种撤销日志。该undo log可能需要提供MVCC机制,因此不能在事务提交时就进行删除。提交是放入undo log链表,等待purge线程进行最后的删除。
undo log的生命周期
undo log生成过程
简要生成过程
假设有2个数值,分别为A=1和B=2, 然后将A修改为3,B修改为4
shell
1、start transaction ;
2、记录A=1到undo log ;
3、update A = 3 ;
4、记录A=3到redo log ;
5、记录B=2到undo log ;
6、update B = 4 ;
7、记录B=4到redo log ;
8、将redo log刷新到磁盘
9、commit
异常情况分析:
1、在1~8步骤的任意一步系统宕机,事务未提交,该事务就不会对磁盘上的数据做任何影响。
2、如果在8~9之间宕机,恢复之后可以选择回滚,也可以选择继续完成事务提交,因为此时redo log已经持久化。
3、若在9之后系统宕机,内存映射中变更的数据还来不及刷回磁盘,那么系统恢复之后,可以根据redo log把数据刷回磁盘。
只有Buffer Pool没有Redo Log和Undo log的流程:
有了Redo Log和Undo Log之后:
在更新Buffer Pool中数据之前,我们需要先将该数据事务开始之前的状态写入Undo log中。假设更新到一半出错了,我们就可以通过Undo log来回滚到事务开始前。
详细生成过程
对于InnoDB引擎来说,每个行记录除了记录本身的数据之外,还有几个隐藏的列:
1、DB_ROW_ID: 如果没有为表显式的定义主键,并且表中也没有定义唯一索引,那么InnoDB会自动为表添加一个row_id的隐藏列作为主键。
2、DB_TRX_ID: 每个事务都会分配一个事务的ID,当对某条记录发生变更时,就会将这个事务的事务ID写入trx_id中。
3、DB_ROLL_PTR: 回滚指针,本质上就是指向undo log的指针。
当我们执行INSERT时:
sql
begin;
INSERT INTO user (name) VALUES ("tom");
插入的数据都会生成一条insert undo log , 并且数据的回滚指针会指向它。undo log会记录undo log的序号,插入主键的列和值..., 那么在进行rollback的时候,通过主键直接把对应的数据删除即可。
当我们执行UPDATE时:
对于更新的操作会产生update undo log,并且会分更新主键的和不更新主键的,假设现在执行:
sql
update user set name = 'Sun' where id = 1 ;
这时会把老的记录写入新的undo log,让回滚指针指向定的undo log,它的undo log是1,并且新的undo log会指向老的undo log(undo no = 0)。
假设现在执行:
sql
update user set id = 2 where id = 1 ;
对于更新主键的操作,会先把原来的数据deletemark标识打开 ,这时并没有真正的删除数据,真正的删除会交给清理线程去判断,然后在后面插入一条新的数据,新数据也会产生undo log,并且undo log的序号会递增。
可以发现每次对数据的变更都会产生一个undo log,当一条记录被变更多次时,那么,就会产生多条undo log,undo log记录的是变更前的日志,并且每个undo log的序号是递增的,那么,要当回滚的时候,按照序号依次向前,这样就会找到原始数据了。
回滚流程
以上面的例子来说,假设执行rollback,那么,对应的流程应该是这样:
1、通过undo no=3的日志把id=2的数据删除
2、通过undo no=2的日志把id=1的数据的deletemark还原成0
3、通过undo no=1的日志把id=1的数据的name还原成Tom
4、通过undo no=0的日志把id=1的数据删除
undo log的删除
1、针对于insert undo log 因为insert操作的记录,只对事务本身可见,对其他事务不可见。故该undo log可以在事务提交后直接删除,不需要进行purge操作。
2、针对于update undo log 该undo log可能需要提供MVCC机制,因此不能在事务提交时就进行删除。提交时放入undo log链表,等待purge线程进行最后的删除。
redo log和undo log的区别
Redo Log 和 Undo Log 是 MySQL 数据库中两种非常重要的日志文件,它们在事务处理、数据恢复和并发控制方面发挥着关键作用。以下是具体比较:
- 功能设计
- Redo Log:Redo Log 主要用于记录数据库页的物理修改操作,即每次事务对数据页进行更改后,都会将变更以"redo record"的形式写入Redo Log。这样可以确保在系统发生异常重启或宕机时,通过重放Redo Log,已提交事务的修改不会丢失,从而保证事务的持久性。
- Undo Log:Undo Log 记录了事务对数据库所做的更改的相反操作,例如,如果事务执行了一条INSERT语句,那么Undo Log会记录一个DELETE操作;若执行UPDATE,则记录一个相反的UPDATE操作。主要用于事务回滚和多版本并发控制(MVCC)。
- 应用场景
- Redo Log:Redo Log 主要用于数据库崩溃恢复和避免频繁刷盘。它采用WAL(Write-Ahead Logging)策略,先写日志再修改磁盘数据,减少磁盘I/O,提升性能。
- Undo Log:Undo Log 主要用于事务回滚和MVCC。当事务需要回滚时,通过Undo Log可以恢复到事务开始前的数据状态。另外,InnoDB利用Undo Log提供不同事务之间的一致性读视图,避免锁竞争,提高并发性能。
- 日志类型
- Redo Log:物理日志,记录的是物理数据页面的修改信息。
- Undo Log:逻辑日志,记录的是事务执行过程中旧值的备份,用于在事务回滚或并发控制需要时恢复数据。
总的来说,Redo Log 和 Undo Log 分别从物理和逻辑层面确保了事务的ACID特性,尤其是保证了事务的持久性和原子性。Redo Log 确保已提交事务的修改在系统故障下不丢失,而Undo Log则实现了事务回滚和多版本并发控制,两者共同维护了数据库的稳定性和高并发性能。