#20260105
MySQL日志分类
MySQL有不同类型的日志:慢查询日志、通用查询日志、错误日志、事务日志、二进制日志等几大类,在MySQL8之后又新增了两种日志------中继日志、数据定义语句日志。其中比较重要的是二进制日志binlog (归档日志) 、事务日志redo log(重做日志) 和 undo log(回滚日志)。
MySQL日志主要包括八种
- 慢查询日志(slow query log):记录所有执行时间超过long_query_time的所有查询,方便对查询进行优化
- 通用查询日志(general log):记录索引连接的起始时间和终止时间,以及连接发送给数据库服务的所有指令,对复原操作的实际场景、发现问题、数据库操作的审计都有帮助
- 二进制日志(bin log):记录所有更改数据的语句,用于主从服务器之间的数据同步、服务器遇到故障时数据的无损恢复
- 错误日志(error log):记录MySQL服务的启动、运行或停止MySQL服务时出现的问题,方便了解服务器的状态,从而对服务器进行维护
- 中继日志(relay log):用于主从服务器架构,从服务器用来存放主服务器二进制日志内容的一个中间件文件。从服务器通过读取中继日志的内容,来同步主服务器上的操作
- 回滚日志(undo log):是Innodb存储引擎层生成的日志,实现了事务中的原子性,主要用于事务回滚和MVCC
- 重做日志(redo log):是Innodb存储引擎层生成的日志,实现了事务中的持久性,主要用于掉电等故障恢复
- 数据定义语句日志:记录数据定义语句执行的元数据操作
除了二进制日志外,其他日志均为文本文件。默认情况下,所有日志均创建于MySQL数据目录中
查询日志
查询日志中记录了客户端的所有操作语句(包括所有的增删改查、DDL、DML、DQL语句),而二进制日志不包含查询数据的SQL语句。默认情况下,查询日志是未开启的。如果需要开启查询日志,可以设置以下配置:

如果想要禁用查询日志,可将general_log设置为0,而后重启MySQL服务sudo systemctl restart mysql
慢查询日志记录了所有执行时间超过参数 long_query_time 设置值并且扫描记录数不小于 min_examined_row_limit 的所有SQL语句的日志,默认未开启。long_query_time 默认为10秒,最小为0,精度可以到微秒。如果要开启慢查询日志,需要在MySQL的配置文件(/etc/my.cnf)中配置如下信息:

同理,如果想要禁用慢查询日志,可将slow_query_log设置为0,而后重启MySQL服务sudo systemctl restart mysql
错误日志是MySQL中最重要的日志之一,它记录了当mysqld启动和停止时,以及服务器在运行过程中发生任何严重错误时的相关信息。当数据库出现任何故障导致无法正常使用时,建议首先查看此日志。
该日志是默认开启的,默认存放目录/var/log/,默认的日志文件名为mysqld.log。查看日志位置:
-- 登录mysql,查看系统变量 show variables like '%log_error';

bin log
作用:
- 灾难时的数据恢复(通过使用 mysqlbinlog 工具来恢复数据);
- MySQL的主从复制(在Master端开启binlog ,每个从库读取binlog、写到暂存日志relay log中,slave端重放binlog 从而达到主从数据一致)。
细节:
在出现IO瓶颈时,将sync_binlog设置成一个较大的值,能提升性能。同样的,若机器宕机会 丢失最近N个事务的binlog日志
undo log
undo log(回滚日志) :是Innodb存储引擎层生成的日志,实现了事务中的原子性 ,主要用于事务回滚和MVCC。它是InnoDB存储引擎在insert、update、delete的时候产生的便于数据回滚的日志。在数据更新之前,MySQL就需要先把更新前的数据记录到 undo log 日志中,当事务回滚时,可以利用 undo log 来进行回滚。作用包含两个------提供回滚、MVCC(多版本并发控制)。undo log主要分为两种:
-
binlog用于记录数据库执行的DDL、DML操作信息(不包括查询),以二进制的形式保存在磁盘中。binlog是mysql的逻辑日志,并且由Server层进行记录,使用任何存储引擎的mysql数据库都会记录binlog日志。
-
binlog是通过追加的方式进行写入的,可以通过max_binlog_size参数设置每个binlog文件的大小,当文件大小达到给定值之后,会生成新的文件来保存日志。
-
MySQL在完成一条更新操作后,Server层会生成一条binlog,binlog采用WAL模式(Write-Ahead Logging,redo log也是采用WAL模式),先写日志,再写磁盘:事务执行过程中,先把日志写入binlog cache,事务提交时,再把binlog cache写到binlog文件中
写入/刷盘机制
事务执行过程中,先把日志写入binlog cache,事务提交时,再把binlog cache写到binlog文件中。一个事务的binlog不能被拆开,确保一次性写入,系统将给每个线程分配一块内存作为binlog cache
对于InnoDB存储引擎而言,只有在事务提交时才会记录binlog,那么binlog什么时候才会将内存中的数据刷到磁盘呢?其实mysql是通过sync_binlog参数控制binlog的刷盘时机,取值范围是0-N【write和fsync的时机,由参数sync_binlog控制,默认为0】
-
0:每次提交事务都只write,由系统自行判断什么时候执行fsync。虽然性能得到提升,但是机器宕机,page cache里的binlog会丢失(不去强制要求,由系统自行判断何时写入磁盘)
-
1:每次提交事务都会执行fsync,如同redolog刷盘流程一样(每次commit的时候都要将binlog写入磁盘)
-
N:每次提交事务都write,但累计N个事务后才fsync(每N个事务,才会将binlog写入磁盘)
-
insert undo log:当insert的时候,产生的undo log日志只在回滚时需要,在事务提交后,可被立即删除(因为这种log只是对本事务可见,其他事务不可见,所以当事务提交后,这种类型的undo log就会被系统直接删除回收,也就是该undo log占用的undo页面链表被释放)。
-
update undo log:update、delete的时候,产生的undo log日志不仅在事务回滚时需要,在快照读时也需要(也就是MVCC),所以不能在事务提交后马上删除,只在提交后放入undo log的链表,等待purge线程进行最后的删除
隐藏字段 ------ 事务ID(TRX_ID)、ROLL_PTR
当我们一张表,我们在查看表结构的时候,InnoDB还会自动的给我们添加三个隐藏字段,分别是:DB_TRX_ID,DB_ROLL_PTR,DB_ROW_ID。
|-------------|---------------------------------------------|
| DB_TRX_ID | 最近修改事务ID,记录插入这条记录或最后一次修改该记录的事务ID。 |
| DB_ROLL_PTR | 回滚指针,指向这条记录的上一个版本,用于配合undo log,指向上一个版本。 |
| DB_ROW_ID | 隐藏主键,如果表结构没有指定主键,将会生成该隐藏字段。 |
而上述的前两个字段是肯定会添加的, 是否添加最后一个字段DB_ROW_ID,得看当前表有没有主键,如果有主键,则不会添加该隐藏字段。
版本链
不同事务或相同事务对同一条记录进行修改,会导致该记录的undolog生成一条记录版本链表,链表的头部是最新的旧记录,链表尾部是最早的旧记录。
redo log
事务日志redo log(重做日志) :是Innodb存储引擎层生成的日志,实现了事务中的持久性 ,主要用于掉电等故障恢复。比如MySQL实例挂了或宕机了,重启时,InnoDB存储引擎会使用redo log恢复数据
-
redo log是物理日志,记录页的物理修改操作:在某个数据页做了什么修改,比如对x表空间中的N数据页ZZ偏移量的地方做了AAAA更新,每当执行一个事务就会产生这样的一条或者多条物理日志
-
保证数据的持久性:redo log会在事务提交时将日志存储到磁盘redo log file,保证日志的持久性 ;同时mysql会将数据写入磁盘,保证数据的持久性【在事务提交时,只要先将redo log 持久化到磁盘即可,可以不需要等到将缓存在 Buffer Pool 里的脏页数据持久化到磁盘;当系统崩溃时,虽然脏页数据没有持久化,但是redo log已经持久化,接着MySQL重启后,可以根据redo log的内容,将所有数据恢复到最新的状态】
-
介绍下 缓冲池与数据页的概念
缓冲池(buffer pool) :主内存中的一个区域,里面可以缓存磁盘上经常操作的真实数据,在执行增删改查操作时,先操作缓冲池中的数据(若缓冲池没有数据,则从磁盘加载并缓存),以一定频率刷新到磁盘,从而减少磁盘IO,加快处理速度 数据页(page):是InnoDB 存储引擎磁盘管理的最小单元,每个页的大小默认为 16KB。页中存储的是行数据
MySQL中数据是以页为单位,你查询一条记录,会从硬盘把一页的数据加载出来,加载出来的数据叫数据页,会放入到Buffer Pool中。后续的查询都是先从Buffer Pool中找,没有命中再去硬盘加载,减少硬盘IO开销,提升性能。
更新表数据的时候,也是如此,发现Buffer Pool里存在要更新的数据,就直接在Buffer Pool里更新。然后会把在某个数据页上做了什么修改记录到重做日志缓存(redo log buffer)里,接着刷盘到redo log文件里。同时,InnoDB引擎会在适当的时候,将这个操作记录更新到磁盘里面。
-
redo log的更新流程
-
执行Update操作
-
先将原始数据读从磁盘读取到内存,修改内存中的数据。
-
生成一条重做日志写入redo log buffer ,纪录数据被修改后的值。
-
当事物提交时,需要将redo log buffer中的内容刷新到redo log file。
-
事物提交后,也会将内存中修改的数据写入到磁盘。
为什么需要写Redo Log Buffer 和 Redo Log Flle?而不是直接持久化到磁盘?
在 MySQL 中,如果每一次的更新要写进磁盘,这么做会带来严重的性能问题:
因为 Innodb 是以页为单位进行磁盘交互的,而一个事务很可能只修改一个数据页里面的几个字节,这时将完整的数据页刷到磁盘的话,太浪费资源了!
一个事务可能涉及修改多个数据页,并且这些数据页在物理上并不连续,使用随机 IO 写入性能太差!