誤操作后快速恢復數據 binlog 解析為反向 SQL

誤操作后快速恢復數據 binlog 解析為反向 SQL

1.前言

本文將介紹使用 reverse_sql 工具將 GreatSQL 數據庫的 binlog 解析為反向 SQL 語句。模擬誤操作后,恢復數據。該工具可以幫助客戶在發生事故時快速恢復數據,避免進一步的損失。使用 reverse_sql 工具非常簡單,客戶只需要指定肇事時間和表名即可。該工具會根據指定的時間點,在數據庫中查找并還原該表在該時間點之前的數據狀態。這樣客戶就能輕松地實現數據恢復,防止因意外操作或其他問題導致的數據丟失。

2.reverse_sql 工具簡介

reverse_sql 是一個用于解析和轉換二進制日志(binlog)的工具。它可以將二進制日志文件中記錄的數據庫更改操作(如插入、更新、刪除)轉換為反向的 SQL 語句,以便進行數據恢復。其運行模式需二進制日志設置為 ROW 格式。

下載地址:

https://github.com/hcymysql/reverse_sql

3.工具特點

該工具的主要功能和特點包括(針對 GreatSQL):

1、解析二進制日志:reverse_sql 能夠解析 GreatSQL 的二進制日志文件,并還原出其中的 SQL 語句。

2、生成可讀的 SQL:生成原始 SQL 和反向 SQL。

3、支持過濾和篩選:可以根據時間范圍、表、DML操作等條件來過濾出具體的誤操作 SQL 語句。

4、支持多線程并發解析 binlog 事件。

注意:reverse_sql 只是將二進制日志還原為 SQL 語句,而不會執行這些 SQL 語句來修改數據庫。

4.使用前檢查

4.1 GreatSQL 環境檢查

首先需要確認二進制日志設置是 ROW 格式以及 row_image 是 FULL。

工具運行時,首先會進行GreatSQL的環境檢測(if binlog_format != 'ROW' and binlog_row_image != 'FULL'),如果不同時滿足這兩個條件,程序直接退出。

greatsql> SHOW VARIABLES LIKE '%binlog%';
+------------------------------------------------+----------------------+
| Variable_name                                  | Value                |
+------------------------------------------------+----------------------+
| binlog_cache_size                              | 32768                |
| binlog_checksum                                | CRC32                |
| binlog_ddl_skip_rewrite                        | OFF                  |
| binlog_direct_non_transactional_updates        | OFF                  |
| binlog_encryption                              | OFF                  |
| binlog_error_action                            | ABORT_SERVER         |
| binlog_expire_logs_auto_purge                  | ON                   |
| binlog_expire_logs_seconds                     | 2592000              |
| binlog_format                                  | ROW                  |
| binlog_group_commit_sync_delay                 | 0                    |
| binlog_group_commit_sync_no_delay_count        | 0                    |
| binlog_gtid_simple_recovery                    | ON                   |
| binlog_max_flush_queue_time                    | 0                    |
| binlog_order_commits                           | ON                   |
| binlog_rotate_encryption_master_key_at_startup | OFF                  |
| binlog_row_event_max_size                      | 8192                 |
| binlog_row_image                               | FULL                 |
| binlog_row_metadata                            | MINIMAL              |
| binlog_row_value_options                       |                      |
| binlog_rows_query_log_events                   | OFF                  |
| binlog_skip_flush_commands                     | OFF                  |
| binlog_space_limit                             | 0                    |
| binlog_stmt_cache_size                         | 32768                |
| binlog_transaction_compression                 | OFF                  |
| binlog_transaction_compression_level_zstd      | 3                    |
| binlog_transaction_dependency_history_size     | 25000                |
| binlog_transaction_dependency_tracking         | WRITESET             |
| have_backup_safe_binlog_info                   | YES                  |
| innodb_api_enable_binlog                       | OFF                  |
| log_statements_unsafe_for_binlog               | ON                   |
| max_binlog_cache_size                          | 4294967296           |
| max_binlog_size                                | 1073741824           |
| max_binlog_stmt_cache_size                     | 18446744073709547520 |
| rpl_read_binlog_speed_limit                    | 0                    |
| sync_binlog                                    | 1                    |
+------------------------------------------------+----------------------+
35 rows in set (0.01 sec)

4.2 參數binlog_format解析

GreatSQL 支持三種二進制日志格式:

  1. STATEMENT (基于語句的復制 - SBR)

    • 記錄實際執行的 SQL 語句

    • 優點:日志文件較小,記錄的是語句而非數據變更

    • 缺點:某些非確定性函數(如 NOW(), UUID(), RAND())可能導致主從數據不一致

  2. ROW (基于行的復制 - RBR) GreatSQL 默認使用 ROW 作為二進制日志格式。

    • 記錄每行數據的變化情況

    • 優點:最安全的復制方式,能準確復制數據變更

    • 缺點:日志文件較大,特別是批量操作時

  3. MIXED (混合模式)

    • 默認使用 STATEMENT 格式,但在某些情況下自動切換到 ROW 格式

4.3 參數binlog_row_image解析

binlog_row_image 是 GreatSQL 中控制二進制日志記錄的參數,它決定了在使用基于行的復制時,二進制日志中的行鏡像如何被記錄。

參數選項

binlog_row_image 參數有三個可選值:

  • FULL:記錄每一行的變更,包括所有列的前后鏡像。–生產環境建議設置為 FULL 最佳。
  • MINIMAL:binlog日志的前鏡像只記錄唯一識別列(唯一索引列、主鍵列),后鏡像只記錄修改列。只記錄必要的列,即在更新操作中只記錄變更的列和用于識別行的最小列集。
  • NOBLOB:類似于 FULL,但不包括 BLOB 和 TEXT 類型的列,除非它們是必要的。

使用場景

  • 當設置為 FULL 時,GreatSQL 記錄所有列的變更,這可以確保數據的完整性,但可能會導致二進制日志的大小增加。
  • 設置為 MINIMAL 可以減少日志的大小,因為它只記錄變更的列和必要的列,這對于減少磁盤 I/O 和網絡傳輸是有益的。
  • NOBLOB 選項適用于那些不希望記錄大型 BLOB 或 TEXT 數據的場景,但仍然需要記錄其他類型列的變更。

5.工具與數據庫用戶賦權

5.1 工具賦予執行權限

在服務器上解壓后,授權:

$ ll rev*
-rwxr-xr-x 1 root root 50780824 Apr 28 14:23 reverse_sql_mysql8
$ chmod 755 reverse_sql_mysql8 

5.2 數據庫用戶賦權(最小化用戶權限)

greatsql> CREATE USER 'test'@'%' identified BY 'test';
Query OK, 0 rows affected (0.15 sec)greatsql> GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO `test`@`%`;
Query OK, 0 rows affected (0.01 sec)greatsql> GRANT SELECT ON `test`.* TO `test`@`%`;
Query OK, 0 rows affected (0.01 sec)greatsql> SHOW grants FOR test;
+------------------------------------------------------------------+
| Grants FOR test@%                                                |
+------------------------------------------------------------------+
| GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO `test`@`%` |
| GRANT SELECT ON `test`.* TO `test`@`%`                           |
+------------------------------------------------------------------+
2 rows in set (0.00 sec)

5.3 查看使用幫助信息

$ ./reverse_sql_mysql8 --help
usage: reverse_sql_mysql8 [-h] [-ot ONLY_TABLES [ONLY_TABLES ...]] [-op ONLY_OPERATION] -H MYSQL_HOST -P MYSQL_PORT -u MYSQL_USER -p MYSQL_PASSWD -d MYSQL_DATABASE [-c MYSQL_CHARSET] --binlog-file BINLOG_FILE[--binlog-pos BINLOG_POS] --start-time ST --end-time ET [--max-workers MAX_WORKERS] [--print] [--replace] [-v]Binlog數據恢復,生成反向SQL語句。options:-h, --help            show this help message and exit-ot ONLY_TABLES [ONLY_TABLES ...], --only-tables ONLY_TABLES [ONLY_TABLES ...]設置要恢復的表,多張表用,逗號分隔-op ONLY_OPERATION, --only-operation ONLY_OPERATION設置誤操作時的命令(insert/update/delete-H MYSQL_HOST, --mysql-host MYSQL_HOSTMySQL主機名-P MYSQL_PORT, --mysql-port MYSQL_PORTMySQL端口號-u MYSQL_USER, --mysql-user MYSQL_USERMySQL用戶名-p MYSQL_PASSWD, --mysql-passwd MYSQL_PASSWDMySQL密碼-d MYSQL_DATABASE, --mysql-database MYSQL_DATABASEMySQL數據庫名-c MYSQL_CHARSET, --mysql-charset MYSQL_CHARSETMySQL字符集,默認utf8--binlog-file BINLOG_FILEBinlog文件--binlog-pos BINLOG_POSBinlog位置,默認4--start-time ST       起始時間--end-time ET         結束時間--max-workers MAX_WORKERS線程數,默認4(并發越高,鎖的開銷就越大,適當調整并發數)--print               將解析后的SQL輸出到終端--replace             將update轉換為replace操作-v, --version         show program's version number and exitExample usage:shell> ./reverse_sql -ot table1 -op delete -H 192.168.198.239 -P 3336 -u admin -p hechunyang -d hcy \--binlog-file mysql-bin.000124 --start-time "2023-07-06 10:00:00" --end-time "2023-07-06 22:00:00" 

6.案例演示

6.1 創建測試數據,模擬誤更新

greatsql> CREATE TABLE t1 (  id INT AUTO_INCREMENT PRIMARY KEY,  name VARCHAR(50),  age INT,  email VARCHAR(100),  gender ENUM('Male', 'Female', 'Other'),  salary DECIMAL(10,2),  join_date DATE,  is_active BOOLEAN  );
Query OK, 0 rows affected (0.04 sec)greatsql> INSERT INTO t1 (name, age, email, gender, salary, join_date, is_active) VALUES  ('張三', 28, 'zhangsan@example.com', 'Male', 8500.00, '2020-05-15', TRUE),  ('李四', 32, 'lisi@example.com', 'Male', 9200.50, '2019-08-22', TRUE),  ('王五', 25, 'wangwu@example.com', 'Male', 7800.00, '2021-03-10', TRUE),  ('趙六', 29, 'zhaoliu@example.com', 'Female', 8800.75, '2020-11-05', TRUE),  ('肖七', 35, 'xiaoqi@example.com', 'Female', 10500.00, '2018-06-18', FALSE),  ('孫八', 27, 'sunba@example.com', 'Male', 8100.00, '2021-01-30', TRUE),  ('周九', 31, 'zhoujiu@example.com', 'Other', 9500.25, '2019-09-12', TRUE),  ('吳十', 24, 'wushi@example.com', 'Female', 7600.50, '2022-02-14', TRUE),  ('鄭十一', 30, 'zhengshiyi@example.com', 'Male', 8900.00, '2020-07-25', FALSE),  ('王十二', 33, 'wangshier@example.com', 'Female', 10000.00, '2019-04-08', TRUE);
Query OK, 10 rows affected (0.01 sec)
Records: 10  Duplicates: 0  Warnings: 0greatsql> SELECT * FROM t1;
+----+-----------+------+------------------------+--------+----------+------------+-----------+
| id | name      | age  | email                  | gender | salary   | join_date  | is_active |
+----+-----------+------+------------------------+--------+----------+------------+-----------+
|  1 | 張三      |   28 | zhangsan@example.com   | Male   |  8500.00 | 2020-05-15 |         1 |
|  2 | 李四      |   32 | lisi@example.com       | Male   |  9200.50 | 2019-08-22 |         1 |
|  3 | 王五      |   25 | wangwu@example.com     | Male   |  7800.00 | 2021-03-10 |         1 |
|  4 | 趙六      |   29 | zhaoliu@example.com    | Female |  8800.75 | 2020-11-05 |         1 |
|  5 | 肖七      |   35 | xiaoqi@example.com     | Female | 10500.00 | 2018-06-18 |         0 |
|  6 | 孫八      |   27 | sunba@example.com      | Male   |  8100.00 | 2021-01-30 |         1 |
|  7 | 周九      |   31 | zhoujiu@example.com    | Other  |  9500.25 | 2019-09-12 |         1 |
|  8 | 吳十      |   24 | wushi@example.com      | Female |  7600.50 | 2022-02-14 |         1 |
|  9 | 鄭十一    |   30 | zhengshiyi@example.com | Male   |  8900.00 | 2020-07-25 |         0 |
| 10 | 王十二    |   33 | wangshier@example.com  | Female | 10000.00 | 2019-04-08 |         1 |
+----+-----------+------+------------------------+--------+----------+------------+-----------+
10 rows in set (0.00 sec)greatsql> UPDATE t1   SET salary = 9800.00, is_active = TRUE   WHERE id = 5;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0greatsql> SELECT * FROM t1;
+----+-----------+------+------------------------+--------+----------+------------+-----------+
| id | name      | age  | email                  | gender | salary   | join_date  | is_active |
+----+-----------+------+------------------------+--------+----------+------------+-----------+
|  1 | 張三      |   28 | zhangsan@example.com   | Male   |  8500.00 | 2020-05-15 |         1 |
|  2 | 李四      |   32 | lisi@example.com       | Male   |  9200.50 | 2019-08-22 |         1 |
|  3 | 王五      |   25 | wangwu@example.com     | Male   |  7800.00 | 2021-03-10 |         1 |
|  4 | 趙六      |   29 | zhaoliu@example.com    | Female |  8800.75 | 2020-11-05 |         1 |
|  5 | 肖七      |   35 | xiaoqi@example.com     | Female |  9800.00 | 2018-06-18 |         1 |
|  6 | 孫八      |   27 | sunba@example.com      | Male   |  8100.00 | 2021-01-30 |         1 |
|  7 | 周九      |   31 | zhoujiu@example.com    | Other  |  9500.25 | 2019-09-12 |         1 |
|  8 | 吳十      |   24 | wushi@example.com      | Female |  7600.50 | 2022-02-14 |         1 |
|  9 | 鄭十一    |   30 | zhengshiyi@example.com | Male   |  8900.00 | 2020-07-25 |         0 |
| 10 | 王十二    |   33 | wangshier@example.com  | Female | 10000.00 | 2019-04-08 |         1 |
+----+-----------+------+------------------------+--------+----------+------------+-----------+
10 rows in set (0.00 sec)

6.2 解析binlog

查看 binlog 當前信息

greatsql> SHOW MASTER STATUS \G
*************************** 1. row ***************************File: binlog.000002Position: 2918Binlog_Do_DB: Binlog_Ignore_DB: 
Executed_Gtid_Set: 2a3248f7-e762-11ef-ae09-00163e11ac96:1,
3837053e-e762-11ef-ade8-00163e2cc6be:1-4008,
615fadb3-234d-11f0-ab29-00163e2cc6be:1-5,
aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa1:1-1067166
1 row in set (0.00 sec)

執行解析命令

 ./reverse_sql_mysql8 -u 'test' -p 'test' -H 172.17.136.70 -P 3301 -d test_111 -ot t1 --binlog-file /greatsql/dbdata/3301/log/binlog.000009 --start-time "2025-05-22 16:30:00" --end-time "2025-05-22 16:40:00"Processing binlogevents: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 50.32event/s]
Processing binlogevents: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 2/2 [00:00<00:00, 111.72event/s]
Processing binlogevents: 0event [00:00, ?event/s]
Processing binlogevents: 0event [00:00, ?event/s]

當出現誤操作時,只需指定誤操作的時間段,其對應的 binlog 文件(通常可以通過 show master status 得到當前的 binlog 文件名)以及剛才誤操作的表,和具體的 DML 命令,比如 update 或者 delete。【不支持一次解析多個 binlog】

6.3 查看解析文件

$ ll test*
-rw-r--r-- 1 root root 5892 Apr 28 15:47 test_111_t1_recover_2025-04-28_15:47:17.sql
$ 
$ cat test_111_t1_recover_2025-04-28_15:47:17.sql 
-- SQL執行時間:2025-04-28 15:41:32
-- 原生sql:-- INSERT INTO `test_111`.`t1`(`id`,`name`,`age`,`email`,`gender`,`salary`,`join_date`,`is_active`) VALUES (1,'張三',28,'zhangsan@example.com','Male',8500.00,'2020-05-15',1);
-- 回滾sql:DELETE FROM `test_111`.`t1` WHERE `id`=1 AND `name`='張三' AND `age`=28 AND `email`='zhangsan@example.com' AND `gender`='Male' AND `salary`=8500.00 AND `join_date`='2020-05-15' AND `is_active`=1;
-- ----------------------------------------------------------
-- SQL執行時間:2025-04-28 15:41:32
-- 原生sql:-- INSERT INTO `test_111`.`t1`(`id`,`name`,`age`,`email`,`gender`,`salary`,`join_date`,`is_active`) VALUES (2,'李四',32,'lisi@example.com','Male',9200.50,'2019-08-22',1);
-- 回滾sql:DELETE FROM `test_111`.`t1` WHERE `id`=2 AND `name`='李四' AND `age`=32 AND `email`='lisi@example.com' AND `gender`='Male' AND `salary`=9200.50 AND `join_date`='2019-08-22' AND `is_active`=1;
-- ----------------------------------------------------------
-- SQL執行時間:2025-04-28 15:41:32
-- 原生sql:-- INSERT INTO `test_111`.`t1`(`id`,`name`,`age`,`email`,`gender`,`salary`,`join_date`,`is_active`) VALUES (3,'王五',25,'wangwu@example.com','Male',7800.00,'2021-03-10',1);
-- 回滾sql:DELETE FROM `test_111`.`t1` WHERE `id`=3 AND `name`='王五' AND `age`=25 AND `email`='wangwu@example.com' AND `gender`='Male' AND `salary`=7800.00 AND `join_date`='2021-03-10' AND `is_active`=1;
-- ----------------------------------------------------------
-- SQL執行時間:2025-04-28 15:41:32
-- 原生sql:-- INSERT INTO `test_111`.`t1`(`id`,`name`,`age`,`email`,`gender`,`salary`,`join_date`,`is_active`) VALUES (4,'趙六',29,'zhaoliu@example.com','Female',8800.75,'2020-11-05',1);
-- 回滾sql:DELETE FROM `test_111`.`t1` WHERE `id`=4 AND `name`='趙六' AND `age`=29 AND `email`='zhaoliu@example.com' AND `gender`='Female' AND `salary`=8800.75 AND `join_date`='2020-11-05' AND `is_active`=1;
-- ----------------------------------------------------------
-- SQL執行時間:2025-04-28 15:41:32
-- 原生sql:-- INSERT INTO `test_111`.`t1`(`id`,`name`,`age`,`email`,`gender`,`salary`,`join_date`,`is_active`) VALUES (5,'肖七',35,'xiaoqi@example.com','Female',10500.00,'2018-06-18',0);
-- 回滾sql:DELETE FROM `test_111`.`t1` WHERE `id`=5 AND `name`='肖七' AND `age`=35 AND `email`='xiaoqi@example.com' AND `gender`='Female' AND `salary`=10500.00 AND `join_date`='2018-06-18' AND `is_active`=0;
-- ----------------------------------------------------------
-- SQL執行時間:2025-04-28 15:41:32
-- 原生sql:-- INSERT INTO `test_111`.`t1`(`id`,`name`,`age`,`email`,`gender`,`salary`,`join_date`,`is_active`) VALUES (6,'孫八',27,'sunba@example.com','Male',8100.00,'2021-01-30',1);
-- 回滾sql:DELETE FROM `test_111`.`t1` WHERE `id`=6 AND `name`='孫八' AND `age`=27 AND `email`='sunba@example.com' AND `gender`='Male' AND `salary`=8100.00 AND `join_date`='2021-01-30' AND `is_active`=1;
-- ----------------------------------------------------------
-- SQL執行時間:2025-04-28 15:41:32
-- 原生sql:-- INSERT INTO `test_111`.`t1`(`id`,`name`,`age`,`email`,`gender`,`salary`,`join_date`,`is_active`) VALUES (7,'周九',31,'zhoujiu@example.com','Other',9500.25,'2019-09-12',1);
-- 回滾sql:DELETE FROM `test_111`.`t1` WHERE `id`=7 AND `name`='周九' AND `age`=31 AND `email`='zhoujiu@example.com' AND `gender`='Other' AND `salary`=9500.25 AND `join_date`='2019-09-12' AND `is_active`=1;
-- ----------------------------------------------------------
-- SQL執行時間:2025-04-28 15:41:32
-- 原生sql:-- INSERT INTO `test_111`.`t1`(`id`,`name`,`age`,`email`,`gender`,`salary`,`join_date`,`is_active`) VALUES (8,'吳十',24,'wushi@example.com','Female',7600.50,'2022-02-14',1);
-- 回滾sql:DELETE FROM `test_111`.`t1` WHERE `id`=8 AND `name`='吳十' AND `age`=24 AND `email`='wushi@example.com' AND `gender`='Female' AND `salary`=7600.50 AND `join_date`='2022-02-14' AND `is_active`=1;
-- ----------------------------------------------------------
-- SQL執行時間:2025-04-28 15:41:32
-- 原生sql:-- INSERT INTO `test_111`.`t1`(`id`,`name`,`age`,`email`,`gender`,`salary`,`join_date`,`is_active`) VALUES (9,'鄭十一',30,'zhengshiyi@example.com','Male',8900.00,'2020-07-25',0);
-- 回滾sql:DELETE FROM `test_111`.`t1` WHERE `id`=9 AND `name`='鄭十一' AND `age`=30 AND `email`='zhengshiyi@example.com' AND `gender`='Male' AND `salary`=8900.00 AND `join_date`='2020-07-25' AND `is_active`=0;
-- ----------------------------------------------------------
-- SQL執行時間:2025-04-28 15:41:32
-- 原生sql:-- INSERT INTO `test_111`.`t1`(`id`,`name`,`age`,`email`,`gender`,`salary`,`join_date`,`is_active`) VALUES (10,'王十二',33,'wangshier@example.com','Female',10000.00,'2019-04-08',1);
-- 回滾sql:DELETE FROM `test_111`.`t1` WHERE `id`=10 AND `name`='王十二' AND `age`=33 AND `email`='wangshier@example.com' AND `gender`='Female' AND `salary`=10000.00 AND `join_date`='2019-04-08' AND `is_active`=1;
-- ----------------------------------------------------------
-- SQL執行時間:2025-04-28 15:41:57
-- 原生sql:-- UPDATE `test_111`.`t1` SET `id`=5,`name`='肖七',`age`=35,`email`='xiaoqi@example.com',`gender`='Female',`salary`=9800.00,`join_date`='2018-06-18',`is_active`=1 WHERE `id`=5 AND `name`='肖七' AND `age`=35 AND `email`='xiaoqi@example.com' AND `gender`='Female' AND `salary`=10500.00 AND `join_date`='2018-06-18' AND `is_active`=0;
-- 回滾sql:UPDATE `test_111`.`t1` SET `id`=5,`name`='肖七',`age`=35,`email`='xiaoqi@example.com',`gender`='Female',`salary`=10500.00,`join_date`='2018-06-18',`is_active`=0 WHERE `id`=5 AND `name`='肖七' AND `age`=35 AND `email`='xiaoqi@example.com' AND `gender`='Female' AND `salary`=9800.00 AND `join_date`='2018-06-18' AND `is_active`=1;
-- ----------------------------------------------------------
$ 

工具運行后,會在當前目錄下生成一個 {db}_{table}_recover.sql 文件,保存著原生 SQL(原生 SQL 會加注釋) 和反向 SQL,如果想將結果輸出到前臺終端,可以指定 --print 選項。

$ ./reverse_sql_mysql8 -u 'test' -p 'test' -H 172.17.136.70 -P 3301 -d test_111 -ot t1 --binlog-file /greatsql/dbdata/3301/log/binlog.000008 --start-time "2025-05-21 11:10:00" --end-time "2025-05-21 11:18:00" --print
Processing binlogevents: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 4/4 [00:00<00:00, 258.00event/s]
Processing binlogevents: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 4/4 [00:00<00:00, 292.57event/s]
Processing binlogevents: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 5/5 [00:00<00:00, 277.56event/s]
Processing binlogevents: 0event [00:00, ?event/s]
-- SQL執行時間:2025-05-21 11:14:01 
-- 原生sql:-- INSERT INTO `test_111`.`t1`(`id`,`name`,`age`,`email`,`gender`,`salary`,`join_date`,`is_active`) VALUES (1,'張三',28,'zhangsan@example.com','Male',8500.00,'2020-05-15',1); 
-- 回滾sql:DELETE FROM `test_111`.`t1` WHERE `id`=1 AND `name`='張三' AND `age`=28 AND `email`='zhangsan@example.com' AND `gender`='Male' AND `salary`=8500.00 AND `join_date`='2020-05-15' AND `is_active`=1;
-- ------------------------------------------------------------ SQL執行時間:2025-05-21 11:14:01 
-- 原生sql:-- INSERT INTO `test_111`.`t1`(`id`,`name`,`age`,`email`,`gender`,`salary`,`join_date`,`is_active`) VALUES (2,'李四',32,'lisi@example.com','Male',9200.50,'2019-08-22',1); 
-- 回滾sql:DELETE FROM `test_111`.`t1` WHERE `id`=2 AND `name`='李四' AND `age`=32 AND `email`='lisi@example.com' AND `gender`='Male' AND `salary`=9200.50 AND `join_date`='2019-08-22' AND `is_active`=1;
-- ----------------------------------------------------------

如果你想把 update 操作轉換為 replace,指定 --replace 選項即可,同時會在當前目錄下生成一個{db}_{table}_recover_replace.sql 文件。

$ ./reverse_sql_mysql8 -u 'test' -p 'test' -H 172.17.136.70 -P 3301 -d test_111 -ot t1 --binlog-file /greatsql/dbdata/3301/log/binlog.000008 --start-time "2025-05-21 11:10:00" --end-time "2025-05-21 11:18:00" --replace
Processing binlogevents: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 4/4 [00:00<00:00, 83.25event/s]
Processing binlogevents: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 4/4 [00:00<00:00, 112.54event/s]
Processing binlogevents: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 5/5 [00:00<00:00, 172.61event/s]
Processing binlogevents: 0event [00:00, ?event/s]
$ ll
-rwxr-xr-x 1 root    root    50780824 May 21 10:28 reverse_sql_mysql8
-rw-r--r-- 1 root    root         646 May 21 11:19 test_111_t1_recover_2025-05-21_11:19:28_replace.sql
-rw-r--r-- 1 root    root        5892 May 21 11:19 test_111_t1_recover_2025-05-21_11:19:28.sql
$ cat test_111_t1_recover_2025-05-21_11:19:28_replace.sql 
-- SQL執行時間:2025-05-21 11:14:36
-- 原生sql:-- UPDATE `test_111`.`t1` SET `id`=5,`name`='錢七',`age`=35,`email`='xiaoqi@example.com',`gender`='Female',`salary`=9800.00,`join_date`='2018-06-18',`is_active`=1 WHERE `id`=5 AND `name`='錢七' AND `age`=35 AND `email`='xiaoqi@example.com' AND `gender`='Female' AND `salary`=10500.00 AND `join_date`='2018-06-18' AND `is_active`=0;
-- 回滾sql:REPLACE INTO `test_111`.`t1` (`id`,`name`,`age`,`email`,`gender`,`salary`,`join_date`,`is_active`) VALUES (5,'錢七',35,'xiaoqi@example.com','Female',10500.00,'2018-06-18',0);
-- ----------------------------------------------------------

但注意,如果字段值中包含換行符,則原生 SQL 后幾行不會被注釋,需要手動處理。

6.4 拿到反向SQL恢復數據

在{db}_{table}_recover.sql 文件中找到剛才誤操作的 DML 語句,然后在 GreatSQL 數據庫中執行逆向工程后的 SQL 以恢復數據。

-- SQL執行時間:2025-04-28 15:41:57
-- 原生sql:-- UPDATE `test_111`.`t1` SET `id`=5,`name`='肖七',`age`=35,`email`='xiaoqi@example.com',`gender`='Female',`salary`=9800.00,`join_date`='2018-06-18',`is_active`=1 WHERE `id`=5 AND `name`='肖七' AND `age`=35 AND `email`='xiaoqi@example.com' AND `gender`='Female' AND `salary`=10500.00 AND `join_date`='2018-06-18' AND `is_active`=0;
-- 回滾sql:UPDATE `test_111`.`t1` SET `id`=5,`name`='肖七',`age`=35,`email`='xiaoqi@example.com',`gender`='Female',`salary`=10500.00,`join_date`='2018-06-18',`is_active`=0 WHERE `id`=5 AND `name`='肖七' AND `age`=35 AND `email`='xiaoqi@example.com' AND `gender`='Female' AND `salary`=9800.00 AND `join_date`='2018-06-18' AND `is_active`=1;
greatsql> UPDATE `test_111`.`t1` SET `id`=5,`name`='肖七',`age`=35,`email`='xiaoqi@example.com',`gender`='Female',`salary`=10500.00,`join_date`='2018-06-18',`is_active`=0 WHERE `id`=5 AND `name`='肖七' AND `age`=35 AND `email`='xiaoqi@example.com' AND `gender`='Female' AND `salary`=9800.00 AND `join_date`='2018-06-18' AND `is_active`=1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0greatsql> SELECT * FROM t1;
+----+-----------+------+------------------------+--------+----------+------------+-----------+
| id | name      | age  | email                  | gender | salary   | join_date  | is_active |
+----+-----------+------+------------------------+--------+----------+------------+-----------+
|  1 | 張三      |   28 | zhangsan@example.com   | Male   |  8500.00 | 2020-05-15 |         1 |
|  2 | 李四      |   32 | lisi@example.com       | Male   |  9200.50 | 2019-08-22 |         1 |
|  3 | 王五      |   25 | wangwu@example.com     | Male   |  7800.00 | 2021-03-10 |         1 |
|  4 | 趙六      |   29 | zhaoliu@example.com    | Female |  8800.75 | 2020-11-05 |         1 |
|  5 | 肖七      |   35 | xiaoqi@example.com     | Female | 10500.00 | 2018-06-18 |         0 |
|  6 | 孫八      |   27 | sunba@example.com      | Male   |  8100.00 | 2021-01-30 |         1 |
|  7 | 周九      |   31 | zhoujiu@example.com    | Other  |  9500.25 | 2019-09-12 |         1 |
|  8 | 吳十      |   24 | wushi@example.com      | Female |  7600.50 | 2022-02-14 |         1 |
|  9 | 鄭十一    |   30 | zhengshiyi@example.com | Male   |  8900.00 | 2020-07-25 |         0 |
| 10 | 王十二    |   33 | wangshier@example.com  | Female | 10000.00 | 2019-04-08 |         1 |
+----+-----------+------+------------------------+--------+----------+------------+-----------+
10 rows in set (0.00 sec)

可以看到這條誤更新的數據已經恢復到最初狀態。

如果 {db}_{table}_recover.sql 文件的內容過多,也可以通過 awk 命令進行分割,以便更容易進行排查。

$ awk '/^-- SQL執行時間/{filename = "output" ++count ".sql"; print > filename; next} {print > filename}' test_111_t1_recover_2025-04-28_15:47:17.sql
$ ll output*
-rw-r--r-- 1 root root 524 Apr 28 16:22 output10.sql
-rw-r--r-- 1 root root 802 Apr 28 16:22 output11.sql
-rw-r--r-- 1 root root 508 Apr 28 16:22 output1.sql
-rw-r--r-- 1 root root 500 Apr 28 16:22 output2.sql
-rw-r--r-- 1 root root 504 Apr 28 16:22 output3.sql
-rw-r--r-- 1 root root 510 Apr 28 16:22 output4.sql
-rw-r--r-- 1 root root 510 Apr 28 16:22 output5.sql
-rw-r--r-- 1 root root 502 Apr 28 16:22 output6.sql
-rw-r--r-- 1 root root 508 Apr 28 16:22 output7.sql
-rw-r--r-- 1 root root 506 Apr 28 16:22 output8.sql
-rw-r--r-- 1 root root 518 Apr 28 16:22 output9.sql
$ cat output1.sql
-- SQL執行時間:2025-04-28 15:41:32
-- 原生sql:-- INSERT INTO `test_111`.`t1`(`id`,`name`,`age`,`email`,`gender`,`salary`,`join_date`,`is_active`) VALUES (1,'張三',28,'zhangsan@example.com','Male',8500.00,'2020-05-15',1);
-- 回滾sql:DELETE FROM `test_111`.`t1` WHERE `id`=1 AND `name`='張三' AND `age`=28 AND `email`='zhangsan@example.com' AND `gender`='Male' AND `salary`=8500.00 AND `join_date`='2020-05-15' AND `is_active`=1;
-- ----------------------------------------------------------
$ 

不支持 drop 和 truncate 操作,因為這兩個操作屬于物理性刪除,需要通過歷史備份進行恢復。

可能存在的問題:

  • 數據庫密碼含特殊字符:需要加單引號圈起來;
  • 數據庫字段中如有換行符或者其他特殊字符,則原生sql顯示的時候后面可能不會注釋,導致回滾 sql 錯亂。因此需要我們將原生 SQL 用正則替換,即“-- 原生sql:”和“-- 回滾sql:”之間的部分(-- 原生sql:.*?-- 回滾sql:)

7.reverse_sql與binlog2sql主要區別

7.1 reverse_sql工具解析

./reverse_sql_mysql8 --help
usage: reverse_sql_mysql8 [-h] [-ot ONLY_TABLES [ONLY_TABLES ...]] [-op ONLY_OPERATION] -H MYSQL_HOST -P MYSQL_PORT -u MYSQL_USER -p MYSQL_PASSWD -d MYSQL_DATABASE [-c MYSQL_CHARSET] --binlog-file BINLOG_FILE[--binlog-pos BINLOG_POS] --start-time ST --end-time ET [--max-workers MAX_WORKERS] [--print] [--replace] [-v]Binlog數據恢復,生成反向SQL語句。options:-h, --help            show this help message and exit-ot ONLY_TABLES [ONLY_TABLES ...], --only-tables ONLY_TABLES [ONLY_TABLES ...]設置要恢復的表,多張表用,逗號分隔-op ONLY_OPERATION, --only-operation ONLY_OPERATION設置誤操作時的命令(insert/update/delete)-H MYSQL_HOST, --mysql-host MYSQL_HOSTMySQL主機名-P MYSQL_PORT, --mysql-port MYSQL_PORTMySQL端口號-u MYSQL_USER, --mysql-user MYSQL_USERMySQL用戶名-p MYSQL_PASSWD, --mysql-passwd MYSQL_PASSWDMySQL密碼-d MYSQL_DATABASE, --mysql-database MYSQL_DATABASEMySQL數據庫名-c MYSQL_CHARSET, --mysql-charset MYSQL_CHARSETMySQL字符集,默認utf8--binlog-file BINLOG_FILEBinlog文件--binlog-pos BINLOG_POSBinlog位置,默認4--start-time ST       起始時間--end-time ET         結束時間--max-workers MAX_WORKERS線程數,默認4(并發越高,鎖的開銷就越大,適當調整并發數)--print               將解析后的SQL輸出到終端--replace             將update轉換為replace操作-v, --version         show program's version number and exitExample usage:shell> ./reverse_sql -ot table1 -op delete -H 192.168.198.239 -P 3336 -u admin -p hechunyang -d hcy \--binlog-file mysql-bin.000124 --start-time "2023-07-06 10:00:00" --end-time "2023-07-06 22:00:00" 

7.2 binlog2sql 工具解析

python binlog2sql.py --help
解析模式: 
--stop-never 持續解析binlog。可選。默認False,同步至執行命令時最新的binlog位置。 
-K, --no-primary-key 對INSERT語句去除主鍵。可選。默認False 
-B, --flashback 生成回滾SQL,可解析大文件,不受內存限制。可選。默認False。與stop-never或no-primary-key不能同時添加。 
--back-interval -B模式下,每打印一千行回滾SQL,加一句SLEEP多少秒,如不想加SLEEP,請設為0。可選。默認1.0。
解析范圍控制: 
--start-file 起始解析文件,只需文件名,無需全路徑 。必須。 
--start-position/--start-pos 起始解析位置。可選。默認為start-file的起始位置。 
--stop-file/--end-file 終止解析文件。可選。默認為start-file同一個文件。若解析模式為stop-never,此選項失效。 
--stop-position/--end-pos 終止解析位置。可選。默認為stop-file的最末位置;若解析模式為stop-never,此選項失效。 
--start-datetime 起始解析時間,格式'%Y-%m-%d %H:%M:%S'。可選。默認不過濾。 
--stop-datetime 終止解析時間,格式'%Y-%m-%d %H:%M:%S'。可選。默認不過濾。
對象過濾: 
-d, --databases 只解析目標db的sql,多個庫用空格隔開,如-d db1 db2。可選。默認為空。 
-t, --tables 只解析目標table的sql,多張表用空格隔開,如-t tbl1 tbl2。可選。默認為空。 
--only-dml 只解析dml,忽略ddl。可選。默認False。 
--sql-type 只解析指定類型,支持INSERT, UPDATE, DELETE。多個類型用空格隔開,如--sql-type INSERT DELETE。可選。默認為增刪改都解析。用了此參數但沒填任何類型,則三者都不解析。

7.3 兩個工具的區別

核心功能對比

特性reverse_sqlbinlog2sql
生成方向專注反向SQL (UNDO)默認正向SQL,需加 -B 參數生成反向SQL
輸出格式直接生成可執行的回滾SQL可選原始SQL/回滾SQL/帶注釋的SQL
過濾條件基礎過濾(時間/表名)更靈活(GTID/position/庫名等)
大文件處理未明確說明明確支持大文件(-B 模式不受內存限制)
時間范圍必須指定 --start-time 和 --end-time可選(支持按時間或位置過濾)
并發控制支持線程數調節(–max-workers)無并發控制選項
斷點續傳依賴binlog位置(–binlog-pos)支持更靈活的起止位置(–start-pos/–end-pos)
表級過濾支持(-ot 多表逗號分隔)支持(-t 多表空格分隔)
操作類型過濾必須指定(-op 僅限一種操作)靈活過濾(–sql-type 可多選或留空)
DDL忽略不支持支持(–only-dml 忽略DDL)
輸出控制終端打印(–print)或直接執行默認打印,支持回滾SLEEP間隔(–back-interval)
主鍵處理無相關選項支持去除主鍵(-K 模式)
UPDATE轉換支持轉REPLACE(–replace)無此功能
  1. 數據恢復方向
    1. reverse_sql:專門用于生成反向 SQL(UNDO SQL),主要用于數據回滾/修復場景
    2. binlog2sql:默認生成正向 SQL,也可通過參數生成反向 SQL
  2. 實現方式
    1. reverse_sql 基于 Python 實現
    2. binlog2sql 也是 Python 實現但更早出現
  3. 使用場景
    1. reverse_sql
      ? 需要快速生成精準回滾 SQL(需明確操作類型和時間范圍)
      ? 處理大型事務(自動分組優化)
    2. binlog2sql
      ? 需要分析 binlog 詳細內容(支持實時解析)
      ?持續監控或復雜過濾條件(多操作類型組合過濾,如 GTID / 多庫多表)
      ? 大 binlog 文件處理,優先 binlog2sql -B(內存優化更可靠)

8.總結

reverse_sql 在生成回滾 SQL 時更加靈活和高效。reverse_sql 數據閃回工具為數據庫管理和維護提供了極大的便利,特別是在數據恢復方面。它允許用戶在發生錯誤時能夠迅速采取行動,減少數據丟失帶來的損失。然而,為了有效使用此工具,必須注意其實施條件,如確保日志記錄功能正常運行,以及維護良好的備份習慣。此外,了解工具的具體操作方法及其限制也非常重要,這有助于更安全、高效地管理數據庫資源。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/89814.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/89814.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/89814.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

ABP VNext + Grafana Loki:集中式日志聚合

&#x1f4dd; ABP VNext Grafana Loki&#xff1a;集中式日志聚合 &#x1f4da; 目錄&#x1f4dd; ABP VNext Grafana Loki&#xff1a;集中式日志聚合一、引言? TL;DR二、環境與依賴&#x1f6e0;? 平臺版本&#x1f517; NuGet 包?? 基礎服務三、Serilog Loki 集成…

分布在內側內嗅皮層(MEC)的帶狀細胞對NLP中的深層語義分析有什么積極的影響和啟示

帶狀細胞&#xff08;Band Cells&#xff09;作為內側內嗅皮層&#xff08;Medial Entorhinal Cortex, MEC&#xff09;層Ⅱ/Ⅲ的核心空間編碼單元&#xff08;如網格細胞、頭方向細胞等&#xff09;&#xff0c;其獨特的神經計算機制為自然語言處理&#xff08;NLP&#xff09…

Django Ninja

Django Ninja 是一個用于 Django 框架的快速、現代化的 API 開發庫&#xff0c;旨在簡化構建高性能、類型安全的 RESTful API。它受到 FastAPI 的啟發&#xff0c;結合了 Django 的強大功能和 FastAPI 的簡潔與現代化設計&#xff0c;特別適合需要快速開發、易于維護且具有強類…

iic時序

數據和應答信號都規定在SCL在高電平期間&#xff0c;SDA電平穩定&#xff1b;SCL在低電平期間&#xff0c;SDA電平才可以變化。要不然容易被誤認為起始或停止信號。應答信號&#xff1a;1. 第九個SCL之前的低電平期間將SDA拉低2. 確保在SCL為高電平時&#xff0c;SDA為穩定的低…

GitHub+Git新手使用說明

Git Git是一個在本地用于隨時保存和查看歷史版本的軟件Git的三個概念&#xff1a;提交commit、倉庫repository、分支branch Git安裝 在電腦里面按winR&#xff0c;輸入cmd進入終端后輸入git --version&#xff0c;然后再次輸入where git&#xff0c;查看git所在位置 Git常用語句…

前端圖像視頻實時檢測

需求&#xff1a;在目標檢測時&#xff0c;我們要求前端能夠將后端檢測的結果實時渲染在圖片或者視頻上。圖片是靜態的&#xff0c;只需要渲染一次&#xff1b;而視頻是動態的&#xff0c;播放時需要根據幀數來實時渲染標注框&#xff0c;可以想象視頻就是由一張張圖片播放的效…

如何解決pip安裝報錯ModuleNotFoundError: No module named ‘sqlalchemy’問題

【Python系列Bug修復PyCharm控制臺pip install報錯】如何解決pip安裝報錯ModuleNotFoundError: No module named ‘sqlalchemy’問題 摘要 在使用 PyCharm 控制臺執行 pip install sqlalchemy 后&#xff0c;仍然在代碼中提示 ModuleNotFoundError: No module named sqlalche…

第4.3節 iOS App生成追溯關系

iOS生成追溯關系的邏輯和Android端從用戶角度來說是一致的&#xff0c;就是需要通過開始和結束關聯用例&#xff0c;將用例信息與覆蓋率信息建立關系&#xff0c;然后再解析覆蓋率數據。 4.3.1 添加關聯用例彈層 關聯用例彈層和Android類似&#xff0c;只要你能設計出相應的樣…

STM32 USB鍵盤實現指南

概述 在STM32上實現鍵盤功能可以通過USB HID(人機接口設備)協議來實現,使STM32設備能被計算機識別為標準鍵盤。以下是完整的實現方案: 硬件準備 STM32開發板(支持USB,如STM32F103、STM32F4系列) USB接口(Micro USB或Type-C) 按鍵矩陣或單個按鍵 必要的電阻和連接…

繼電器基礎知識

繼電器是一種電控制器件,它具有隔離功能的自動開關元件,廣泛應用于遙控、遙測、通訊、自動控制、機電一體化及電力電子設備中,是最重要的控制元件之一。 繼繼電器的核心功能是通過小電流來控制大電流的通斷。它通常包含一個線圈和一組或多組觸點。當給繼電器的線圈施加一定…

MYSQL:庫的操作

文章目錄MYSQL&#xff1a;庫的操作1. 本文簡述2. 查看數據庫2.1 語法3. 創建數據庫3.1 語法3.2 示例3.2.1 創建一個簡單的數據庫3.2.2 使用 IF NOT EXISTS 防止重復創建3.2.3 再次運行&#xff0c;觀察現象3.2.4 查看這個警告到底是什么3.2.5 創建數據庫允許使用關鍵字4. 字符…

Xilinx FPGA XCKU115?2FLVA1517I AMD KintexUltraScale

XCKU115?2FLVA1517I 隸屬于 Xilinx &#xff08;AMD&#xff09;Kintex UltraScale 系列&#xff0c;基于領先的 20?nm FinFET 制程打造。該器件采用 1517?ball FCBGA&#xff08;FLVA1517&#xff09;封裝&#xff0c;速度等級 ?2&#xff0c;可實現高達 725?MHz 的核心邏…

Linux Ubuntu安裝教程|附安裝文件?安裝教程

[軟件名稱]: Linux Ubuntu18.0 [軟件大小]: 1.8GB [安裝環境]: VMware [夸克網盤接] 鏈接&#xff1a;https://pan.quark.cn/s/971f685256ef &#xff08;建議用手機保存到網盤后&#xff0c;再用電腦下載&#xff09;更多免費軟件見https://docs.qq.com/sheet/DRkdWVFFCWm9UeG…

深入解析Hadoop YARN:三層調度模型與資源管理機制

Hadoop YARN概述與產生背景從MapReduce到YARN的演進之路在Hadoop早期版本中&#xff0c;MapReduce框架采用JobTracker/TaskTracker架構&#xff0c;這種設計逐漸暴露出嚴重局限性。JobTracker需要同時處理資源管理和作業控制兩大核心功能&#xff0c;隨著集群規模擴大&#xff…

Pycaita二次開發基礎代碼解析:邊線提取、路徑追蹤與曲線固定

本文將深入剖析CATIA二次開發中三個核心類方法&#xff1a;邊線提取特征創建、元素結構路徑查找和草圖曲線固定技術。通過逐行解讀代碼實現&#xff0c;揭示其在工業設計中的專業應用價值和技術原理。一、邊線提取技術&#xff1a;幾何特征的精確捕獲與復用1.1 方法功能全景ext…

Linux 任務調度在進程管理中的關系和運行機制

&#x1f4d6; 推薦閱讀&#xff1a;《Yocto項目實戰教程:高效定制嵌入式Linux系統》 &#x1f3a5; 更多學習視頻請關注 B 站&#xff1a;嵌入式Jerry Linux 任務調度在進程管理中的關系和運行機制 Linux 內核中的“任務調度”是進程管理系統的核心部分&#xff0c;相互關聯而…

JAVA后端開發—— JWT(JSON Web Token)實踐

1. 什么是HTTP請求頭 (Request Headers)&#xff1f;當你的瀏覽器或手機App向服務器發起一個HTTP請求時&#xff0c;這個請求并不僅僅包含你要訪問的URL&#xff08;比如 /logout&#xff09;和可能的數據&#xff08;請求體&#xff09;&#xff0c;它還附帶了一堆“元數據&am…

【SVM smote】MAP - Charting Student Math Misunderstandings

針對數據不平衡問題&#xff0c;用調整類別權重的方式來處理數據不平衡問題&#xff0c;同時使用支持向量機&#xff08;SVM&#xff09;模型進行訓練。 我們通過使用 SMOTE&#xff08;Synthetic Minority Over-sampling Technique&#xff09;進行過采樣&#xff0c;增加少數…

repmgr+pgbouncer實現對業務透明的高可用切換

本方案說明 PostgreSQL repmgr&#xff1a;實現主從自動故障檢測與切換&#xff08;Failover&#xff09;。PgBouncer&#xff1a;作為連接池&#xff0c;屏蔽后端數據庫變動&#xff0c;提供透明連接。動態配置更新&#xff1a;通過repmgr組件的promote_command階段觸發腳本…

查找服務器上存在線程泄露的進程

以下是一個改進的命令&#xff0c;可以列出所有線程數大于200的進程及其PID和線程數&#xff1a; find /proc -maxdepth 1 -type d -regex /proc/[0-9] -exec sh -c for pid_dir dopid$(basename "$pid_dir")if [ -f "$pid_dir/status" ]; thenthreads$(aw…