MySQL在服务启停、执行SQL的时候,会有各种日志的生成,这些日志可以分为以下类型:
日志类型 | 日志内容 |
---|---|
错误日志 | 记录MySQL在运行、启动、停止的时候遇到的错误信息 |
通用查询日志 | 记录建立的客户端连接和执行的语句 |
二进制日志 | DML操作,修改数据库的数据操作的日志 |
中继日志 | 从主服务器接收的数据更改日志 |
慢查询日志 | 查询时间超过long_query_time设定的时间,或者不使用索引的查询 |
DDL日志 | DDL操作,涉及到元数据的变更的日志 |
其中,二进制日志中记录的是DML的操作,也就是增、删、改数据的日志信息,称为binary-log,也就是我们要说的bin-log日志。
MySQL的bin-log日志,可以说是上述的日志中最重要的日志。其中记录的不仅仅是DML的操作,DDL的操作也会记录在其中。可以说除了查询操作(select、show),都会记录在这样的日志中!
bin-log日志中,以事件的新式记录日志,记录你进行的SQL操作,同时还记录操作所消耗的时间等信息。学习bin-log日志最主要的目的是为了复制和恢复
MySQL8的bin-log日志是默认开启的,因此我们更多关注如何关闭即可。
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 |
# +---------------------------------+-----------------------------+
#
# 其中 log_bin 的值为ON,表示bin-log已经开启。
通过修改MySQL的配置文件可以关闭bin-log日志:
配置文件的路径:
- Windows: C:\Program Data\MySQL\MySQL Server8.0\my.ini
- Mac通过dmg直接安装: /etc/my.cnf
- Mac通过HomeBrew安装: $HOMEBREW_HOME/etc/my.cnf
- CentOS: /etc/my.cnf
在 [mysqld] 标签下添加
skip-log-bin
重启MySQL服务
-- 禁用bin-log
mysql> SET SQL_LOG_BIN=0;
-- 启用bin-log
mysql> SET SQL_LOG_BIN=1;
-- 设置完成后需要重启MySQL服务生效
-- 查看是否启用bin-log日志
show variables like 'log_bin';
-- 查看bin-log日志的目录
show variables like '%log_bin%';
-- 查看使用的bin-log文件大小
show binary logs;
-- 查看正在使用的bin-log文件名和Position
show master status;
-- 查看bin-log文件内容,默认查看第一个bin-log文件
show binlog events;
-- 查看指定的bin-log文件内容
show binlog events in 'binlog.000003';
-- 查看指定的bin-log文件内容,从指定位置开始查看
show binlog events in 'binlog.000003' from 865;
-- 查看指定的bin-log文件内容,从指定位置开始查看,查看指定偏移量
show binlog events in 'binlog.000003' from 125 limit 3;
-- 设置bin-log文件保存时间,单位是天
-- set global expire_log_days=3; MySQL8以下
-- 方式一:
set global binlog_expire_logs_seconds=2592000;
-- 方式二:在配置文件中修改 my.cnf / my.ini
-- 删除当前的bin-log文件
reset master;
由上述过程可以看出,持久化到磁盘的bin-log文件中的是最后一步。而在这里,需要了解一个参数: sync_binlog。这个参数表示提交多少个事务之后,将binlog的内容持久化到磁盘文件中。
sync_binlog=0
每次提交事务都只会提交到binlog中,不会持久化到磁盘文件中。
sync_binlog=1
每次提交事务都会把日志持久化到磁盘文件中。
sync_binlog=N
例如设置为100,意义为提交100个事务,将日志持久化到磁盘文件中。
sync_binlog的值设置
通常情况下,我们会在1和N之间进行选择:
1:
优点: 安全,不会丢失事务。
缺点: 磁盘IO消耗高。
N:
优点: 效率高,不会占用太多的磁盘IO。
缺点: 不安全,如果服务器异常重启,会丢失最近N个事务的binlog日志。
bin-log日志格式有三种类型:
在MySQL5.7.7之前,默认的格式是STATEMENT,5.7.7之后,默认的格式是ROW,可以通过binlog_format参数来设置日志的格式:
binlog_format=STATEMENT
binlog_format=ROW
binlog_format=MIXED
STATEMENT
每一条会修改数据的SQL操作(DDL、DML)都会记录在bin-log中。
- 优点:
不需要记录每一行的变化,减少bin-log日志量,减少磁盘消耗,提高性能。
- 缺点:
主从模式下,有些语句不支持;容易出现主从不一致。
ROW
5.1.5版本开始,MySQL支持了ROW格式,也是现在的默认的日志格式。这种格式下,不记录SQL语句,仅保存哪条记录被修改了。
- 优点:
1. 会记录每一条记录被修改的细节。
2. 在主从模式下,不会出现存储过程、function、触发器无法被正确复制的问题。
- 缺点:
会产生大量的日志内容。
MIXED
从5.1.8版本开始,MySQL支持Mixed格式。这种其实其实是STATEMENT和ROW格式的结合。
- 一般的语句修改使用STATEMENT格式保存bin-log。
- 对应STATEMENT无法完成的主从复制操作,例如存储过程、函数等,采用ROW格式保存bin-log。
bin-log日志文件包含两类:
binlog.index
索引文件,是一个普通的文本文件,可以直接查看里面的内容。其中记录的是当前的bin-log文件列表。
binlog.0000*
这种文件是二进制文件,其中记录着操作的日志。
# 查看bin-log二进制文件中的所有内容
mysqlbinlog -v --base64-output=decode-rows binlog.000001
# 查看bin-log二进制文件中指定范围的内容
mysqlbinlog -v --base64-output=decode-rows binlog.000001 \
--start-position="100" \
--stop-position="1000" \
--start-datetime="2021-01-01 10:00:05" \
--stop-datetime="2021-01-10 12:00:00"
bin-log日志文件中记录的都是一些事件Event,其中第一个Event表示当前日志文件的起点和格式,最后一个Event表示下一个日志的起点和格式。中间的都是当前日志文件中记录的事件信息。
# at 235
#220126 23:18:44 server id 1 end_log_pos 475 CRC32 0x5372bdc8 Query thread_id=8 exec_time=0 error_code=0 Xid = 9
SET TIMESTAMP=1643210324.145384/*!*/;
SET @@session.pseudo_thread_id=8/*!*/;
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 utf8mb4 *//*!*/;
SET @@session.character_set_client=255,@@session.collation_connection=255,@@session.collation_server=255/*!*/;
SET @@session.time_zone='SYSTEM'/*!*/;
SET @@session.lc_time_names=0/*!*/;
SET @@session.collation_database=DEFAULT/*!*/;
/*!80011 SET @@session.default_collation_for_utf8mb4=255*//*!*/;
ALTER USER 'root'@'localhost' IDENTIFIED WITH 'caching_sha2_password' AS '$A$005$h^X^Sc^A^VI ^D/!^]%N-^K^K^CYn7vZ6L6SMh9v5bwFvMZSqDcl6sEH.8quuhEr4C.ASsx9'
/*!*/;
这是我截取到的一部分的event,其中包含以下内容:
position:
第一行的at 235,表示这条事件记录是从文件的第235个字节开始
timestamp:
第二行的,#220126 23:18:44,表示这个事件发生的时间
server id:
事件发生的服务器标识
end_log_pos:
当前事件的结束位置+1,即下一个事件的起始位置
thread_id:
执行这个事件的线程id
exec_time:
执行这个事件花费的时间
error_code:
事件的错误码,0表示没有错误
我们可以通过查看bin-log文件,查看文件中保存的事件,也可以通过命令,更加直观的查看。
mysql> show binlog events in 'binlog.000015';
+---------------+------+----------------+-----------+-------------+---------------------------------------------------------------------------------------------------+
| Log_name | Pos | Event_type | Server_id | End_log_pos | Info |
+---------------+------+----------------+-----------+-------------+---------------------------------------------------------------------------------------------------+
| binlog.000015 | 4 | Format_desc | 1 | 125 | Server ver: 8.0.27, Binlog ver: 4 |
| binlog.000015 | 125 | Previous_gtids | 1 | 156 | |
| binlog.000015 | 156 | Anonymous_Gtid | 1 | 233 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' |
| binlog.000015 | 233 | Query | 1 | 394 | use `mydb2`; create table user(`uid` int primary key, `name` varchar(20), `age` int) /* xid=41 */ |
| binlog.000015 | 394 | Anonymous_Gtid | 1 | 473 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' |
| binlog.000015 | 473 | Query | 1 | 549 | BEGIN |
| binlog.000015 | 549 | Table_map | 1 | 609 | table_id: 100 (mydb2.user) |
| binlog.000015 | 609 | Write_rows | 1 | 662 | table_id: 100 flags: STMT_END_F |
| binlog.000015 | 662 | Xid | 1 | 693 | COMMIT /* xid=42 */ |
| binlog.000015 | 693 | Anonymous_Gtid | 1 | 772 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' |
| binlog.000015 | 772 | Query | 1 | 848 | BEGIN |
| binlog.000015 | 848 | Table_map | 1 | 908 | table_id: 100 (mydb2.user) |
| binlog.000015 | 908 | Write_rows | 1 | 957 | table_id: 100 flags: STMT_END_F |
| binlog.000015 | 957 | Xid | 1 | 988 | COMMIT /* xid=43 */ |
| binlog.000015 | 988 | Anonymous_Gtid | 1 | 1067 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' |
| binlog.000015 | 1067 | Query | 1 | 1143 | BEGIN |
| binlog.000015 | 1143 | Table_map | 1 | 1203 | table_id: 100 (mydb2.user) |
| binlog.000015 | 1203 | Write_rows | 1 | 1254 | table_id: 100 flags: STMT_END_F |
| binlog.000015 | 1254 | Xid | 1 | 1285 | COMMIT /* xid=44 */ |
| binlog.000015 | 1285 | Anonymous_Gtid | 1 | 1364 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' |
| binlog.000015 | 1364 | Query | 1 | 1440 | BEGIN |
| binlog.000015 | 1440 | Table_map | 1 | 1500 | table_id: 100 (mydb2.user) |
| binlog.000015 | 1500 | Write_rows | 1 | 1552 | table_id: 100 flags: STMT_END_F |
| binlog.000015 | 1552 | Xid | 1 | 1583 | COMMIT /* xid=45 */ |
| binlog.000015 | 1583 | Anonymous_Gtid | 1 | 1662 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' |
| binlog.000015 | 1662 | Query | 1 | 1738 | BEGIN |
| binlog.000015 | 1738 | Table_map | 1 | 1798 | table_id: 100 (mydb2.user) |
| binlog.000015 | 1798 | Write_rows | 1 | 1849 | table_id: 100 flags: STMT_END_F |
| binlog.000015 | 1849 | Xid | 1 | 1880 | COMMIT /* xid=46 */ |
| binlog.000015 | 1880 | Anonymous_Gtid | 1 | 1959 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' |
| binlog.000015 | 1959 | Query | 1 | 2044 | BEGIN |
| binlog.000015 | 2044 | Table_map | 1 | 2104 | table_id: 100 (mydb2.user) |
| binlog.000015 | 2104 | Update_rows | 1 | 2172 | table_id: 100 flags: STMT_END_F |
| binlog.000015 | 2172 | Xid | 1 | 2203 | COMMIT /* xid=47 */ |
| binlog.000015 | 2203 | Anonymous_Gtid | 1 | 2282 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' |
| binlog.000015 | 2282 | Query | 1 | 2358 | BEGIN |
| binlog.000015 | 2358 | Table_map | 1 | 2418 | table_id: 100 (mydb2.user) |
| binlog.000015 | 2418 | Delete_rows | 1 | 2470 | table_id: 100 flags: STMT_END_F |
| binlog.000015 | 2470 | Xid | 1 | 2501 | COMMIT /* xid=48 */ |
| binlog.000015 | 2501 | Rotate | 1 | 2545 | binlog.000016;pos=4 |
+---------------+------+----------------+-----------+-------------+---------------------------------------------------------------------------------------------------+
40 rows in set (0.00 sec)
-- 刷新bin-log,生成一个新的bin-log文件,方便我们进行恢复
mysql> flush logs;
-- 创建一张新的表
mysql> create table `t_user`(`uid` int primary key, `uname` varchar(20), `age` int);
-- 插入数据若干
mysql> insert into `t_user` values (1, 'zhangsan', 18);
mysql> insert into `t_user` values (2, 'lisi', 20);
mysql> insert into `t_user` values (3, 'wangwu', 19);
mysql> insert into `t_user` values (4, 'zhaoliu', 21);
mysql> insert into `t_user` values (5, 'tianqi', 18);
-- 修改数据
mysql> update `t_user` set `age` = 20 where `uid` = 5;
-- 删除数据
mysql> delete from `t_user` where `uid` = 4;
-- 刷新bin-log,记录刚才的操作
mysql> flush logs;
我们已经进行了一些DDL、DML的操作,假如我不小心把表删除了
mysql> drop table `t_user`;
此时可以查看bin-log中的events
mysql> show binlog events in 'binlog.000017';
+---------------+------+----------------+-----------+-------------+---------------------------------------------------------------------------------------------------------+
| Log_name | Pos | Event_type | Server_id | End_log_pos | Info |
+---------------+------+----------------+-----------+-------------+---------------------------------------------------------------------------------------------------------+
| binlog.000017 | 4 | Format_desc | 1 | 125 | Server ver: 8.0.27, Binlog ver: 4 |
| binlog.000017 | 125 | Previous_gtids | 1 | 156 | |
| binlog.000017 | 156 | Anonymous_Gtid | 1 | 233 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' |
| binlog.000017 | 233 | Query | 1 | 399 | use `mydb2`; create table `t_user`(`uid` int primary key, `uname` varchar(20), `age` int) /* xid=431 */ |
| binlog.000017 | 399 | Anonymous_Gtid | 1 | 478 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' |
| binlog.000017 | 478 | Query | 1 | 554 | BEGIN |
| binlog.000017 | 554 | Table_map | 1 | 616 | table_id: 103 (mydb2.t_user) |
| binlog.000017 | 616 | Write_rows | 1 | 669 | table_id: 103 flags: STMT_END_F |
| binlog.000017 | 669 | Xid | 1 | 700 | COMMIT /* xid=432 */ |
| binlog.000017 | 700 | Anonymous_Gtid | 1 | 779 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' |
| binlog.000017 | 779 | Query | 1 | 855 | BEGIN |
| binlog.000017 | 855 | Table_map | 1 | 917 | table_id: 103 (mydb2.t_user) |
| binlog.000017 | 917 | Write_rows | 1 | 966 | table_id: 103 flags: STMT_END_F |
| binlog.000017 | 966 | Xid | 1 | 997 | COMMIT /* xid=433 */ |
| binlog.000017 | 997 | Anonymous_Gtid | 1 | 1076 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' |
| binlog.000017 | 1076 | Query | 1 | 1152 | BEGIN |
| binlog.000017 | 1152 | Table_map | 1 | 1214 | table_id: 103 (mydb2.t_user) |
| binlog.000017 | 1214 | Write_rows | 1 | 1265 | table_id: 103 flags: STMT_END_F |
| binlog.000017 | 1265 | Xid | 1 | 1296 | COMMIT /* xid=434 */ |
| binlog.000017 | 1296 | Anonymous_Gtid | 1 | 1375 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' |
| binlog.000017 | 1375 | Query | 1 | 1451 | BEGIN |
| binlog.000017 | 1451 | Table_map | 1 | 1513 | table_id: 103 (mydb2.t_user) |
| binlog.000017 | 1513 | Write_rows | 1 | 1565 | table_id: 103 flags: STMT_END_F |
| binlog.000017 | 1565 | Xid | 1 | 1596 | COMMIT /* xid=435 */ |
| binlog.000017 | 1596 | Anonymous_Gtid | 1 | 1675 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' |
| binlog.000017 | 1675 | Query | 1 | 1751 | BEGIN |
| binlog.000017 | 1751 | Table_map | 1 | 1813 | table_id: 103 (mydb2.t_user) |
| binlog.000017 | 1813 | Write_rows | 1 | 1864 | table_id: 103 flags: STMT_END_F |
| binlog.000017 | 1864 | Xid | 1 | 1895 | COMMIT /* xid=436 */ |
| binlog.000017 | 1895 | Anonymous_Gtid | 1 | 1974 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' |
| binlog.000017 | 1974 | Query | 1 | 2059 | BEGIN |
| binlog.000017 | 2059 | Table_map | 1 | 2121 | table_id: 103 (mydb2.t_user) |
| binlog.000017 | 2121 | Update_rows | 1 | 2189 | table_id: 103 flags: STMT_END_F |
| binlog.000017 | 2189 | Xid | 1 | 2220 | COMMIT /* xid=437 */ |
| binlog.000017 | 2220 | Anonymous_Gtid | 1 | 2299 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' |
| binlog.000017 | 2299 | Query | 1 | 2375 | BEGIN |
| binlog.000017 | 2375 | Table_map | 1 | 2437 | table_id: 103 (mydb2.t_user) |
| binlog.000017 | 2437 | Delete_rows | 1 | 2489 | table_id: 103 flags: STMT_END_F |
| binlog.000017 | 2489 | Xid | 1 | 2520 | COMMIT /* xid=438 */ |
| binlog.000017 | 2520 | Rotate | 1 | 2564 | binlog.000018;pos=4 |
+---------------+------+----------------+-----------+-------------+---------------------------------------------------------------------------------------------------------+
40 rows in set (0.00 sec)
可以看到每一个Event的Pos信息,那么我们就可以通过Pos信息恢复数据
# --start-position: 指定起始的事件位置,如果不指定,默认从头开始
# --stop-position: 指定结束的事件位置,如果不指定,默认到结尾
# --start-datetime: 指定起始的事件时间,如果不指定,默认从头开始
# --stop-datetime: 指定结束的事件时间,如果不指定,默认到结尾
mysqlbinlog --stop-position=2489 --database=mydb2 binlog.000017 | mysql -uroot -p123456
可以看到数据都恢复啦!