MySQL閃回原理與實戰

来源:http://www.cnblogs.com/dfcao/archive/2017/01/09/mysql-flashback-priciple-and-practice.html
-Advertisement-
Play Games

本文將介紹閃回原理,給出筆者的實戰經驗,並對現存的閃回工具作比較。 DBA或開發人員,有時會誤刪或者誤更新數據,如果是線上環境並且影響較大,就需要能快速回滾。傳統恢復方法是利用備份重搭實例,再應用去除錯誤sql後的binlog來恢複數據。此法費時費力,甚至需要停機維護,並不適合快速回滾。也有團隊利用 ...


本文將介紹閃回原理,給出筆者的實戰經驗,並對現存的閃回工具作比較。

 

DBA或開發人員,有時會誤刪或者誤更新數據,如果是線上環境並且影響較大,就需要能快速回滾。傳統恢復方法是利用備份重搭實例,再應用去除錯誤sql後的binlog來恢複數據。此法費時費力,甚至需要停機維護,並不適合快速回滾。也有團隊利用LVM快照來縮短恢復時間,但快照的缺點是會影響mysql的性能。

MySQL閃回(flashback)利用binlog直接進行回滾,能快速恢復且不用停機。本文將介紹閃回原理,給出筆者的實戰經驗,並對現存的閃回工具作比較。

開胃菜

某天,小明因種種原因,誤刪了大批線上用戶表的數據。他急忙找到公司DBA請求幫助,“客服電話已被打爆,大量用戶投訴無法登陸,領導非常惱火。請問多久能恢複數據?”DBA一臉懵逼,沉默十秒後,伸出一根手指。“你的意思是一分鐘就能恢復?太好了。”小明終於有些放鬆,露出了一絲笑容。“不,我們中有個人將會離開公司。”DBA沉痛的說道。

勿讓悲劇發生,儘早將此文轉給公司DBA。

閃回原理

binlog概述

MySQL binlog以event的形式,記錄了MySQL server從啟用binlog以來所有的變更信息,能夠幫助重現這之間的所有變化。MySQL引入binlog主要有兩個目的:一是為了主從複製;二是某些備份還原操作後需要重新應用binlog。

有三種可選的binlog格式,各有優缺點:

  • statement:基於SQL語句的模式,binlog數據量小,但是某些語句和函數在複製過程可能導致數據不一致甚至出錯;
  • row:基於行的模式,記錄的是行的完整變化。很安全,但是binlog會比其他兩種模式大很多;
  • mixed:混合模式,根據語句來選用是statement還是row模式;

利用binlog閃回,需要將binlog格式設置為row。row模式下,一條使用innodb的insert會產生如下格式的binlog:

# at 1129
#161225 23:15:38 server id 3773306082  end_log_pos 1197         Query   thread_id=1903021       exec_time=0     error_code=0
SET TIMESTAMP=1482678938/*!*/;
BEGIN
/*!*/;
# at 1197
#161225 23:15:38 server id 3773306082  end_log_pos 1245         Table_map: `test`.`user` mapped to number 290
# at 1245
#161225 23:15:38 server id 3773306082  end_log_pos 1352         Write_rows: table id 290 flags: STMT_END_F

BINLOG '
muJfWBPiFOjgMAAAAN0EAAAAACIBAAAAAAEABHRlc3QABHVzZXIAAwMPEQMeAAAC
muJfWB7iFOjgawAAAEgFAAAAACIBAAAAAAEAAgAD//gBAAAABuWwj+i1tVhK1hH4AgAAAAblsI/p
krFYStYg+AMAAAAG5bCP5a2ZWE/onPgEAAAABuWwj+adjlhNeAD4BQAAAAJ0dFhRYJM=
'/*!*/;
# at 1352
#161225 23:15:38 server id 3773306082  end_log_pos 1379         Xid = 5327954
COMMIT/*!*/;

 

閃回原理

既然binlog以event形式記錄了所有的變更信息,那麼我們把需要回滾的event,從後往前回滾回去即可。

對於單個event的回滾,我們以表test.user來演示原理

mysql> show create table test.user\G
*************************** 1. row ***************************
       Table: user
Create Table: CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(10) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8

 

  • 對於delete操作,我們從binlog提取出delete信息,生成的回滾語句是insert。(註:為了方便解釋,我們用binlog2sql將原始binlog轉化成了可讀SQL)

    原始:DELETE FROM `test`.`user` WHERE `id`=1 AND `name`='小趙';
    回滾:INSERT INTO `test`.`user`(`id`, `name`) VALUES (1, '小趙');
  • 對於insert操作,回滾SQL是delete。

    原始:INSERT INTO `test`.`user`(`id`, `name`) VALUES (2, '小錢');
    回滾:DELETE FROM `test`.`user` WHERE `id`=2 AND `name`='小錢';
  • 對於update操作,回滾sql應該交換SET和WHERE的值。

    原始:UPDATE `test`.`user` SET `id`=3, `name`='小李' WHERE `id`=3 AND `name`='小孫';
    回滾:UPDATE `test`.`user` SET `id`=3, `name`='小孫' WHERE `id`=3 AND `name`='小李';

閃回實戰

真實的閃回場景中,最關鍵的是能快速篩選出真正需要回滾的SQL。

我們使用開源工具binlog2sql來進行實戰演練。binlog2sql由美團點評DBA團隊(上海)出品,多次線上上環境做快速回滾。

首先我們安裝binlog2sql:

shell> git clone https://github.com/danfengcao/binlog2sql.git && cd binlog2sql
shell> pip install -r requirements.txt

 

背景:小明在11:44時誤刪了test庫user表大批的數據,需要緊急回滾。

test庫user表原有數據
mysql> select * from user;
+----+--------+---------------------+
| id | name   | addtime             |
+----+--------+---------------------+
|  1 | 小趙   | 2013-11-11 00:04:33 |
|  2 | 小錢   | 2014-11-11 00:04:48 |
|  3 | 小孫   | 2016-11-11 20:25:00 |
|  4 | 小李   | 2013-11-11 00:00:00 |
.........
+----+--------+---------------------+
16384 rows in set (0.04 sec)

11:44時,user表大批數據被誤刪除。與此同時,正常業務數據是在繼續寫入的
mysql> delete from user where addtime>'2014-01-01';
Query OK, 16128 rows affected (0.18 sec)

mysql> select count(*) from user;
+----------+
| count(*) |
+----------+
|      261 |
+----------+

 

恢複數據步驟
  1. 登錄mysql,查看目前的binlog文件

    mysql> show master logs;
    +------------------+-----------+
    | Log_name         | File_size |
    +------------------+-----------+
    | mysql-bin.000053 | 168652863 |
    | mysql-bin.000054 |    504549 |
    +------------------+-----------+

     

  2. 最新的binlog文件是mysql-bin.000054。我們的目標是篩選出需要回滾的SQL,由於誤操作人只知道大致的誤操作時間,我們首先根據時間做一次過濾。只需要解析test庫user表。(註:如果有多個sql誤操作,則生成的binlog可能分佈在多個文件,需解析多個文件)

    shell> python binlog2sql/binlog2sql.py -h127.0.0.1 -P3306 -uadmin -p'admin' -dtest -tuser --start-file='mysql-bin.000054' --start-datetime='2016-12-26 11:44:00' --stop-datetime='2016-12-26 11:50:00' > /tmp/raw.sql
    raw.sql 輸出:
    DELETE FROM `test`.`user` WHERE `addtime`='2014-11-11 00:04:48' AND `id`=2 AND `name`='小錢' LIMIT 1; #start 257427 end 265754 time 2016-12-26 11:44:56
    DELETE FROM `test`.`user` WHERE `addtime`='2015-11-11 20:25:00' AND `id`=3 AND `name`='小孫' LIMIT 1; #start 257427 end 265754 time 2016-12-26 11:44:56
    ...
    DELETE FROM `test`.`user` WHERE `addtime`='2016-12-14 23:09:07' AND `id`=24530 AND `name`='tt' LIMIT 1; #start 257427 end 504272 time 2016-12-26 11:44:56
    INSERT INTO `test`.`user`(`addtime`, `id`, `name`) VALUES ('2016-12-10 00:04:33', 32722, '小王'); #start 504299 end 504522 time 2016-12-26 11:49:42
    ...

     

  3. 根據位置信息,我們確定了誤操作sql來自同一個事務,準確位置在257427-504272之間(binlog2sql對於同一個事務會輸出同樣的start position)。再根據位置過濾,使用 -B 選項生成回滾sql,檢查回滾sql是否正確。(註:真實場景下,生成的回滾SQL經常會需要進一步篩選。結合grep、編輯器等)

    shell> python binlog2sql/binlog2sql.py -h127.0.0.1 -P3306 -uadmin -p'admin' -dtest -tuser --start-file='mysql-bin.000054' --start-position=257427 --stop-position=504272 -B > /tmp/rollback.sql
    rollback.sql 輸出:
    INSERT INTO `test`.`user`(`addtime`, `id`, `name`) VALUES ('2016-12-14 23:09:07', 24530, 'tt'); #start 257427 end 504272 time 2016-12-26 11:44:56
    INSERT INTO `test`.`user`(`addtime`, `id`, `name`) VALUES ('2016-12-12 00:00:00', 24529, '小李'); #start 257427 end 504272 time 2016-12-26 11:44:56
    ...
    INSERT INTO `test`.`user`(`addtime`, `id`, `name`) VALUES ('2014-11-11 00:04:48', 2, '小錢'); #start 257427 end 265754 time 2016-12-26 11:44:56
    
    shell> wc -l /tmp/rollback.sql
    16128 /tmp/rollback.sql

     

  4. 與業務方確認回滾sql沒問題,執行回滾語句。登錄mysql,確認回滾成功。

    shell> mysql -h127.0.0.1 -P3306 -uadmin -p'admin' < /tmp/rollback.sql
    
    mysql> select count(*) from user;
    +----------+
    | count(*) |
    +----------+
    |    16389 |
    +----------+

     

TIPS

  • 閃回的關鍵是快速篩選出真正需要回滾的SQL。
  • 先根據庫、表、時間做一次過濾,再根據位置做更準確的過濾。
  • 由於數據一直在寫入,要確保回滾sql中不包含其他數據。可根據是否是同一事務、誤操作行數、欄位值的特征等等來幫助判斷。
  • 執行回滾sql時如有報錯,需要查實具體原因,一般是因為對應的數據已發生變化。由於是嚴格的行模式,只要有唯一鍵(包括主鍵)存在,就只會報某條數據不存在的錯,不必擔心會更新不該操作的數據。
  • 如果待回滾的表與其他表有關聯,要與開發說明回滾和不回滾各自的副作用,再確定方案。
  • 回滾後數據變化,可能對用戶和線上應用造成困惑(類似幻讀)。

再重覆下最重要的兩點:篩選出正確SQL溝通清楚

閃回工具

MySQL閃回特性最早由阿裡彭立勛開發,彭在2012年給官方提交了一個patch,並對閃回設計思路做了說明(設計思路很有啟發性,強烈推薦閱讀)。但是因為種種原因,業內安裝這個patch的團隊至今還是少數,真正應用到線上的更是少之又少。彭之後,又有多位人員針對不同mysql版本不同語言開發了閃回工具,原理用的都是彭的思路。

我將這些閃回工具按實現方式分成了三類。

  • 第一類是以patch形式集成到官方工具mysqlbinlog中。以彭提交的patch為代表。

    優點

    • 上手成本低。mysqlbinlog原有的選項都能直接利用,只是多加了一個閃回選項。閃回特性未來有可能被官方收錄。
    • 支持離線解析。

    缺點

    • 相容性差、項目活躍度不高。由於binlog格式的變動,如果閃回工具作者不及時對補丁升級,則閃回工具將無法使用。目前已有多位人員分別針對mysql5.5,5.6,5.7開發了patch,部分項目代碼公開,但總體上活躍度都不高。
    • 難以添加新功能,實戰效果欠佳。在實戰中,經常會遇到現有patch不滿足需求的情況,比如要加個表過濾,很簡單的一個需求,代碼改動也不會大,但對大部分DBA來說,改mysql源碼還是很困難的事。
    • 安裝稍顯麻煩。需要對mysql源碼打補丁再編譯生成。

    這些缺點,可能都是閃回沒有流行開來的原因。

  • 第二類是獨立工具,通過偽裝成slave拉取binlog來進行處理。以binlog2sql為代表。

    優點

    • 相容性好。偽裝成slave拉binlog這項技術在業界應用的非常廣泛,多個開發語言都有這樣的活躍項目,MySQL版本的相容性由這些項目搞定,閃回工具的相容問題不再突出。
    • 添加新功能的難度小。更容易被改造成DBA自己喜歡的形式。更適合實戰。
    • 安裝和使用簡單。

    缺點

    • 必須開啟MySQL server。
  • 第三類是簡單腳本。先用mysqlbinlog解析出文本格式的binlog,再根據回滾原理用正則進行匹配並替換。

    優點

    • 腳本寫起來方便,往往能快速搞定某個特定問題。
    • 安裝和使用簡單。
    • 支持離線解析。

    缺點

    • 通用性不好。
    • 可靠性不好。

就目前的閃回工具而言,線上環境的閃回,筆者建議使用binlog2sql,離線解析使用mysqlbinlog。

關於DDL的flashback

本文所述的flashback僅針對DML語句的快速回滾。但如果誤操作是DDL的話,是無法利用binlog做快速回滾的,因為即使在row模式下,binlog對於DDL操作也不會記錄每行數據的變化。要實現DDL快速回滾,必須修改MySQL源碼,使得在執行DDL前先備份老數據。目前有多個mysql定製版本實現了DDL閃回特性,阿裡林曉斌團隊提交了patch給MySQL官方,MariaDB預計在不久後加入包含DDL的flashback特性。DDL閃回的副作用是會增加額外存儲。考慮到其應用頻次實在過低,本文不做詳述,有興趣的同學可以自己去瞭解,重要的幾篇文章我在參考資料中做了引用。

有任何問題,或有mysql閃回相關的優秀工具優秀文章遺漏,煩請告知。 [email protected]

參考資料

[1] MySQL Internals Manual , Chapter 20 The Binary Log

[2] 彭立勛,MySQL下實現閃回的設計思路

[3] Lixun Peng, Provide the flashback feature by binlog

[4] 王廣友,mysqlbinlog flashback 5.6完全使用手冊與原理

[5] 薑承堯, 拿走不謝,Flashback for MySQL 5.7

[6] 林曉斌, MySQL閃回方案討論及實現

[7] xiaobin lin, flashback from binlog for MySQL

[8] mariadb.com, AliSQL and some features that have made it into MariaDB Server

[9] danfengcao, binlog2sql: Parse MySQL binlog to SQL you want


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 雙擊pkg文件安裝 一路向下,記得保存最後彈出框中的密碼(它是你mysql root賬號的密碼) 正常情況下,安裝成功。 此時只是安裝成功,但還需要額外的配置: (1) 進入系統偏好設置 (2) 點擊mysql 開啟mysql服務 此時我們在命令行輸入mysql -uroot -p命令會提示沒有co ...
  • 記憶體分配機制Slab Allocation 本文參考博客:https://my.oschina.net/bieber/blog/505458 Memcached的記憶體分配是以slabs為單位的,會根據初始chunk大小、增長因數、存儲數據的大小實際劃分出多個不同的slabs class,slab c ...
  • 大綱簡介 安裝前,先簡單介紹一下memcached。 memcached是一個免費、開源、高性能的分散式緩存。設計memcached的初衷是為了加快web應用程式,減少DB負載。 安裝要求:支持大多數linux和基於BSD的系統,官方沒有給出windows版本,但是網上有memcached for ...
  • 本篇將去探索twemproxy源碼的主幹流程,想來對於想要開始啃這份優秀源碼生肉的童鞋會有不小的幫助。這裡我們首先要找到 twemproxy正確的打開方式——twemproxy的文件結構,接著介紹twemproxy程式代碼框架,最後介紹twemproxy程式的主幹流程。主幹流程是本章節的重中之重。這 ...
  • 本文出處:http://www.cnblogs.com/wy123/p/6262800.html 在考慮重編譯T-SQL(或者存儲過程)的時候,有兩種方式可以實現強制重編譯(前提是忽略導致重編譯的其他因素的情況下,比如重建索引,更新統計信息等等), 一是基於WITH RECOMPILE的存儲過程級別 ...
  • 寫在前面 在QQ群,微信群,論壇中經常幫助使用SQL Server資料庫的朋友解決問題,但是有一些最常見最基本的問題,每天都有人問,回答多了也不想再解答了,索性把這些問題整理一下,再有人問到直接發鏈接。 一時想法而寫這篇文章,問題可能不全面,後續會一直更新。 基礎問題收集 資源下載 描述:XX版本數 ...
  • 一、HBase的特點是什麼 1.HBase一個分散式的基於列式存儲的資料庫,基於hadoop的hdfs存儲,zookeeper進行管理。 2.HBase適合存儲半結構化或非結構化數據,對於數據結構欄位不夠確定或者雜亂無章很難按一個概念去抽取的數據。 3.HBase為null的記錄不會被存儲. 4.基 ...
  • twemproxy背景 在業務量劇增的今天,單台高速緩存伺服器已經無法滿足業務的需求, 而相較於大容量SSD數據存儲方案,緩存具備速度和成本優勢,但也存在數據安全性的挑戰。為此搭建一個高速緩存伺服器集群來進行分散式存儲是十分必要的。 目前主流的高速緩存伺服器是redis和memchache。而twe ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...