首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫

基於 Binlog 的 MySQL 誤刪除數據回滾 Python 腳本實戰

     編輯:MySQL 資料庫相關知識

場景:誤 DELETE 如何秒級恢復?

備份恢復需重放 Binlog,耗時不可控。**前提:binlog_format=ROW**,否則無法從 Binlog 解析出具體行數據,只能走備份恢復。

核心思路:Binlog 解析 → DELETE 轉 INSERT → 回放

  1. mysqlbinlog 導出指定時間區間 Binlog
  2. 解析 ROW 格式事件,將 DELETE 反轉為 INSERT
  3. 處理特殊數據類型(DATETIME 加引號、負數去括號、轉義字符)
  4. 生成 SQL 腳本回放恢復

步驟 1:導出 Binlog 片段

mysqlbinlog --no-defaults \
  --start-datetime='2012-12-25 14:56:00' \
  --stop-datetime='2012-12-25 14:57:00' \
  -vv mysql-bin.000001 > /home/zhoujy/restore/binlog.txt

-vv 關鍵:將 ROW 事件解碼為可讀的偽 SQL 格式。

步驟 2:原始 Binlog 長什麼樣?

### DELETE FROM test.me_info
### WHERE
### @1=2165974 /* INT meta=0 nullable=0 is_null=0 */
### @2='1984:03:17' /* DATE meta=0 nullable=1 is_null=0 */
### @3=NULL /* DATE meta=765 nullable=1 is_null=1 */
### @4=2012-10-25 00:00:00 /* DATETIME meta=0 nullable=0 is_null=0 */
### @5='' /* VARSTRING(765) meta=765 nullable=1 is_null=0 */
### @6=0 /* TINYINT meta=0 nullable=1 is_null=0 */
### @7='' /* VARSTRING(765) meta=765 nullable=1 is_null=0 */
### @8=-1 (4294967295) /* INT meta=0 nullable=1 is_null=0 */
### @9=0 /* MEDIUMINT meta=0 nullable=1 is_null=0 */
...(共 36 列)

步驟 3:三大轉換規則

類型Binlog 格式目標 INSERT 格式
DATETIME@4=2012-10-25 00:00:00'2012-10-25 00:00:00'
負數 INT@8=-1 (4294967295)-1(去掉括號內無符號補碼)
字符串轉義'O\'Reilly''O\\'Reilly'(雙重轉義)

步驟 4:Python 恢復腳本 restore.py

#!/bin/env python
# -*- encoding: utf-8 -*-
# restore.py: 通過 Binlog 恢復 DELETE 誤操作數據
# Author: zhoujy

import sys

def read_binlog(file, column_num):
    f = open(file)
    num = '@' + str(column_num)
    while True:
        line = f.readline()
        if not line:
            break
        if line.strip()[:3] != '###':
            continue
        parts = line.split(' ', 3)
        # DELETE FROM -> INSERT INTO
        if parts[1] == 'DELETE' and parts[2] == 'FROM':
            parts[1] = 'INSERT'
            parts[2] = 'INTO'
            parts[3] = parts[3].strip()
        # WHERE -> VALUES (
        if parts[1].strip() == 'WHERE':
            parts[1] = 'VALUES ('
        # 處理列值
        if '@' in ''.join(parts):
            col_name = parts[3].split('=', 1)[0]
            val = parts[3].split('=', 1)[1].strip()
            # 去除類型註釋 /* ... */
            val = val.split('/*')[0].strip()
            # 字符串處理:去首尾引號、轉義單引號、反斜杠
            if val.startswith("'") and val.endswith("'"):
                val = val[1:-1].replace('\\', '').replace("'", "\\'")
                val = "'" + val + "'"
            # 負數處理:去掉 (4294967295)
            elif ' ' in val and val.split()[1].startswith('('):
                val = val.split()[0]
            # NULL 處理
            elif val == 'NULL':
                pass
            # 判斷是否最後一列
            if col_name == num:
                val += ');'
            else:
                val += ','
            parts[3] = val
        print(' '.join(parts[1:]))

if __name__ == '__main__':
    read_binlog(sys.argv[1], sys.argv[2])

步驟 5:執行與驗證

# column_num=36 表示表有 36 個列
python restore.py binlog.txt 36 > binlog.sql

# 檢查生成的 SQL
head -5 binlog.sql
# INSERT INTO test.me_info VALUES (2123269, '1990:11:12', NULL, 2, '', 0, '', -1, 0, 340800, 1, 0, '', ..., 1, NULL);

# 回放恢復
mysql test < binlog.sql

避坑指南與擴展

  • 只適用 ROW 格式:STATEMENT/MIXED 格式無法解析具體行值。
  • 時間窗口要精確:--start/stop-datetime 縮小範圍,避免解析無關事件。
  • 大表分批恢復:生成的 SQL 過大可分檔案、分批 source。
  • 擴展支持 UPDATE:解析 ### UPDATE 事件,提取 SET 子句與 WHERE 子句(前後映射)。
  • 生產環境建議:封裝為工具鏈,集成 flashback 類開源項目(如 mysqlbinlog-flashback),功能更完善。

總結

掌握 mysqlbinlog -vv 解析 ROW 事件格式,編寫針對性轉換腳本,能在**分鐘級恢復誤刪數據**,是 DBA 應急工具箱裡的利器。核心在于**細節處理**:DATETIME 加引號、負數去括號、字符串雙重轉義。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved