第17章_其他数据库日志
两种日志:重做日志、回滚日志。
对于线上数据库应用系统,突然遭遇数据库宕机怎么办?在这种情况下,定位宕机的原因就非常关键。我们可以查看数据库的错误日志。因为日志中记录了数据库运行中的诊断信息,包括了错误、警告和注释等信息。比如:从日志中发现某个连接中的SQL操作发生了死循环,导致内存不足,被系统强行终止了。明确了原因,处理起来也就轻松了,系统很快就恢复了运行。
除了发现错误,日志在数据复制、数据恢复、操作审计,以及确保数据的永久性和一致性等方面,都有着不可替代的作用。
1.MySQL支持的日志
1.1 日志类型
MySQL有不同类型的日志文件,用来存储不同类型的日志,分为 二进制日志 、 错误日志 、 通用查询日志
和 慢查询日志=,这也是常用的4种。MySQL 8又新增两种支持的日志: 中继日志 和 数据定义语句日志 。使
用这些日志文件,可以查看MySQL内部发生的事情。
这6类日志分别为:
- 慢查询日志:记录所有执行时间超过long_query_time的所有查询,方便我们对查询进行优化。
- **通用查询日志:**记录所有连接的起始时间和终止时间,以及连接发送给数据库服务器的所有指令,
对我们复原操作的实际场景、发现问题,甚至是对数据库操作的审计都有很大的帮助。 - 错误日志:记录MySQL服务的启动、运行或停止MySQL服务时出现的问题,方便我们了解服务器的
状态,从而对服务器进行维护。 - **二进制日志:**记录所有更改数据的语句,可以用于主从服务器之间的数据同步,以及服务器遇到故
障时数据的无损失恢复。 - **中继日志:**用于主从服务器架构中,从服务器用来存放主服务器二进制日志内容的一个中间文件。
从服务器通过读取中继日志的内容,来同步主服务器上的操作。 - 数据定义语句日志:记录数据定义语句执行的元数据操作。
除二进制日志外,其他日志都是 文本文件 。默认情况下,所有日志创建于 MySQL数据目录 中。
1.2 日志的弊端
- 日志功能会 降低MySQL数据库的性能 。
- 日志会 占用大量的磁盘空间 。
2.慢查询日志(slow query log)
前面章节《第09章_性能分析工具的使用》
3.通用查询日志(general query log)
通用查询日志用来 记录用户的所有操作 ,包括启动和关闭MySQL服务、所有用户的连接开始时间和截止
时间、发给 MySQL 数据库服务器的所有 SQL 指令等。当我们的数据发生异常时,查看通用查询日志,
还原操作时的具体场景,可以帮助我们准确定位问题。
3.1 问题场景
在电商系统中,购买商品开且使用微信支付完成以后,却发现支付中心的记录并没有新增,此时用尸再次使用支付宝支付,就会出现重复支付的问题。但是当去数据库中查询数据的时候,会发现只有一条记录存在。那么此时给到的现象就是只有一条支付记录,但是用户却支付了两次。
我们对系统进行了仔细检查,没有发现数据问题,因为用户编号和订单编号以及第三方流水号都是对的。可是用户确实支付了两次,这个时候,我们想到了检查通用查询日志,看看当天到底发生了什么。
查看之后,发现: 1月1日下午2点,用户使用微信支付完以后,但是由于网络故障,支付中心没有及时收到微信支付的回调通知,导致当时没有写入数据。1月1日下午2点轴,用户又使用支付宝支付,此时记录更新到支付中心。1月1日晚上9点,微信的回调通知过来了,但是支付中心已经存在了支付宝的记录,所以只能覆盖记录了。
3.2 查看当前状态
mysql> SHOW VARIABLES LIKE '%general%'; +------------------+------------------------------+ | Variable_name | Value | +------------------+------------------------------+ | general_log | OFF | #通用查询日志处于关闭状态 | general_log_file | /var/lib/mysql/atguigu01.log | #通用查询日志文件的名称是atguigu01.log +------------------+------------------------------+ 2 rows in set (0.03 sec)
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%';
3.4 查看日志
通用查询日志是以 文本文件 的形式存储在文件系统中的,可以使用 文本编辑器 直接打开日志文件。每台
MySQL服务器的通用查询日志内容是不同的。
- 在Windows操作系统中,使用文本文件查看器;
- 在Linux系统中,可以使用vi工具或者gedit工具查看;
- 在Mac OSX系统中,可以使用文本文件查看器或者vi等工具查看。
从SHOW VARIABLES LIKE 'general_log%';
结果中可以看到通用查询日志的位置。
/usr/sbin/mysqld, Version: 8.0.26 (MySQL Community Server - GPL). started with: Tcp port: 3306 Unix socket: /var/lib/mysql/mysql.sock Time Id Command Argument 2022-01-04T07:44:58.052890Z 10 Query SHOW VARIABLES LIKE '%general%' 2022-01-04T07:45:15.666672Z 10 Query SHOW VARIABLES LIKE 'general_log%' 2022-01-04T07:45:28.970765Z 10 Query select * from student 2022-01-04T07:47:38.706804Z 11 Connect root@localhost on using Socket 2022-01-04T07:47:38.707435Z 11 Query select @@version_comment limit 1 2022-01-04T07:48:21.384886Z 12 Connect root@172.16.210.1 on using TCP/IP 2022-01-04T07:48:21.385253Z 12 Query SET NAMES utf8 2022-01-04T07:48:21.385640Z 12 Query USE `atguigu12` 2022-01-04T07:48:21.386179Z 12 Query SHOW FULL TABLES WHERE Table_Type != 'VIEW' 2022-01-04T07:48:23.901778Z 13 Connect root@172.16.210.1 on using TCP/IP 2022-01-04T07:48:23.902128Z 13 Query SET NAMES utf8 2022-01-04T07:48:23.905179Z 13 Query USE `atguigu` 2022-01-04T07:48:23.905825Z 13 Query SHOW FULL TABLES WHERE Table_Type != 'VIEW' 2022-01-04T07:48:32.163833Z 14 Connect root@172.16.210.1 on using TCP/IP 2022-01-04T07:48:32.164451Z 14 Query SET NAMES utf8 2022-01-04T07:48:32.164840Z 14 Query USE `atguigu` 2022-01-04T07:48:40.006687Z 14 Query select * from account
在通用查询日志里面,我们可以清楚地看到,什么时候开启了新的客户端登陆数据库,登录之后做了什
么 SQL 操作,针对的是哪个数据表等信息
3.5 停止日志
方式1:永久性方式
修改 my.cnf 或者 my.ini 文件,把[mysqld]组下的 general_log 值设置为 OFF 或者把general_log一项
注释掉。修改保存后,再 重启MySQL服务 ,即可生效。 举例1:
[mysqld] general_log=OFF
[mysqld] #general_log=ON
方式2:临时性方式
SET GLOBAL general_log=off;
查询通用日志功能:
SHOW VARIABLES LIKE 'general_log%';
3.6 删除\刷新日志
如果数据的使用非常频繁,那么通用查询日志会占用服务器非常大的磁盘空间。数据管理员可以删除很
长时间之前的查询日志,以保证MySQL服务器上的硬盘空间。
手动删除文件
使用如下命令重新生成查询日志文件,具体命令如下。刷新MySQL数据目录,发现创建了新的日志文
件。前提一定要开启通用日志。
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错误日志是以文本文件形式存储的,可以使用文本编辑器直接查看。
查询错误日志的存储路径:
mysql> SHOW VARIABLES LIKE 'log_err%'; +----------------------------+----------------------------------------+ | Variable_name | Value | +----------------------------+----------------------------------------+ | log_error | /var/log/mysqld.log | | log_error_services | log_filter_internal; log_sink_internal | | log_error_suppression_list | | | log_error_verbosity | 2 | +----------------------------+----------------------------------------+ 4 rows in set (0.01 sec)
5.二进制日志(bin log)
binlog可以说是MySQL中比较 重要 的日志了,在日常开发及运维过程中,经常会遇到。
binlog即binary log,二进制日志文件,也叫作变更日志(update log)。它记录了数据库所有执行的
DDL 和 DML 等数据库更新事件的语句,但是不包含没有修改任何数据的语句(如数据查询语句select、
show等)。
binlog主要应用场景:
- 一是用于 数据恢复
- 二是用于 数据复制
5.1 查看默认情况
查看记录二进制日志是否开启:在MySQL8中默认情况下,二进制文件是开启的。
mysql> show variables like '%log_bin%'; +---------------------------------+----------------------------------+ | Variable_name | Value | +---------------------------------+----------------------------------+ | log_bin | ON | | log_bin_basename | /var/lib/mysql/binlog | | log_bin_index | /var/lib/mysql/binlog.index | | log_bin_trust_function_creators | OFF | | log_bin_use_v1_row_events | OFF | | sql_log_bin | ON | +---------------------------------+----------------------------------+ 6 rows in set (0.00 sec)
5.2 日志参数设置
方式1:永久性方式
修改MySQL的 my.cnf 或 my.ini 文件可以设置二进制日志的相关参数:
[mysqld] #启用二进制日志 log-bin=atguigu-bin binlog_expire_logs_seconds=600 max_binlog_size=100M
重新启动MySQL服务,查询二进制日志的信息,执行结果
mysql> show variables like '%log_bin%'; +---------------------------------+----------------------------------+ | Variable_name | Value | +---------------------------------+----------------------------------+ | log_bin | ON | | log_bin_basename | /var/lib/mysql/atguigu-bin | | log_bin_index | /var/lib/mysql/atguigu-bin.index | | log_bin_trust_function_creators | OFF | | log_bin_use_v1_row_events | OFF | | sql_log_bin | ON | +---------------------------------+----------------------------------+ 6 rows in set (0.00 sec)
设置带文件夹的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),就
会创建一个新的日志文件。
查看当前的二进制日志文件列表及大小。指令如下
mysql> SHOW BINARY LOGS; +--------------------+-----------+-----------+ | Log_name | File_size | Encrypted | +--------------------+-----------+-----------+ | atguigu-bin.000001 | 156 | No | +--------------------+-----------+-----------+ 1 行于数据集 (0.02 秒)
下面命令将行事件以 伪SQL的形式 表现出来
mysqlbinlog -v "/var/lib/mysql/binlog/atguigu-bin.000002" #220105 9:16:37 server id 1 end_log_pos 324 CRC32 0x6b31978b Query thread_id=10 exec_time=0 error_code=0 SET TIMESTAMP=1641345397/*!*/; SET @@session.pseudo_thread_id=10/*!*/; SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1/*!*/; SET @@session.sql_mode=1168113696/*!*/; SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/; /*!\C utf8mb3 *//*!*/; SET @@session.character_set_client=33,@@session.collation_connection=33,@@session.collatio n_server=255/*!*/; SET @@session.lc_time_names=0/*!*/; SET @@session.collation_database=DEFAULT/*!*/; /*!80011 SET @@session.default_collation_for_utf8mb4=255*//*!*/; BEGIN /*!*/; # at 324 #220105 9:16:37 server id 1 end_log_pos 391 CRC32 0x74f89890 Table_map: `atguigu14`.`student` mapped to number 85 # at 391 #220105 9:16:37 server id 1 end_log_pos 470 CRC32 0xc9920491 Update_rows: table id 85 flags: STMT_END_F BINLOG ' dfHUYRMBAAAAQwAAAIcBAAAAAFUAAAAAAAEACWF0Z3VpZ3UxNAAHc3R1ZGVudAADAw8PBDwAHgAG AQEAAgEhkJj4dA== dfHUYR8BAAAATwAAANYBAAAAAFUAAAAAAAEAAgAD//8AAQAAAAblvKDkuIkG5LiA54+tAAEAAAAL 5byg5LiJX2JhY2sG5LiA54+tkQSSyQ== '/*!*/; ### UPDATE `atguigu`.`student` ### WHERE ### @1=1 ### @2='张三' ### @3='一班' ### SET ### @1=1 ### @2='张三_back' ### @3='一班' # at 470 #220105 9:16:37 server id 1 end_log_pos 501 CRC32 0xca01d30f Xid = 15 COMMIT/*!*/;
前面的命令同时显示binlog格式的语句,使用如下命令不显示它
mysqlbinlog -v --base64-output=DECODE-ROWS "/var/lib/mysql/binlog/atguigu-bin.000002" #220105 9:16:37 server id 1 end_log_pos 324 CRC32 0x6b31978b Query thread_id=10 exec_time=0 error_code=0 SET TIMESTAMP=1641345397/*!*/; SET @@session.pseudo_thread_id=10/*!*/; SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1/*!*/; SET @@session.sql_mode=1168113696/*!*/; SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/; /*!\C utf8mb3 *//*!*/; SET @@session.character_set_client=33,@@session.collation_connection=33,@@session.collatio n_server=255/*!*/; SET @@session.lc_time_names=0/*!*/; SET @@session.collation_database=DEFAULT/*!*/; /*!80011 SET @@session.default_collation_for_utf8mb4=255*//*!*/; BEGIN /*!*/; # at 324 #220105 9:16:37 server id 1 end_log_pos 391 CRC32 0x74f89890 Table_map: `atguigu14`.`student` mapped to number 85 # at 391 #220105 9:16:37 server id 1 end_log_pos 470 CRC32 0xc9920491 Update_rows: table id 85 flags: STMT_END_F ### UPDATE `atguigu14`.`student` ### WHERE ### @1=1 ### @2='张三' ### @3='一班' ### SET ### @1=1 ### @2='张三_back' ### @3='一班' # at 470 #220105 9:16:37 server id 1 end_log_pos 501 CRC32 0xca01d30f Xid = 15
关于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
:查询总条数(不指定就是所有行)
mysql> show binlog events in 'atguigu-bin.000002'; +--------------------+-----+----------------+-----------+-------------+--------------- --------------------------------------------------------------+ | Log_name | Pos | Event_type | Server_id | End_log_pos | Info | +--------------------+-----+----------------+-----------+-------------+--------------- --------------------------------------------------------------+ | atguigu-bin.000002 | 4 | Format_desc | 1 | 125 | Server ver: 8.0.26, Binlog ver: 4 | | atguigu-bin.000002 | 125 | Previous_gtids | 1 | 156 | | | atguigu-bin.000002 | 156 | Anonymous_Gtid | 1 | 235 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' | | atguigu-bin.000002 | 235 | Query | 1 | 324 | BEGIN | | atguigu-bin.000002 | 324 | Table_map | 1 | 391 | table_id: 85 (atguigu14.student) | | atguigu-bin.000002 | 391 | Update_rows | 1 | 470 | table_id: 85 flags: STMT_END_F | | atguigu-bin.000002 | 470 | Xid | 1 | 501 | COMMIT /* xid=15 */ | | atguigu-bin.000002 | 501 | Anonymous_Gtid | 1 | 578 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' | | atguigu-bin.000002 | 578 | Query | 1 | 721 | use `atguigu14`; create table test(id int, title varchar(100)) /* xid=19 */ | | atguigu-bin.000002 | 721 | Anonymous_Gtid | 1 | 800 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' | | atguigu-bin.000002 | 800 | Query | 1 | 880 | BEGIN | | atguigu-bin.000002 | 880 | Table_map | 1 | 943 | table_id: 89 (atguigu14.test) | | atguigu-bin.000002 | 943 | Write_rows | 1 | 992 | table_id: 89 flags: STMT_END_F | | atguigu-bin.000002 | 992 | Xid | 1 | 1023 | COMMIT /* xid=21 */ +--------------------+-----+----------------+-----------+-------------+--------------- --------------------------------------------------------------+ 14 行于数据集 (0.02 秒)
上面我们讲了这么多都是基于binlog的默认格式,binlog格式查看
mysql> show variables like 'binlog_format'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | binlog_format | ROW | +---------------+-------+ 1 行于数据集 (0.02 秒)
除此之外,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的结合。
详细情况,下章
第17章_其他数据库日志(2)https://developer.aliyun.com/article/1530740