MySQL-数据库服务日志管理与数据闪回

一、数据库中存在什么日志

1. 错误日志

默认存在,位置在数据目录中,文件名为"主机名称.err":

bash 复制代码
[root@db02 data]# ll /data/3306/data/db02.err
-rw-r----- 1 mysql mysql 2694183 10月 21 10:51 db02.err

设置生成错误日志文件 (vim /etc/my.cnf):

bash 复制代码
log_error=/tmp/mysql3306.err    -- 永久设置

日志作用:记录数据库服务运行情况,通过错误信息可以分析数据库服务故障原理。

2. 通用日志

默认不存在,需要手动开启。

设置生成通用日志文件 (vim /etc/my.cnf):

bash 复制代码
general_log=ON                                -- 是否开启通用日志记录功能
general_log_file=/data/3306/data/general.log  -- 设置日志存储路径和名称

验证日志文件生成:

bash 复制代码
ll /data/3306/data/general.log
-rw-r----- 1 mysql mysql 867  6月 26 18:07 /data/3306/data/general.log

日志作用:审计日志/开发人员做调式使用的日志。具体包括:

  1. 可以记录登录数据库的用户信息,是否登录成功可以做记录;
  2. 可以记录登录成功后,用户在数据库中操作行为(DDL、DCL、DML、DQL);
  3. 数据备份mysqldump 做的数据备份流程和原理也体现在日志中。

3.慢查询日志

作用:记录慢查询数据语句信息,利用及早发现的慢查询语句做优化,可以提高数据库服务性能。

设置:默认在数据库中不会创建慢查询日志,需手动配置开启。

慢查询日志相关参数说明:

参数 作用
slow_query_log 确认是否创建慢查询日志
slow_query_log_file 确认慢查询日志存储路径和日志名称
long_query_time 确认语句执行多长时间算慢查询
log_queries_not_using_indexes 在查询数据信息时,没有利用索引的查询
bash 复制代码
# 配置慢查询日志
[root@db01 ~]# vim /etc/my.cnf
slow_query_log=on
slow_query_log_file=/tmp/slow.log 
long_query_time=0.001
log_queries_not_using_indexes=on

配置项说明:

1.slow_query_log=on:开启慢查询日志功能(对应"确认是否创建慢查询日志");
2.slow_query_log_file=/tmp/slow.log:设置慢查询日志的存储路径和日志名称;
3.long_query_time=0.001:设置语句执行多长时间算慢查询(此处设置为 0.001 秒);
4.log_queries_not_using_indexes=on:开启记录功能,在查询数据信息时,没有利用索引的查询也会被记录。

4.二进制日志

二进制日志的作用:

  1. 可以用于数据恢复(历史数据、实时增量数据);
  2. 可以用于主从同步 :主数据库 insert → binlog → 从数据库 insert。

配置开启binlog日志:

bash 复制代码
1. 先创建binlog存储目录
[root@db01 ~]# mkdir -p  /data/3306/log

2. 给目录授予mysql用户权限
[root@db01 ~]# chown -R mysql.mysql /data/3306/log

3. 配置文件增加binlog日志
[root@db01 ~]# vim /etc/my.cnf
log_bin=/data/3306/log/xiaoQ-binlog

mysql> show variables like '%log_bin%';
+---------------------------------+-----------------------------------+
| Variable_name                   | Value                             |
+---------------------------------+-----------------------------------+
| log_bin                         | ON                                |
| log_bin_basename                | /data/3306/log/mysql-binlog       |
| log_bin_index                   | /data/3306/log/mysql-binlog.index |
| log_bin_trust_function_creators | OFF                               |
| log_bin_use_v1_row_events       | OFF                               |
| sql_log_bin                     | ON                                |
+---------------------------------+-----------------------------------+


相关参数:
参数	            作用
log_bin_basename	指定日志存储路径和名称
log_bin_index	    指定日志索引信息路径和名称


查询当前 MySQL 实例中与二进制日志(Binary Log,简称 Binlog)相关的系统变量配置。
1. log_bin = ON
含义:表示当前 MySQL 实例的二进制日志功能是否开启。
说明:ON 代表已开启,OFF 代表未开启。这是 MySQL 非常重要的一个全局开关,开启后,数据库所有的数据修改操作(如 INSERT、UPDATE、DELETE 等)都会按顺序记录到二进制日志文件中,常用于主从复制和数据恢复。

2. log_bin_basename = /data/3306/log/mysql-binlog
含义:二进制日志文件的基础路径和名称前缀。
说明:当 MySQL 生成新的二进制日志文件时,会以这个值为基础。例如,它会在该路径下生成 mysql-binlog.000001、mysql-binlog.000002 等文件。日志文件正是存放在 /data/3306/log/ 目录下。

3. log_bin_index = /data/3306/log/mysql-binlog.index
含义:二进制日志索引文件的存放路径。
说明:这个 .index 文件是一个普通的文本文件。里面记录了当前 MySQL 所有正在使用的二进制日志文件(如 000001, 000002 等)的完整路径清单,MySQL 通过它来追踪有哪些日志文件。

4. log_bin_trust_function_creators = OFF
含义:是否信任创建存储函数/触发器的用户。
说明:当开启了 Binlog 后,为了防止主从复制时由于函数执行结果不一致导致主从数据错乱(比如用到 NOW() 或 RAND() 函数),MySQL 默认(OFF)会限制普通用户创建存储函数或触发器。只有拥有 SUPER 权限的用户才能创建。如果设为 ON,则允许普通用户创建。

5. log_bin_use_v1_row_events = OFF
含义:是否使用 V1 版本的 Row 格式事件。
说明:这是 MySQL 历史遗留的一个版本控制参数。在 MySQL 5.1 版本以后,为了支持更丰富的数据类型和功能,Row 格式的日志事件升级到了 V2 版本。通常情况下,这个值都是 OFF(即默认使用 V2 版本),不需要修改,修改它可能会导致主从版本不兼容。

6. sql_log_bin = ON
含义:当前会话(Session)是否将 SQL 操作记录到 Binlog 中。
说明:这既是一个全局变量,也是一个会话变量。默认是 ON。如果你当前登录的会话不想把某条 SQL 写入 Binlog(比如你正在做临时的测试、批量导数据,且不想让从库同步),你可以执行 SET sql_log_bin = 0; 临时关闭当前会话的日志记录,执行完操作后再 SET sql_log_bin = 1; 打开。

查看日志:

方式一:在数据库中查看 binlog 日志信息

sql 复制代码
show binary logs;                      -- 确认可以查看binlog日志名称
show master status;                    -- 确认正在记录内容binlog日志名称
show binlog events in '日志名称';       -- 查看binlog日志内容


# 日志切割(全量备份时先执行一次)
mysql> flush logs;
Query OK, 0 rows affected (0.01 sec)
强制 MySQL 立即关闭当前正在使用的日志文件,并生成(切换)一个新的日志文件。
具体来说,这个命令会执行以下三个主要动作:
重新生成一个新的二进制日志(Binlog):
比如你之前在看 mysql-binlog.000001,执行完这个命令后,MySQL 会停止向 000001 写入,并创建一个全新的 mysql-binlog.000002,后续的所有数据修改都会记录到新的 000002 文件里。
重新生成一个新的错误日志(Error Log)(如果开启了该功能,取决于系统的日志配置)。
重新生成一个慢查询日志(Slow Query Log)(如果开启了慢查询日志)。

(常见使用场景)
方便备份与数据恢复:
当你准备对数据库进行全量备份时,通常会先执行一次 flush logs;。这样可以把备份时刻之前的 Binlog 封存(比如 000001),备份完成后的新操作记录到新文件(000002)中。后续如果要用 Binlog 做"基于时间点恢复(PITR)",边界会非常清晰,不会混淆。
主从复制切换:
在主从架构中,有时候为了让从库重新同步(比如重置复制状态或重新指定起点),会在主库执行这个命令,方便从库定位新的文件位置。
清理磁盘空间:
当你的 Binlog 文件占用磁盘过大时,如果 000001 不再被使用,你可以执行 flush logs; 生成 000002,然后用 PURGE BINARY LOGS TO 'mysql-binlog.000002'; 删掉 000001,从而释放磁盘空间。
排查问题:
当前的日志文件可能非常大了,你想通过一个全新的文件来单独观察接下来发生的操作,方便排查问题。

# 执行了两次日志切割
mysql> show binary logs;
+---------------------+-----------+-----------+
| Log_name            | File_size | Encrypted |
+---------------------+-----------+-----------+
| mysql-binlog.000001 |       873 | No        |
| mysql-binlog.000002 |       247 | No        |
| mysql-binlog.000003 |       197 | No        |
+---------------------+-----------+-----------+
3 rows in set (0.00 sec)


mysql> show binlog events in 'mysql-binlog.000001';
+---------------------+-----+----------------+-----------+-------------+-----------------------------------+
| Log_name            | Pos | Event_type     | Server_id | End_log_pos | Info                              |
+---------------------+-----+----------------+-----------+-------------+-----------------------------------+
| mysql-binlog.000001 |   4 | Format_desc    |         1 |         126 | Server ver: 8.0.36, Binlog ver: 4 |
| mysql-binlog.000001 | 126 | Previous_gtids |         1 |         157 |                                   |
+---------------------+-----+----------------+-----------+-------------+-----------------------------------+
2 rows in set (0.00 sec)


位置点概念:

Pos:日志信息开始位置点
End_log_pos:日志信息结束位置点
bash 复制代码
1. 各列含义解释
Log_name: 当前事件所在的 Binlog 文件名。因为你在查询时指定了 in 'mysql-binlog.000001',所以这里全是一样的。
Pos (Start Position): 该事件在 Binlog 文件中的起始字节位置(偏移量)。
Event_type: 事件的类型。比如 Query(执行的 SQL 语句)、Table_map(映射表 ID 和表名)、Write_rows(插入数据)、Xid(事务提交)等。
Server_id: 产生该事件的 MySQL 服务器的唯一 ID。
End_log_pos: 该事件在 Binlog 文件中结束的字节位置。
Info: 具体的事件信息。通常包含了 SQL 语句、GTID 编号或者表 ID 等信息。

2. 重点解释:Pos 和 End_log_pos 的关系
这两个字段是理解 Binlog 结构的关键:
Pos 是当前事件的起点。
End_log_pos 是当前事件的终点(同时也是下一个事件的起点)。

3.Binlog 是一个连续的二进制文件,所有事件像串珠一样紧挨着写入。你可以观察表格中的数据:
第 1 行:从 4 开始,到 126 结束(记录了文件头格式)。
第 2 行:从 126 开始,到 157 结束(即上一行的 End_log_pos 等于这一行的 Pos)。
以此类推。
当你看到 Xid (COMMIT) 时,比如 Pos 为 459,End_log_pos 为 490,说明这个提交事件占了 31 个字节。紧接着下一个 Gtid 事件就从 490 开始了。
结论:End_log_pos - Pos = 该事件占用的字节大小。它们首尾相连,构成了整个日志文件。

4. 深入解析:这份日志记录了什么?
从这份日志可以看出,你(或者某个程序)向 testing.student 表中插入(Insert)了两次数据,且开启了 GTID 模式。我们来拆解其中一次完整的事务(比如 Pos 157 到 490 这一段):
Gtid (Pos 157): 分配了全局事务 ID(...f97d83:2),表示这是一个新事务。
Query (Pos 236): 记录了 BEGIN,表示事务开始。
Table_map (Pos 314): 记录了要操作的表是 table_id: 89 (testing.student)。这里只记录表 ID 和表名,不记录具体数据。
Write_rows (Pos 388): 记录了实际的插入数据。注意,因为这是 Row 格式的 Binlog,具体的列名和数据(比如张三、18岁等)在底层被转成了二进制,这里简略显示为 Write_rows。
Xid (Pos 459): 记录了 COMMIT,表示事务提交成功。

结合起来看:
在这个表里插入了两次数据,形成了两个事务(GTID:2 和 GTID:3)。
第一个事务记录从 Pos 157 开始,到 Pos 490 结束(提交)。
第二个事务紧挨着从 Pos 490 开始,到 Pos 823 结束(提交)。

方式二:在命令行中查看 binlog 日志信息

bash 复制代码
# 进入binlog存储路径查看,这里使用的是相对路径
[root@db01 log]# mysqlbinlog mysql-binlog.000003 
# The proper term is pseudo_replica_mode, but we use this compatibility alias
# to make the statement usable on server versions 8.0.24 and older.
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/;
/*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/;
DELIMITER /*!*/;
# at 4
#261022 16:17:54 server id 1  end_log_pos 126 CRC32 0x77161edb 	Start: binlog v 4, server v 8.0.36 created 261022 16:17:54
# Warning: this binlog is either in use or was not closed properly.
BINLOG '
ssbZag8BAAAAegAAAH4AAAABAAQAOC4wLjM2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAEwANAAgAAAAABAAEAAAAYgAEGggAAAAICAgCAAAACgoKKioAEjQA
CigAAdseFnc=
'/*!*/;
# at 126
#261022 16:17:54 server id 1  end_log_pos 197 CRC32 0x185e08c1 	Previous-GTIDs
# 630b76d3-cc61-11f1-8061-000c29f97d83:2-3
# at 197
#261022 16:26:14 server id 1  end_log_pos 274 CRC32 0xde185fef 	GTID	last_committed=0	sequence_number=1	rbr_only=no	original_committed_timestamp=1792657574462605	immediate_commit_timestamp=1792657574462605	transaction_length=188
# original_commit_timestamp=1792657574462605 (2026-10-22 16:26:14.462605 CST)
# immediate_commit_timestamp=1792657574462605 (2026-10-22 16:26:14.462605 CST)
/*!80001 SET @@session.original_commit_timestamp=1792657574462605*//*!*/;
/*!80014 SET @@session.original_server_version=80036*//*!*/;
/*!80014 SET @@session.immediate_server_version=80036*//*!*/;
SET @@SESSION.GTID_NEXT= '630b76d3-cc61-11f1-8061-000c29f97d83:4'/*!*/;
# at 274
#261022 16:26:14 server id 1  end_log_pos 385 CRC32 0x0380691e 	Query	thread_id=1307	exec_time=0	error_code=0	Xid = 29
SET TIMESTAMP=1792657574/*!*/;
SET @@session.pseudo_thread_id=1307/*!*/;
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.lc_time_names=0/*!*/;
SET @@session.collation_database=DEFAULT/*!*/;
/*!80011 SET @@session.default_collation_for_utf8mb4=255*//*!*/;
/*!80016 SET @@session.default_table_encryption=0*//*!*/;
create database testq
/*!*/;
# at 385
#261022 16:27:59 server id 1  end_log_pos 462 CRC32 0x6a66a6e3 	GTID	last_committed=1	sequence_number=2	rbr_only=no	original_committed_timestamp=1792657679159908	immediate_commit_timestamp=1792657679159908	transaction_length=213
# original_commit_timestamp=1792657679159908 (2026-10-22 16:27:59.159908 CST)
# immediate_commit_timestamp=1792657679159908 (2026-10-22 16:27:59.159908 CST)
/*!80001 SET @@session.original_commit_timestamp=1792657679159908*//*!*/;
/*!80014 SET @@session.original_server_version=80036*//*!*/;
/*!80014 SET @@session.immediate_server_version=80036*//*!*/;
SET @@SESSION.GTID_NEXT= '630b76d3-cc61-11f1-8061-000c29f97d83:5'/*!*/;
# at 462
#261022 16:27:59 server id 1  end_log_pos 598 CRC32 0x661c1e69 	Query	thread_id=1307	exec_time=0	error_code=0	Xid = 31
use `testing`/*!*/;
SET TIMESTAMP=1792657679/*!*/;
/*!80013 SET @@session.sql_require_primary_key=0*//*!*/;
CREATE TABLE testq.q1 (id int,name char(10))
/*!*/;
SET @@SESSION.GTID_NEXT= 'AUTOMATIC' /* added by mysqlbinlog */ /*!*/;
DELIMITER ;
# End of log file
/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;
1)扩展知识:DML 语句信息查看方法/记录方法
  1. 行格式记录(binlog_format=ROW)

命令行日志查看:

  • DML 语句以行格式记录 ,不能直接查看;
  • binlog_format=ROW:记录行的数据信息;
  • 设置作用:保证数据的一致性。
bash 复制代码
# 解码操作
[root@db01 log]# mysqlbinlog --base64-output=decode-rows -vvv mysql-binlog.000003


 @1=1 表示在第一列插入数据
 @1=2 表示在第二列插入数据
 @1=3 表示在第三列插入数据
### INSERT INTO `testq`.`q3` SET @1=1  @2=xx @3=xx  
  1. 语句方式记录(binlog_format=statement)
sql 复制代码
# 记录语句信息
mysql> select @@binlog_format;
+-----------------+
| @@binlog_format |
+-----------------+
| STATEMENT       |
+-----------------+
1 row in set, 1 warning (0.00 sec)

# 将 MySQL 二进制日志(Binlog)的记录格式全局修改为 STATEMENT(基于 SQL 语句)模式。
mysql> set global binlog_format='statement';
Query OK, 0 rows affected, 1 warning (0.00 sec)

三种主要格式的对比
你可以把 Binlog 记录数据修改想象成"记录做菜":
1.STATEMENT(语句模式,你刚设置的):
记录方式:直接记录菜谱(即原封不动的 SQL 语句)。
优点:日志文件非常小,节省磁盘 I/O 和网络带宽(主从同步时传的快)。
缺点:如果 SQL 里有不确定的函数(比如 NOW()、RAND()、UUID()),主库和从库执行同一句 SQL 得到的时间或结果可能不一样,会导致主从数据不一致。

2.ROW(行模式,MySQL 5.7 之后的默认模式):
记录方式:记录食材具体变成了什么样(即记录每一行数据修改前后的具体内容)。
优点:绝对安全,保证主从数据 100% 一致,不会因为函数或环境不同而错乱。
缺点:日志文件非常大(比如一条 UPDATE 改了 10 万行,就会记录 10 万行的变化),占用磁盘空间大。

3.MIXED(混合模式):
记录方式:MySQL 自己判断,普通的 SQL 用 STATEMENT 记录,遇到不安全的函数(如 RAND())自动切换成 ROW 记录。
  • DML 语句以语句方式记录 ,可以直接查看内容;
  • binlog_format=statement:记录语句信息;
  • 设置作用:方便查看 DML 语句信息,但无法保证数据一致性。

日志记录方式已经发生改变:

命令查看日志:

5.日志切割

方式一:手动进行日志切割

sql 复制代码
flush logs;

方式二:自动进行日志切割

bash 复制代码
执行链路:
flush logs --> shell脚本 --> crontab

通过设置单个 binlog 文件大小上限,达到阈值自动切割:
set global max_binlog_size=104857600;

(104857600 字节 = 100M)

4)清理日志

PS:什么 binlog 日志可以清理?

  • 已经备份的 binlog 日志可以清理;

  • binlog 日志中的 DML、DDL、DCL 信息已经保存备份到备份文件,此日志可以清理。

例如:create database、create user、insert 等操作产生的日志,已经通过 mysqldump / xbk 备份保存后,对应 binlog 即可清理。

方式一:手动清理 binlog 日志

按日志文件清理(清理到指定文件之前):

sql 复制代码
# 按日志文件清理(清理到指定文件之前):
purge binary logs to 'xiaoQ-binlog.000004';

# 按时间点清理(清理指定时间之前的日志):
PURGE BINARY LOGS BEFORE '2026-06-28 19:30:26';

方式二:自动清理 binlog 日志

参数 作用
binlog_expire_logs_seconds 根据历史日志存在的秒数做日志清理
expire_logs_days 根据历史日志存在的天数做日志清理

设置示例(1 小时):

bash 复制代码
binlog_expire_logs_seconds=3600    -- 1小时

PS:千万不要直接 rm 清理 binlog 日志文件

直接 rm 删除 binlog 日志文件会导致问题(关闭数据库服务),必须使用上述 purge 或自动过期参数的方式清理。

6.备份日志与日志信息截取

1)备份日志(备份服务:10.0.0.52)
bash 复制代码
步骤一:在备份服务器,创建日志备份服务主机/并设置日志存储目录
mkdir /backup/binlog/10.0.0.53/ -p
cd /backup/binlog/10.0.0.53/

步骤二:执行 mysqlbinlog 命令实现日志备份,备份53服务上的日志
mysqlbinlog -R --host=10.0.0.53 --port=3306 --user=root --password=root123 --raw --stop-never mysql-binlog.000001 &

参数说明:

参数 作用
-R 实现远程备份日志信息功能
--raw 实时读取日志信息变化,将内容同步到远程日志备份服务器上
--stop-never 日志备份过程采用守护进程方式进行日志备份

--raw 实时同步示意(10.0.0.53 → 10.0.0.52):

日志切割:

日志自动实时通步:

数据库主机 10.0.0.53 日志备份主机 10.0.0.52
mysql-binlog.000001 mysql-binlog.000001
insert insert
update update
insert insert

即主库 binlog 中记录的每条操作(insert/update ...),都会实时同步到远程日志备份服务器的同名日志文件中。

2)日志信息截取

方式一:根据位置点截取信息

参数说明:

  • --start-position:截取日志信息从哪个位置点开始截取
  • --stop-position:截取日志信息截取到哪个位置点
bash 复制代码
#在binlog日志存储路径执行

[root@db01 log]# mysqlbinlog --start-position=276 --stop-position=476 mysql-binlog.000014 >/backup/backup.sql

恢复数据:

方式二:根据时间截取信息

场景:恢复数据库,数据库大概在 2026-10-10 18:05:00 创建 ;2026-10-10 22:35:00 误操作(数据库删除)。

确定截取时间范围:

  • 起始时间:2026-10-26 16:30:15
  • 结束时间:2026-10-26 16:31:05

(范围取在创建之后、误操作之前,避开误删除操作。)

bash 复制代码
[root@db01 log]# mysqlbinlog --start-datetime="2026-10-26 16:30:00" --stop-datetime="2026-10-26 16:32:00" --skip-gtids --base64-output=DECODE-ROWS -v mysql-binlog.000016 > /backup/backup01.sql

## mysqlbinlog 参数说明
--start-datetime
只解析指定时间点之后的 binlog 事件,如 `"2026-10-26 16:30:00"`。需与 MySQL 服务器时区一致。
--stop-datetime
只解析指定时间点之前的 binlog 事件,如 `"2026-10-26 16:32:00"`。与 start 搭配可精确圈定一个时间窗口。
--skip-gtids
输出结果中不生成 `SET @@GLOBAL.GTID_PURGED` 等 GTID 语句,防止恢复数据时污染目标实例的 GTID 集合,影响主从复制。
--base64-output=DECODE-ROWS 
解码 ROW 格式的行变更事件,不输出原始 Base64 编码串(`BINLOG '...'`)。需配合 `-v` 才能还原为可读内容。
-v(--verbose)
把 ROW 事件重建为可读的伪 SQL(含 WHERE / SET 的列值,`@1`、`@2` 为列序号)。加 `-vv` 可额外输出列名等元信息。必须与 `--base64-output=DECODE-ROWS` 搭配使用。

方式三:截取指定数据库的数据信息

sql 复制代码
# 切割日志--查看新的binlog日志--创建数据库、表以及插入数据--备份数据--删除数据库--恢复数据

flush logs;

show master status;

CREATE DATABASE IF NOT EXISTS prod_db DEFAULT CHARSET utf8mb4;

USE prod_db;

CREATE TABLE users (
  id INT AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(50),
  age INT
);

INSERT INTO users (name, age) VALUES ('张三', 20), ('李四', 25);

[root@db01 log]# mysqlbinlog --database=prod_db --skip-gtids mysql-binlog.000019 > /backup/prod.sql

--database 指定只截取某个数据库的数据信息

mysql> DROP DATABASE prod_db;

mysql> source /backup/prod.sql;

7.数据恢复

1)数据库异常数据丢失后,修复数据方式

方式一:借助备份文件+binlog恢复数据

模拟企业场景:

sql 复制代码
步骤一:在数据库中存储数据信息
mysql> create database abs;
Query OK, 1 row affected (0.00 sec)

mysql> create table abs.t1 (id int,name char(10));
Query OK, 0 rows affected (0.01 sec)

mysql> INSERT INTO abs.t1 (id, name) VALUES (1, '张三');
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO abs.t1 (id, name) VALUES (2, '李四');
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO abs.t1 (id, name) VALUES (3, '王五');
Query OK, 1 row affected (0.00 sec)

mysql> commit;

步骤二:定期对数据库做备份
[root@db01 backup]# mysqldump -uroot -proot123  -B abs >/backup/abs.sql;

[root@db01 log]#  mysqldump -uroot -proot123  -B test_db --source-data >/backup/test_db.sql;
--source-data:
这个参数在 MySQL 8.0.26 之前被称为 --master-data。
核心作用:它会在导出的 SQL 文件中记录备份开始那一刻的二进制日志(binlog)文件名和位置(Position)。


步骤三:增加增量数据
CREATE DATABASE IF NOT EXISTS demo_binlog DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

USE demo_binlog;

CREATE TABLE demo_binlog.t1 (
    id INT,
    name CHAR(10)
);

INSERT INTO demo_binlog.t1 (id, name) VALUES (1, '张三'),(2, '李四'),(3, '王五');

步骤四:模拟数据库数据损坏
mysql> drop database abs;
mysql> drop database demo_binlog;

步骤五:恢复数据
mysql> show master status;  --查看binlog日志
mysql> show binlog events in 'mysql-binlog.000019';  --查看日志内容

# 备份目的是为了查看位置(Position),因为删除数据的时候未备份demo_binlog数据
[root@db01 log]#  mysqldump -uroot -proot123  -B test_db --source-data >/backup/test_db.sql;
bash 复制代码
# 临时关闭blog日志记录,避免日志重复记录
mysql> set session sql_log_bin=0;

# 根据日志记录实现增量数据恢复
[root@db01 log]# mysqlbinlog  --start-position=3277  --stop-position=4098   mysql-binlog.000019 >/backup/demo_binlog.sql

#恢复数据
mysql> source /backup/abs.sql;
mysql> source /backup/demo_binlog.sql

# 打开blog日志记录
mysql> set session sql_log_bin=1;

测试验证:数据已恢复

2)利用数据闪回工具实现数据修复

方式二:使用binlog2sql 数据闪回数据恢复

在日常的数据库运维工作中,DML(数据操作语言)误操作是最高频的"事故"之一。一条手抖执行的 DELETE、一次忘记加 WHERE 条件的 UPDATE,都可能让生产数据瞬间丢失或错乱。传统手段依赖备份恢复,不仅耗时长,还会丢失误操作之后的正常数据变更。

binlog2sql 是一款基于 MySQL binlog 解析的开源工具,它能够将二进制日志(binlog)解析为可读的 SQL 语句,并支持反向生成 (Flashback)回滚 SQL,从而实现对 INSERT、UPDATE、DELETE 三类 DML 误操作的精确修复------只回滚误操作,不影响其他正常数据。

sql 复制代码
步骤一:模拟创建企业环境数据
use abs;
select * from student;
步骤二:模拟进行DML语句误操作
INSERT INTO student (name, gender, age, grade, major, email, phone) VALUES
('林十一', '男', 20, '大二', '计算机科学', 'linshiyi@example.com', '13800000011');
-- 插入误操作
UPDATE student SET gender = '男' WHERE age >= 20;
-- 误修改操作  
DELETE FROM student WHERE id BETWEEN 6 AND 9;
-- 误删除操作
mysql> select * from student;
+----+-----------+--------+------+--------+-----------------+----------------------+-------------+
| id | name      | gender | age  | grade  | major           | email                | phone       |
+----+-----------+--------+------+--------+-----------------+----------------------+-------------+
|  1 | 张三      | 男     |   20 | 大二   | 计算机科学      | zhangsan@example.com | 13800000001 |
|  2 | 李四      | 女     |   19 | 大一   | 软件工程        | lisi@example.com     | 13800000002 |
|  3 | 王五      | 男     |   21 | 大三   | 人工智能        | wangwu@example.com   | 13800000003 |
|  4 | 赵六      | 男     |   22 | 大四   | 数据科学        | zhaoliu@example.com  | 13800000004 |
|  5 | 孙七      | 男     |   20 | 大二   | 网络安全        | sunqi@example.com    | 13800000005 |
| 10 | 陈二      | 男     |   22 | 大四   | 数据科学        | chener@example.com   | 13800000010 |
| 11 | 林十一    | 男     |   20 | 大二   | 计算机科学      | linshiyi@example.com | 13800000011 |
+----+-----------+--------+------+--------+-----------------+----------------------+-------------+

步骤三:利用数据闪回工具实现误操作数据修复(DML)
yum install -y python3 python3-pip
pip3 install -r requirements.txt
unzip binlog2sql-master.zip -d /opt/module/
[root@db01 module]# mv requirements.txt binlog2sql-master/
cd binlog2sql-master
-- 安装数据闪回工具
# 执行以下sql
ALTER USER 'root'@'db01' IDENTIFIED WITH mysql_native_password BY 'root123';
FLUSH PRIVILEGES;
# 解析所有 DML
[root@db01 binlog2sql-master]# python3 binlog2sql.py -h10.0.0.53 -P3306 -uroot -proot123 -d abs -t student --start-file='mysql-binlog.000019'
# 只解析 INSERT 语句
python3 binlog2sql.py -h10.0.0.51 -P3306 -uroot -p123456 -d school -t student --start-file='xiaoQ-binlog.000015' --sql-type=insert
# 只解析 INSERT + 生成回滚 SQL(-B)
python3 binlog2sql.py -h10.0.0.51 -P3306 -uroot -p123456 -d school -t student --start-file='xiaoQ-binlog.000015' --sql-type=insert -B
-- 应用工具实现数据闪回
-- 将误插入数据信息做删除
DELETE FROM `abs`.`student` WHERE `id`=11 AND `name`='林十一' AND `gender`='男' AND `age`=20 AND `grade`='大二' AND `major`='计算机科学' AND `email`='linshiyi@example.com' AND `phone`='13800000011' LIMIT 1;
sql 复制代码
# 查看这个 binlog 里 student 表上所有 UPDATE 的原始语句
python3 binlog2sql.py -h10.0.0.53 -P3306 -uroot -proot123 -d abs -t student --start-file='mysql-binlog.000019' --sql-type=update
# 把 UPDATE 操作反转,生成回滚 SQL
python3 binlog2sql.py -h10.0.0.53 -P3306 -uroot -proot123 -d abs -t student --start-file='mysql-binlog.000019' --sql-type=update -B

-- 将误修改数据信息做恢复
UPDATE `abs`.`student` SET `id`=4, `name`='赵六', `gender`='男', `age`=22, `grade`='大四', `major`='数据科学', `email`='zhaoliu@example.com', `phone`='13800000004' WHERE `id`=4 AND `name`='赵六' AND `gender`='女' AND `age`=22 AND `grade`='大四' AND `major`='数据科学' AND `email`='zhaoliu@example.com' AND `phone`='13800000004' LIMIT 1;
UPDATE `abs`.`student` SET `id`=8, `name`='郑十', `gender`='男', `age`=21, `grade`='大三', `major`='计算机科学', `email`='zhengshi@example.com', `phone`='13800000008' WHERE `id`=8 AND `name`='郑十' AND `gender`='女' AND `age`=21 AND `grade`='大三' AND `major`='计算机科学' AND `email`='zhengshi@example.com' AND `phone`='13800000008' LIMIT 1;
UPDATE `abs`.`student` SET `id`=10, `name`='陈二', `gender`='男', `age`=22, `grade`='大四', `major`='数据科学', `email`='chener@example.com', `phone`='13800000010' WHERE `id`=10 AND `name`='陈二' AND `gender`='女' AND `age`=22 AND `grade`='大四' AND `major`='数据科学' AND `email`='chener@example.com' AND `phone`='13800000010' LIMIT 1;
UPDATE `abs`.`student` SET `id`=10, `name`='陈二', `gender`='女', `age`=22, `grade`='大四', `major`='数据科学', `email`='chener@example.com', `phone`='13800000010' WHERE `id`=10 AND `name`='陈二' AND `gender`='男' AND `age`=22 AND `grade`='大四' AND `major`='数据科学' AND `email`='chener@example.com' AND `phone`='13800000010' LIMIT 1;
UPDATE `abs`.`student` SET `id`=8, `name`='郑十', `gender`='女', `age`=21, `grade`='大三', `major`='计算机科学', `email`='zhengshi@example.com', `phone`='13800000008' WHERE `id`=8 AND `name`='郑十' AND `gender`='男' AND `age`=21 AND `grade`='大三' AND `major`='计算机科学' AND `email`='zhengshi@example.com' AND `phone`='13800000008' LIMIT 1;
UPDATE `abs`.`student` SET `id`=4, `name`='赵六', `gender`='女', `age`=22, `grade`='大四', `major`='数据科学', `email`='zhaoliu@example.com', `phone`='13800000004' WHERE `id`=4 AND `name`='赵六' AND `gender`='男' AND `age`=22 AND `grade`='大四' AND `major`='数据科学' AND `email`='zhaoliu@example.com' AND `phone`='13800000004' LIMIT 1;
sql 复制代码
# 查看这个 binlog 里 student 表上所有 DELETE 的原始记录。
python3 binlog2sql.py -h10.0.0.53 -P3306 -uroot -proot123 -d abs -t student --start-file='mysql-binlog.000019'    --sql-type=delete
# 把 DELETE 操作反转成 INSERT,生成回滚 SQL------用来把删掉的数据插回去。
python3 binlog2sql.py -h10.0.0.53 -P3306 -uroot -proot123 -d abs -t student --start-file='mysql-binlog.000019'    --sql-type=delete -B

-- 将误删除数据信息做恢复
INSERT INTO `abs`.`student`(`id`, `name`, `gender`, `age`, `grade`, `major`, `email`, `phone`) VALUES (9, '刘一', '男', 20, '大二', '人工智能', 'liuyi@example.com', '13800000009');
INSERT INTO `abs`.`student`(`id`, `name`, `gender`, `age`, `grade`, `major`, `email`, `phone`) VALUES (8, '郑十', '男', 21, '大三', '计算机科学', 'zhengshi@example.com', '13800000008');
INSERT INTO `abs`.`student`(`id`, `name`, `gender`, `age`, `grade`, `major`, `email`, `phone`) VALUES (7, '吴九', '男', 23, '大四', '软件工程', 'wujiu@example.com', '13800000007');
INSERT INTO `abs`.`student`(`id`, `name`, `gender`, `age`, `grade`, `major`, `email`, `phone`) VALUES (6, '周八', '女', 19, '大一', '物联网工程', 'zhouba@example.com', '13800000006');
相关推荐
香蕉大香蕉(06敬畏市场)1 小时前
MySQL 单点详解(01)
java·数据库·mysql·面试·博客·实习
LaughingZhu1 小时前
Product Hunt 热榜 2026-10-30:Empacer、Scape、Floot in ChatGPT
java·前端·数据库·人工智能·chatgpt·notion
standxy1 小时前
即时库存同步实战:从旺店通拉取库存详情落库 MySQL 的完整策略
数据库·集成·电商
麦壳饼1 小时前
SonnetDB:工业 IoT 中的 PID 历史分析与 PLC 协作
数据库·sonnetdb
吹什么轩2 小时前
MySQL 事务管理:从 ACID 到 MVCC,一篇讲透
数据库·mysql
时间售卖铺2 小时前
Oracle DQL精髓:从基础到高级实战
运维·数据库·云原生·oracle
paopaokaka_luck2 小时前
基于springboot+vue的益暖微光公益服务平台(DeepSeek公益助手、善款信息追溯、志愿服务积分闭环、ECharts运营分析、活动状态自动流转)
java·前端·javascript·spring boot·mysql·架构·echarts
m0_639906942 小时前
MySQL 锁机制详解|表锁、行锁、意向锁,死锁成因与解决方案
数据库·mysql
进击的荆棘3 小时前
MySQL——在ubuntu环境下安装MySQL
数据库·mysql·ubuntu