# 第17章_其他数据库日志
# 千万不要小看日志。很多看似奇怪的问题,答案往往就藏在日志里。很多情况下,
# 只有通过查看日志才能发现问题的原因,真正解决问题。所以,一定要学会查看日志,养成检查日志的习惯,
# 对提升你的数据库应用开发能力至关重要。
# 1. MySQL支持的日志
# 1.1 日志类型
# MySQL有不同类型的日志文件,用来存储不同类型的日志,
# 分为 二进制日志 、 错误日志 、 通用查询日志和 慢查询日志 ,这也是常用的4种。
# MySQL 8又新增两种支持的日志: 中继日志 和 数据定义语句日志 。使用这些日志文件,
# 可以查看MySQL内部发生的事情。
# 这6类日志分别为:
# 慢查询日志:记录所有执行时间超过long_query_time的所有查询,方便我们对查询进行优化。
# 通用查询日志:记录所有连接的起始时间和终止时间,以及连接发送给数据库服务器的所有指令,
# 对我们复原操作的实际场景、发现问题,甚至是对数据库操作的审计都有很大的帮助。
# 错误日志:记录MySQL服务的启动、运行或停止MySQL服务时出现的问题,方便我们了解服务器的状态,
# 从而对服务器进行维护。
# 二进制日志:记录所有更改数据的语句,可以用于主从服务器之间的数据同步,
# 以及服务器遇到故障时数据的无损失恢复。
# 中继日志:用于主从服务器架构中,从服务器用来存放主服务器二进制日志内容的一个中间文件。
# 从服务器通过读取中继日志的内容,来同步主服务器上的操作。
# 数据定义语句日志:记录数据定义语句执行的元数据操作。
# 除二进制日志外,其他日志都是 文本文件 。默认情况下,所有日志创建于 MySQL数据目录 中。
# 1.2 日志的弊端
# 日志功能会 降低MySQL数据库的性能 。
# 日志会 占用大量的磁盘空间 。
# 2. 慢查询日志(slow query log)
# 3. 通用查询日志(general query log)
# 通用查询日志用来 记录用户的所有操作 ,包括启动和关闭MySQL服务、所有用户的连接开始时间和截止时间、
# 发给 MySQL 数据库服务器的所有 SQL 指令等。当我们的数据发生异常时,查看通用查询日志,
# 还原操作时的具体场景,可以帮助我们准确定位问题。
# 3.2 查看当前状态
SHOW VARIABLES LIKE '%general%';
# 3.3 启动日志
# 方式1:永久性方式
# 修改my.cnf或者my.ini配置文件来设置。在[mysqld]组下加入log选项,并重启MySQL服务。格式如下:
# [mysqld]
# general_log=ON
# general_log_file=[path[filename]] #日志文件所在目录路径,filename为日志文件名
# 如果不指定目录和文件名,通用查询日志将默认存储在MySQL数据目录中的hostname.log文件中,hostname表示主机名。
# 方式2:临时性方式
# SET GLOBAL general_log=on; # 开启通用查询日志
# SET GLOBAL general_log_file=’path/filename’; # 设置日志文件保存位置
# 对应的,关闭操作SQL命令如下:
# SET GLOBAL general_log=off; # 关闭通用查询日志
# 查看设置后情况:
# SHOW VARIABLES LIKE 'general_log%';
SET GLOBAL general_log = ON;
# 3.4 查看日志
# 通用查询日志是以 文本文件 的形式存储在文件系统中的,可以使用 文本编辑器 直接打开日志文件。
# 每台MySQL服务器的通用查询日志内容是不同的。
# 在Windows操作系统中,使用文本文件查看器;
# 在Linux系统中,可以使用vi工具或者gedit工具查看;
# 在Mac OSX系统中,可以使用文本文件查看器或者vi等工具查看。
# 从 SHOW VARIABLES LIKE 'general_log%'; 结果中可以看到通用查询日志的位置。
SHOW VARIABLES LIKE '%general%';
USE atguigudb3;
SHOW TABLES;
SELECT * FROM student;
DELETE FROM student WHERE id = 20;
# 在通用查询日志里面,我们可以清楚地看到,什么时候开启了新的客户端登陆数据库,登录之后做了什
# 么 SQL 操作,针对的是哪个数据表等信息。
# 3.5 停止日志
# 方式1:永久性方式
# 修改 my.cnf 或者 my.ini 文件,把[mysqld]组下的 general_log 值设置为 OFF 或者把general_log一项注释掉。
# 修改保存后,再 重启MySQL服务 ,即可生效。 举例1:
# [mysqld]
# general_log=OFF
# 举例2:
# [mysqld]
#general_log=ON
# 方式2:临时性方式
# 使用SET语句停止MySQL通用查询日志功能:
# SET GLOBAL general_log=off;
# 查询通用日志功能:
# SHOW VARIABLES LIKE 'general_log%';
SET GLOBAL general_log = OFF;
SELECT * FROM student;
# 3.6 删除\刷新日志
# 如果数据的使用非常频繁,那么通用查询日志会占用服务器非常大的磁盘空间。
# 数据管理员可以删除很长时间之前的查询日志,以保证MySQL服务器上的硬盘空间。
# 手动删除文件
# SHOW VARIABLES LIKE 'general_log%';
# 可以看出,通用查询日志的目录默认为MySQL数据目录。在该目录下手动删除通用查询日志atguigu01.log。
# 使用如下命令重新生成查询日志文件,具体命令如下。刷新MySQL数据目录,发现创建了新的日志文件。
# 前提一定要开启通用日志。
# mysqladmin -uroot -p flush-logs
#Linux下执行
# mysqladmin -uroot -p flush-logs
# 4. 错误日志(error log)
# 4.1 启动日志
# 在MySQL数据库中,错误日志功能是 默认开启 的。而且,错误日志 无法被禁止 。
# 默认情况下,错误日志存储在MySQL数据库的数据文件夹下,
# 名称默认为 mysqld.log (Linux系统)或hostname.err (mac系统)。
# 如果需要制定文件名,则需要在my.cnf或者my.ini中做如下配置:
# [mysqld]
# log-error=[path/[filename]] #path为日志文件所在的目录路径,filename为日志文件名
# 修改配置项后,需要重启MySQL服务以生效。
# 4.2 查看日志
# MySQL错误日志是以文本文件形式存储的,可以使用文本编辑器直接查看。
SHOW VARIABLES LIKE 'log_err%';
# 执行结果中可以看到错误日志文件是mysqld.log,位于MySQL默认的数据目录下。
# 4.3 删除\刷新日志
# 对于很久以前的错误日志,数据库管理员查看这些错误日志的可能性不大,可以将这些错误日志删除,
# 以保证MySQL服务器上的 硬盘空间 。MySQL的错误日志是以文本文件的形式存储在文件系统中的,可以
# 直接删除 。
#Linux下执行
# mysqladmin -uroot -p flush-logs
# [root@atguigu01 log]# mysqladmin -uroot -p flush-logs
# Enter password:
# mysqladmin: refresh failed; error: 'Could not open file '/var/log/mysqld.log' for
# error logging.'
# 补充操作:
# install -omysql -gmysql -m0644 /dev/null /var/log/mysqld.log
# 5. 二进制日志(bin log)
# binlog可以说是MySQL中比较 重要 的日志了,在日常开发及运维过程中,经常会遇到。
# binlog即binary log,二进制日志文件,也叫作变更日志(update log)。
# 它记录了数据库所有执行的DDL 和 DML 等数据库更新事件的语句,
# 但是不包含没有修改任何数据的语句(如数据查询语句select、show等)。
# binlog主要应用场景:
# 一是用于 数据恢复
# 二是用于 数据复制
# 5.1 查看默认情况
# 查看记录二进制日志是否开启:在MySQL8中默认情况下,二进制文件是开启的。
SHOW VARIABLES LIKE '%log_bin%';
# 5.2 日志参数设置
# 方式1:永久性方式
# 修改MySQL的 my.cnf 或 my.ini 文件可以设置二进制日志的相关参数:
# [mysqld]
#启用二进制日志
# log-bin=server2-bin
# binlog_expire_logs_seconds=600
# max_binlog_size=100M
# 重新启动MySQL服务,查询二进制日志的信息,执行结果:
SHOW VARIABLES LIKE '%log_bin%';
SET SESSION sql_log_bin = 1;
SHOW VARIABLES LIKE '%log_bin%';
# 设置带文件夹的bin-log日志存放目录
# 如果想改变日志文件的目录和名称,可以对my.cnf或my.ini中的log_bin参数修改如下:
# [mysqld]
# log-bin="/var/lib/mysql/binlog/atguigu-bin"
# 注意:新建的文件夹需要使用mysql用户,使用下面的命令即可。
# chown -R -v mysql:mysql binlog
# 方式2:临时性方式
# 如果不希望通过修改配置文件并重启的方式设置二进制日志的话,还可以使用如下指令,
# 需要注意的是在mysql8中只有 会话级别 的设置,没有了global级别的设置。
# global 级别
# mysql> set global sql_log_bin=0;
# ERROR 1228 (HY000): Variable 'sql_log_bin' is a SESSION variable and can`t be used
# with SET GLOBAL
# session级别
# mysql> SET sql_log_bin=0;
# Query OK, 0 rows affected (0.01 秒)
# 5.3 查看日志
# 当MySQL创建二进制日志文件时,先创建一个以“filename”为名称、以“.index”为后缀的文件,
# 再创建一个以“filename”为名称、以“.000001”为后缀的文件。
# MySQL服务 重新启动一次 ,以“.000001”为后缀的文件就会增加一个,并且后缀名按1递增。
# 即日志文件的个数与MySQL服务启动的次数相同;如果日志长度超过了 max_binlog_size 的上限(默认是1GB),
# 就会创建一个新的日志文件。
# 查看当前的二进制日志文件列表及大小。指令如下:
SHOW BINARY LOGS;
#开始查看binlog
USE atguigudb3;
SELECT * FROM student;
INSERT INTO student(id, name, class)
VALUES (18,'Jerry','四班');
UPDATE student SET name = 'Tom1'
WHERE id = 17;
SHOW BINARY LOGS;
# mysqlbinlog "/var/lib/mysql/server2-bin.000002"
# 下面命令将行事件以 伪SQL的形式 表现出来
# mysqlbinlog "/var/lib/mysql/server2-bin.000002"
# 前面的命令同时显示binlog格式的语句,使用如下命令不显示它
# mysqlbinlog -v --base64-output=DECODE-ROWS "/var/lib/mysql/server2-bin.000002"
# 关于mysqlbinlog工具的使用技巧还有很多,例如只解析对某个库的操作或者某个时间段内的操作等。
# 简单分享几个常用的语句,更多操作可以参考官方文档。
# 可查看参数帮助
# mysqlbinlog --no-defaults --help
# 查看最后100行
# mysqlbinlog --no-defaults --base64-output=decode-rows -vv atguigu-bin.000002 |tail -100
# 根据position查找
# mysqlbinlog --no-defaults --base64-output=decode-rows -vv atguigu-bin.000002 |grep -A 20 '4939002'
# 上面这种办法读取出binlog日志的全文内容比较多,不容易分辨查看到pos点信息,下面介绍一种更为方便的查询命令:
# mysql> show binlog events [IN 'log_name'] [FROM pos] [LIMIT [offset,] row_count];
# IN 'log_name' :指定要查询的binlog文件名(不指定就是第一个binlog文件)
# FROM pos :指定从哪个pos起始点开始查起(不指定就是从整个文件首个pos点开始算)
# LIMIT [offset] :偏移量(不指定就是0)
# row_count :查询总条数(不指定就是所有行)
SHOW BINLOG EVENTS IN 'server2-bin.000002';
SHOW BINLOG EVENTS IN 'server2-bin.000002' FROM 468;
SHOW BINLOG EVENTS IN 'server2-bin.000002' FROM 468 LIMIT 0,2;
SHOW BINLOG EVENTS IN 'server2-bin.000002' FROM 468 LIMIT 1,3;
# 上面我们讲了这么多都是基于binlog的默认格式,binlog格式查看
SHOW VARIABLES LIKE 'binlog_format';
# 除此之外,binlog还有2种格式,分别是Statement和Mixed
# Statement
# 每一条会修改数据的sql都会记录在binlog中。
# 优点:不需要记录每一行的变化,减少了binlog日志量,节约了IO,提高性能。
# Row
# 5.1.5版本的MySQL才开始支持row level 的复制,它不记录sql语句上下文相关信息,仅保存哪条记录被修改。
# 优点:row level 的日志内容会非常清楚的记录下每一行数据修改的细节。
# 而且不会出现某些特定情况下的存储过程,或function,以及trigger的调用和触发无法被正确复制的问题。
# Mixed
# 从5.1.8版本开始,MySQL提供了Mixed格式,实际上就是Statement与Row的结合。
# 5.4 使用日志恢复数据
# mysqlbinlog恢复数据的语法如下:
# mysqlbinlog [option] filename|mysql –uuser -ppass;
# 这个命令可以这样理解:使用mysqlbinlog命令来读取filename中的内容,
# 然后使用mysql命令将这些内容恢复到数据库中。
# filename :是日志文件名。
# option :可选项,比较重要的两对option参数是--start-date、--stop-date 和 --start-position、
# --stop-position。
# --start-date 和 --stop-date :可以指定恢复数据库的起始时间点和结束时间点。
# --start-position和--stop-position :可以指定恢复数据的开始位置和结束位置。
# 注意:使用mysqlbinlog命令进行恢复操作时,必须是编号小的先恢复,
# 例如atguigu-bin.000001必须在atguigu-bin.000002之前恢复。
SHOW VARIABLES LIKE '%log_bin%';
SHOW BINARY LOGS;
USE atguigudb3;
SELECT * FROM student;
INSERT INTO student(id, name, class)
VALUES (20,'aaa','No.1');
INSERT INTO student(id, name, class)
VALUES (21,'aaa','No.1');
INSERT INTO student(id, name, class)
VALUES (22,'aaa','No.1');
SELECT * FROM student;
DELETE FROM student
WHERE id = 20;
SELECT * FROM student;
UPDATE student SET name = 'bbb'
WHERE id = 21;
SELECT * FROM student;
DELETE FROM student
WHERE id > 20;
SELECT * FROM student;
SHOW BINARY LOGS;
FLUSH LOGS;
SHOW BINARY LOGS;
SHOW BINLOG EVENTS IN 'server2-bin.000002';
#使用position恢复
#恢复增加的数据
# /usr/bin/mysqlbinlog --start-position=884 --stop-position=1729 --database=atguigudb3 /var/lib/mysql/server2-bin.000002 | /usr/bin/mysql -uroot -p123456 -v atguigudb3
SELECT * FROM student;
#恢复删除的数据
# /usr/bin/mysqlbinlog --start-position=1808 --stop-position=2037 --database=atguigudb3 /var/lib/mysql/server2-bin.000002 | /usr/bin/mysql -uroot -p123456 -v atguigudb3
SELECT * FROM student;
#恢复更改的数据
# /usr/bin/mysqlbinlog --start-position=2116 --stop-position=2369 --database=atguigudb3 /var/lib/mysql/server2-bin.000002 | /usr/bin/mysql -uroot -p123456 -v atguigudb3
SELECT * FROM student;
#使用日期恢复
# mysqlbinlog '/var/lib/mysql/server2-bin.000002';
# 5.5 删除二进制日志
# MySQL的二进制文件可以配置自动删除,同时MySQL也提供了安全的手动删除二进制文件的方法。
# PURGE MASTER LOGS 只删除指定部分的二进制日志文件, RESET MASTER 删除所有的二进制日志文件。具体如下:
# 1. PURGE MASTER LOGS:删除指定日志文件
# PURGE MASTER LOGS语法如下:
# PURGE {MASTER | BINARY} LOGS TO ‘指定日志文件名’
# PURGE {MASTER | BINARY} LOGS BEFORE ‘指定日期’
SHOW BINARY LOGS;
FLUSH LOGS;
FLUSH LOGS;
FLUSH LOGS;
FLUSH LOGS;
SHOW BINARY LOGS;
PURGE MASTER LOGS TO 'server2-bin.000003';
PURGE MASTER LOGS BEFORE '20220105';
# 2. RESET MASTER:删除所有二进制日志文件
RESET MASTER;
SHOW BINARY LOGS;
# 5.6 其它场景
# 二进制日志可以通过数据库的 全量备份 和二进制日志中保存的 增量信息 ,完成数据库的 无损失恢复 。
# 但是,如果遇到数据量大、数据库和数据表很多(比如分库分表的应用)的场景,用二进制日志进行数据恢复,
# 是很有挑战性的,因为起止位置不容易管理。
# 在这种情况下,一个有效的解决办法是 配置主从数据库服务器 ,甚至是 一主多从 的架构,
# 把二进制日志文件的内容通过中继日志,同步到从数据库服务器中,
# 这样就可以有效避免数据库故障导致的数据异常等问题。
# 6. 再谈二进制日志(binlog)
# 6.1 写入机制
# binlog的写入时机也非常简单,事务执行过程中,先把日志写到 binlog cache ,事务提交的时候,
# 再把binlog cache写到binlog文件中。因为一个事务的binlog不能被拆开,无论这个事务多大,
# 也要确保一次性写入,所以系统会给每个线程分配一个块内存作为binlog cache。
# write和fsync的时机,可以由参数 sync_binlog 控制,默认是 0 。为0的时候,表示每次提交事务都只write,
# 由系统自行判断什么时候执行fsync。虽然性能得到提升,但是机器宕机,page cache里面的binglog 会丢失。
# 为了安全起见,可以设置为 1 ,表示每次提交事务都会执行fsync,就如同redo log 刷盘流程一样。
# 最后还有一种折中方式,可以设置为N(N>1),表示每次提交事务都write,但累积N个事务后才fsync。
# 在出现IO瓶颈的场景里,将sync_binlog设置成一个比较大的值,可以提升性能。同样的,如果机器宕机,
# 会丢失最近N个事务的binlog日志。
# 6.2 binlog与redolog对比
# redo log 它是 物理日志 ,记录内容是“在某个数据页上做了什么修改”,属于 InnoDB 存储引擎层产生的。
# 而 binlog 是 逻辑日志 ,记录内容是语句的原始逻辑,类似于“给 ID=2 这一行的 c 字段加 1”,
# 属于MySQL Server 层。
# 6.3 两阶段提交
# 在执行更新语句过程,会记录redo log与binlog两块日志,以基本的事务为单位,
# redo log在事务执行过程中可以不断写入,而binlog只有在提交事务时才写入,
# 所以redo log与binlog的 写入时机 不一样。
# 7. 中继日志(relay log)
# 7.1 介绍
# 中继日志只在主从服务器架构的从服务器上存在。从服务器为了与主服务器保持一致,
# 要从主服务器读取二进制日志的内容,并且把读取到的信息写入 本地的日志文件 中,
# 这个从服务器本地的日志文件就叫中继日志 。然后,从服务器读取中继日志,
# 并根据中继日志的内容对从服务器的数据进行更新,完成主从服务器的 数据同步 。
# 搭建好主从服务器之后,中继日志默认会保存在从服务器的数据目录下。
# 文件名的格式是: 从服务器名 -relay-bin.序号 。中继日志还有一个索引文件: 从服务器名 -relay-bin.index ,
# 用来定位当前正在使用的中继日志。
# 7.2 查看中继日志
# 中继日志与二进制日志的格式相同,可以用 mysqlbinlog 工具进行查看。
# 7.3 恢复的典型错误
# 如果从服务器宕机,有的时候为了系统恢复,要重装操作系统,这样就可能会导致你的 服务器名称 与之前 不同 。
# 而中继日志里是 包含从服务器名 的。在这种情况下,就可能导致你恢复从服务器的时候,
# 无法从宕机前的中继日志里读取数据,以为是日志文件损坏了,其实是名称不对了。
# 解决的方法也很简单,把从服务器的名称改回之前的名称。
6. 再谈二进制日志(binlog)
6.1 写入机制
binlog的写入时机也非常简单,事务执行过程中,先把日志写到 binlog cache ,事务提交的时候,再
把binlog cache写到binlog文件中。因为一个事务的binlog不能被拆开,无论这个事务多大,也要确保一
次性写入,所以系统会给每个线程分配一个块内存作为binlog cache。
write和fsync的时机,可以由参数 sync_binlog 控制,默认是 0 。为0的时候,表示每次提交事务都只
write,由系统自行判断什么时候执行fsync。虽然性能得到提升,但是机器宕机,page cache里面的
binglog 会丢失。如下图:
为了安全起见,可以设置为 1 ,表示每次提交事务都会执行fsync,就如同redo log 刷盘流程一样。
最后还有一种折中方式,可以设置为N(N>1),表示每次提交事务都write,但累积N个事务后才fsync。
在出现IO瓶颈的场景里,将sync_binlog设置成一个比较大的值,可以提升性能。同样的,如果机器宕
机,会丢失最近N个事务的binlog日志。
6.2 binlog与redolog对比
redo log 它是 物理日志 ,记录内容是“在某个数据页上做了什么修改”,属于 InnoDB 存储引擎层产生
的。
而 binlog 是 逻辑日志 ,记录内容是语句的原始逻辑,类似于“给 ID=2 这一行的 c 字段加 1”,属于
MySQL Server 层。
6.3 两阶段提交
在执行更新语句过程,会记录redo log与binlog两块日志,以基本的事务为单位,redo log在事务执行过程
中可以不断写入,而binlog只有在提交事务时才写入,所以redo log与binlog的 写入时机 不一样。
redo log与binlog两份日志之间的逻辑不一致,会出现什么问题?
由于binlog没写完就异常,这时候binlog里面没有对应的修改记录。
为了解决两份日志之间的逻辑一致问题,InnoDB存储引擎使用两阶段提交方案。
使用两阶段提交后,写入binlog时发生异常也不会有影响
另一个场景,redo log设置commit阶段发生异常,那会不会回滚事务呢?
并不会回滚事务,它会执行上图框住的逻辑,虽然redo log是处于prepare阶段,但是能通过事务id找到对
应的binlog日志,所以MySQL认为是完整的,就会提交事务恢复数据。