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