本系列文章主要是本人在游戲服務端開發過程中,遇到的一些不那么為人熟知但我又覺得比較重要的MySQL知識的介紹。希望里面淺薄的文字能為了提供一點點的幫助。
數據回滾與數據還原
相信大家在自己的職業生涯中可能都有聽說過甚至是遇到過下面這樣的場景:
- 直接操作數據庫時失誤,不小心delete錯表或者delete的where條件錯誤(曾經同組一個開發因為delete語句沒加where條件半夜把我call醒)。對于開發成員有線上數據庫操作權限的項目組來說出現這種情況的概率比想象的要大很多。需要手動操作數據庫說明情況已經比較緊急了,慌忙且緊急的情況下出問題的概率自然會高;
- 代碼出了bug導致數據操作錯誤,比如數據被誤刪、修改等等;
- 某某公司的一位員工一怒之下把線上數據庫給清了;
DBA或開發人員,有時會誤刪或者誤更新數據。線上環境如果出現這樣的情況就需要快速恢復。其中使用數據庫手段回滾數據,是最常用并且很多時候是唯一的選項(有些功能上出現的BUG可能通過其他途徑進行修復)。
上面所說的問題有些可以通過權限管理和其他手段去避免(這些我會在后面提到)。但是這里想介紹的是如果真的不幸發生了以上這些問題,我們可以如何做數據回滾。下面我用一個自己遇到的案例來介紹,并列舉一些業界比較常用的方案做一些簡單的對比。如果你們還沒有對恢復數據這一塊做過考慮,可以根據自身項目的實際情況看看哪種比較適合你們。
回滾案例介紹
Bug表現:
線上新上了一個換皮活動B(由活動A換皮而來),活動B上線一段時間陸續有玩家投訴之前的活動A的獎勵進度沒了,原來一些能夠領取的活動A的獎勵也無法領??;還有一些玩家剛參與活動B就能領取最終大獎。
Bug原因:
查看代碼發現,因為活動A和活動B邏輯和持久化的數據都一模一樣,開發只是將活動A的代碼拷過來就當是活動B,最后導致活動B的數據存進了活動A的表中。這樣就有一部分玩家活動A的數據被活動B數據替換,導致活動A的數據丟失和活動B的數據錯亂。數據的邏輯錯誤和正確的情況如下圖所示:
錯誤的做法:
正確的做法:
活動B上線之后,活動A是不會對數據進行修改。所以我們只要拿到活動B上線前對表A最近的一次備份,然后執行從這個備份時間節點到活動B真正產生數據時間節點這一時間段表A產生的SQL即可。
修復過程:
活動A的數據修復:
1. 取受影響的活動A的表A最近一次全量備份,我們線上數據庫是一天一備。所以最近一次備份是當天0點;
2. 用第一步得到的備份表A恢復出一個臨時表A;
3. 然后通過線上binlog日志,解析出從備份時間節點到活動B上線之間表A產生的SQL(只取增刪改的SQL);
4. 把第3步得到的SQL,在臨時表A執行一遍;
5. 最后把臨時表A,復制到線上數據庫,熱更代碼使活動A讀取這張臨時表A。
活動B的數據修復:
1. 緊急屏蔽線上活動B;
2. 通過線上binlog日志,解析出從活動B上線之后在表A產生的所有SQL(只取增刪改的SQL);
3. 創建表B(和表A結構一致),然后把第2步得到的SQL,在表B執行一遍;
4. 熱更代碼使活動B讀取表B,線上重新開啟活動B。
業界其他數據回滾方式:
1. Flashback:
介紹:Flashback方式可以在沒有全備的情況下,將數據回滾到一定時間范圍內(和binlog日志保存時間掛鉤)任意時間節點上。Flashback恢復數據的原理是:binlog_format=ROW的情況下會記錄數據更新前后全部狀態,這樣就能從binlog中獲得顛倒的SQL然后拿回到當前表進行重放(insert變delete、、detele變回insert、update前后的值互換)。原理介紹請戳:https://zhuanlan.zhihu.com/p/68845158
優點:利用備份重搭實例,再應用去除錯誤sql后的binlog來恢復數據的方式操作較為繁瑣,甚至需要停機維護,很難做到快速回滾。Flashback利用binlog直接進行回滾,能快速恢復且不用停機。
缺點:因為binlog要設置為ROW格式,所以binlog日志會變得更大,對硬件的要求也會提升。
使用要求:需要數據庫的binlog相關配置滿足:
binlog_format=ROW # 日志記錄精確到每一行的修改
binlog_row_image=FULL # 每次修改都記錄修改前后的值
查看binlog日志是否開啟命令:show global variables like 'log_bin%';
查看binlog_format格式命令:show global variables like '%binlog_format%';
或者查看MySQL的配置文件:my.cnf(Linux系統) 或者 my.ini(Windows系統)
業界一些優秀的Flashback工具:MyFlash 、binlog2sql。
2. 沒有binlog日志的每日全備:
介紹:很多公司都有每日全備(存最近15天的全備數據,每天一份)。如果出現問題,直接從最近的一次全備數據中取數據。但是由于沒有開啟binlog日志,就沒辦法回滾到指定時間點的數據。比如想回滾2號下午5點的數據,只能是從2號凌晨0點全備的數據拿出來,舍棄了2號凌晨5點至2號下午5點變更的數據。
優點:不用打印binlog日志,節省機器性能;操作簡單,還原速度快。適用于一些即使有少部分數據會被丟棄也沒關系的系統——用戶行為統計日志、策劃需要查詢的玩家物品收支日志等等;
缺點:數據恢復只能恢復一部分。
使用要求:對于每日全量備份這一點,其實所有云服務器廠商都支持。如果是自己搭建的MySQL服務器可以使用全量熱備工具:Percona XtraBackup來實現每日全備,阿里和騰訊的MySQL云服務就是通過這個工具來實現備份的。該工具是一個MySQL物理熱備份工具,優點有:
- 可以在目標數據庫不關服的情況下進行熱備,過程中目標庫能正常運行(不會鎖表鎖庫)。官網對原理的介紹比較粗略,這一篇寫的更容易理解一點。實現原理是通過復制.ibd文件和redo log日志回放實現;
- 既可以進行全量備份也可以進行增量備份;
- 能將MySQL備份壓縮傳輸到其他服務器;
- 備份過程對服務器負載很小(在可控范圍會對IO和磁盤有一點點影響,當然還是建議選擇在服務器壓力最小的時段進行);
- 開源免費
總結
由于游戲常常面臨大數據量和高并發的場景,所以會對原理和設計有比較高的要求。許多游戲公司可能沒有專門的DBA(甚至也沒有專門的運維、SDK支撐),這種情況下開發人員扎實的數據庫基礎就是項目可靠的重要保證。