生產環境誤操作刪除表(DROP TABLE),無備份情況下,利用 MySQL 二進制日志(Binlog)提取 DML 語句重放,實現數據零損失恢復。本文記錄完整實戰過程:發現 Binlog、導出 SQL、編寫提取程序、篩選目標表操作、重放驗證。
FLUSH TABLES WITH READ LOCK 不適用)phpMyAdmin 「狀態」→「二進制日志」顯示:
log_bin)MIXED(含 STATEMENT 格式的 DDL/DML)mysql-bin.000015 ~ 000019

# 查找數據目錄
find / -name "mysql-bin.*" 2>/dev/null
# 典型路徑:/var/lib/mysql/mysql-bin.000015

# 單文件導出
mysqlbinlog --no-defaults /var/lib/mysql/mysql-bin.000015 > /tmp/000015.sql
# 批量導出(按時間範圍)
mysqlbinlog --no-defaults --start-datetime="2024-01-01 00:00:00" --stop-datetime="2024-01-02 00:00:00" /var/lib/mysql/mysql-bin.00001* > /tmp/recover.sql

需求:從海量 Binlog SQL 中提取 task 表的 INSERT/UPDATE,DELETE 轉為 INSERT(因 MyISAM 無事務,需去重)。
package com.nerve.sql.reload;
import java.io.*;
import java.util.*;
public class ReloadWorker {
public void read(List orgFiles, String targetFile, String table) throws Exception {
BufferedWriter bw = new BufferedWriter(new FileWriter(targetFile, true));
table = table.toUpperCase();
for (String file : orgFiles) {
BufferedReader br = new BufferedReader(new FileReader(file));
String line;
while ((line = br.readLine()) != null) {
String upper = line.toUpperCase();
// 提取 UPDATE 語句
if (upper.startsWith("UPDATE " + table)) {
bw.append(line + ";\n");
}
// 提取 INSERT 語句,轉換為 DELETE + INSERT 避免主鍵衝突
else if (upper.startsWith("INSERT INTO " + table)) {
// 簡單提取 ID 值(實際需解析 VALUES 子句)
String idPart = upper.substring(upper.lastIndexOf(","));
String id = idPart.replaceAll("[^0-9]", "");
bw.append("DELETE FROM " + table + " WHERE id=" + id + ";\n");
bw.append(line + ";\n");
}
}
br.close();
}
bw.flush();
bw.close();
}
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
List files = Arrays.asList(
"C:/Users/IBM_ADMIN/Desktop/000015.txt",
"C:/Users/IBM_ADMIN/Desktop/000016.txt",
"C:/Users/IBM_ADMIN/Desktop/000017.txt",
"C:/Users/IBM_ADMIN/Desktop/000018.txt",
"C:/Users/IBM_ADMIN/Desktop/000019.txt"
);
new ReloadWorker().read(files, "C:/Users/IBM_ADMIN/Desktop/000017_sql.txt", "task");
System.out.println("DONE, on " + (System.currentTimeMillis() - start)/1000 + " s");
}
}
# 導入提取的 SQL
mysql -u root -p -D production_db < /tmp/000017_sql.txt

| 環節 | 關鍵點 |
|---|---|
| Binlog 格式 | STATEMENT/MIXED 格式下 DDL/DML 可直接讀取;ROW 格式需 --verbose --base64-output=DECODE-ROWS 解析 |
| 提取邏輯 | 針對目標表名匹配;INSERT 需配合 DELETE 避免主鍵衝突(MyISAM/無主鍵表) |
| 時間範圍 | 用 --start-datetime/--stop-datetime 縮小範圍,加速導出 |
| 數據一致性 | MyISAM 無事務,恢復後需業務驗證;InnoDB 恢復需注意事務邊界 |
| 工具選擇 | 小數據量用 grep/awk;大數據量/複雜邏輯寫程序(Java/Python/Go) |
log_bin=mysql-bin,設置 expire_logs_days=7 以上binlog_format=ROW,避免主從不一致,恢復時用 mysqlbinlog --verboseBinlog 是誤操作後的「後悔藥」。本案例通過 mysqlbinlog 導出 + 定向提取程序 + 重放,在無備份情況下實現 **100% 數據恢復**,耗時約 5 分鐘。核心經驗:**平時不燒香,臨時抱佛腳不靈譜** —— 提前配置 Binlog、驗證恢復流程,才是生產環境生存之道。