來自公#眾#號(hào):新世界雜貨鋪
程序猿最大的悲哀是什么!
經(jīng)歷了這兩次事故后,筆者覺得最大的悲哀莫過于半夜打電話給DBA請(qǐng)求幫忙恢復(fù)數(shù)據(jù)。程序猿和PM之間的戰(zhàn)斗往往還有來有回,而筆者碰上DBA之后撒会,那可真是求人辦事,怎么慫怎么來师妙,只要DBA大爺高興诵肛!
為了以后盡量少跪舔DBA大爺,筆者將親身經(jīng)歷的兩次事故記錄下來以提醒自己默穴。
第一次數(shù)據(jù)回滾
PM是需求的生產(chǎn)者怔檩,程序猿是需求的消費(fèi)者,這二者就是典型的生產(chǎn)者與消費(fèi)者模型蓄诽。因此本次事故的根因還是PM提出了需求薛训,故筆者認(rèn)為只要PM不再提需求就不再有事故。
唉仑氛!快醒醒乙埃,別做夢(mèng)了!
回到事故的本身锯岖,筆者先描述一下當(dāng)時(shí)的背景。
PM有大量的數(shù)據(jù)需要緊急更新到線上出吹。這需求有多緊急呢遇伞?PM要繞過QA驗(yàn)證,直接在線上先用少量數(shù)據(jù)進(jìn)行測(cè)試,少量數(shù)據(jù)驗(yàn)證通過后就更新所有剩余的數(shù)據(jù)跳芳。
結(jié)合筆者所在公司的業(yè)務(wù)場(chǎng)景孽水,筆者按照以下步驟完成了本次數(shù)據(jù)更新测柠。
1轰胁、將需要更新的數(shù)據(jù)使用mysqldump
進(jìn)行備份赃阀。
mysqldump --replace -f --single-transaction -t \
-h hostname -u user -P 3936 -p dbname tablename \
--where="id in (1,2,3)" > tablename.sql
2懂缕、開發(fā)一個(gè)腳本直接調(diào)用線上已有更新數(shù)據(jù)的接口(開發(fā)時(shí)筆者已經(jīng)在測(cè)試環(huán)境自測(cè))。
3、在線上先更新少量數(shù)據(jù)子库,并更新修改數(shù)據(jù)部分的緩存,PM對(duì)少量數(shù)據(jù)進(jìn)行驗(yàn)證脖捻。
4、PM確認(rèn)該部分?jǐn)?shù)據(jù)驗(yàn)證通過后,開始對(duì)剩余數(shù)據(jù)進(jìn)行線上更新操作率寡。
初看上面的步驟好像沒什么大問題,但實(shí)際結(jié)果卻是狠狠地打了筆者的臉。下面庙楚,筆者就好好掰扯掰扯到底是哪些原因造成了本次事故酪捡。
1、更新接口邏輯沒有理清楚
纳账,導(dǎo)致線上數(shù)據(jù)更新錯(cuò)誤逛薇。
該接口是一個(gè)比較老的服務(wù)且相關(guān)文檔少,筆者因?yàn)闆]有梳理清楚所有邏輯疏虫,調(diào)用接口時(shí)部分?jǐn)?shù)據(jù)參數(shù)傳遞有誤金刁,導(dǎo)致線上數(shù)據(jù)更新錯(cuò)誤帅涂。
2、更新接口實(shí)現(xiàn)有問題
尤蛮,調(diào)用服務(wù)后媳友,刪除了關(guān)聯(lián)表的數(shù)據(jù),所以需要恢復(fù)产捞。
如果只是上述第一個(gè)問題醇锚,筆者自己備份的replace into
語句就可完成數(shù)據(jù)的恢復(fù),但很明顯問題不止于此坯临。當(dāng)事故發(fā)生后筆者開始對(duì)該服務(wù)邏輯進(jìn)行二次梳理焊唬,發(fā)現(xiàn)此接口對(duì)主表的關(guān)聯(lián)表也進(jìn)行了更新而且更新邏輯為先刪除關(guān)聯(lián)數(shù)據(jù)然后插入新的關(guān)聯(lián)數(shù)據(jù)
。只是如此倒也罷了看靠,關(guān)鍵是該接口的實(shí)現(xiàn)者將所有請(qǐng)求參數(shù)作為一個(gè)關(guān)聯(lián)數(shù)組并將此關(guān)聯(lián)數(shù)組傳遞給所有函數(shù)赶促。好家伙,各個(gè)具有不同業(yè)務(wù)功能的函數(shù)傳遞的參數(shù)都是一樣的挟炬,這導(dǎo)致筆者第一次梳理邏輯時(shí)無法完全理清楚各個(gè)業(yè)務(wù)函數(shù)真實(shí)需要的數(shù)據(jù)到底是什么鸥滨。
關(guān)聯(lián)數(shù)據(jù)被刪除筆者也沒有備份,最后只好跪舔DBA大大幫忙進(jìn)行數(shù)據(jù)回滾谤祖。
警告?:代碼不清晰婿滓,程序猿淚兩行!
3粥喜、未經(jīng)過QA的保證凸主,就直接在線上測(cè)試。
筆者自己雖然在測(cè)試環(huán)境進(jìn)行了簡(jiǎn)單測(cè)試额湘,但是程序猿的本職還是開發(fā)不能耗費(fèi)過多的精力去完成QA的工作卿吐,而PM很明顯也不夠?qū)I(yè),這才在質(zhì)量保證環(huán)節(jié)出了錯(cuò)并擴(kuò)大了線上的錯(cuò)誤范圍锋华。
4嗡官、測(cè)試時(shí)未在無緩存環(huán)境下進(jìn)行驗(yàn)證。
初始供置,PM對(duì)少量數(shù)據(jù)的驗(yàn)證結(jié)果是沒有問題的谨湘,但是當(dāng)所有數(shù)據(jù)更新完成后緩存已經(jīng)開始逐步重建绽快,數(shù)據(jù)有誤和數(shù)據(jù)被刪的問題就開始暴露了芥丧。這是因?yàn)楣P者只更新了PM想要驗(yàn)證的數(shù)據(jù)的緩存,卻沒更新關(guān)聯(lián)數(shù)據(jù)部分的緩存坊罢,因此只有等這部分緩存自然失效問題才逐漸顯現(xiàn)续担。
后續(xù)
DBA對(duì)數(shù)據(jù)進(jìn)行回滾后,批量更新數(shù)據(jù)還得繼續(xù)盎詈ⅰ物遇!狠心的PM愣是逼著筆者大半夜修好問題繼續(xù)驗(yàn)證,唯一值得高興的可能就是這次僅更新少量數(shù)據(jù)第二天繼續(xù)更新剩余數(shù)據(jù)。最后询兴,筆者修好問題并成功地更新完全部數(shù)據(jù)乃沙。
第二次數(shù)據(jù)回滾
PM又又又提出批量更新數(shù)據(jù)的需求了,不過這次筆者信心滿滿诗舰,畢竟這次需求和第一次需求幾乎一樣警儒,唯一的區(qū)別是PM指定部分?jǐn)?shù)據(jù)不需要更新(這部分PM給到的數(shù)據(jù)是有問題的,所以不更新)眶根。
但是人怎么可能不犯錯(cuò)呢蜀铲,筆者忘記了部分?jǐn)?shù)據(jù)不需要更新這個(gè)點(diǎn),最后正確的和不正確的數(shù)據(jù)都更新至線上属百。萬萬沒想到记劝,經(jīng)歷了第一次數(shù)據(jù)回滾之后還能遭遇第二次數(shù)據(jù)回滾,筆者心態(tài)是真的崩了族扰。
事情已經(jīng)發(fā)生厌丑,筆者也只能想辦法解決了,下面是筆者基于實(shí)際業(yè)務(wù)場(chǎng)景想到的兩個(gè)數(shù)據(jù)恢復(fù)方案:
方案一:
1别伏、先通過數(shù)據(jù)ID確認(rèn)哪些數(shù)據(jù)需要修復(fù)(筆者在執(zhí)行腳本時(shí)記錄了數(shù)據(jù)ID的log日志)蹄衷。
2、解析備份SQL中需要恢復(fù)的數(shù)據(jù)并拼接為新的恢復(fù)SQL厘肮。
3愧口、調(diào)用服務(wù)刪除新增的數(shù)據(jù)(數(shù)據(jù)更新接口在修改數(shù)據(jù)的同時(shí)會(huì)新增其他關(guān)聯(lián)表的數(shù)據(jù))。
4类茂、執(zhí)行步驟2中生成的SQL恢復(fù)數(shù)據(jù)耍属。
方案二:
尋求DBA大爺?shù)膸椭謴?fù)數(shù)據(jù)。
方案一可以自己恢復(fù)數(shù)據(jù)巩检,而且正確的數(shù)據(jù)會(huì)保留厚骗,但操作麻煩且恢復(fù)過程可能產(chǎn)生新的問題,所以最后還是厚顏無恥地去找DBA恢復(fù)數(shù)據(jù)兢哭。
DBA恢復(fù)數(shù)據(jù)后還給筆者發(fā)了下面恢復(fù)線上數(shù)據(jù)的SQL:
alter table table_a rename to table_a_bk_2;
alter table table_a_bk rename to table_a;
好家伙领舰,DBA暗示已經(jīng)這么明顯了嘛,筆者二話不說默默地發(fā)了一封郵件準(zhǔn)備申請(qǐng)一個(gè)具有DDL權(quán)限的賬號(hào)迟螺。筆者現(xiàn)在想的十分清楚冲秽,以后再有這種批量更新線上數(shù)據(jù)的操作一定好好全表備份數(shù)據(jù)而不是使用僅有讀權(quán)限的賬號(hào)備份replace into
語句。
-- 全表備份sql語句
CREATE TABLE table_a_bk AS SELECT * FROM table_a;
總結(jié)
下面是這兩次事故發(fā)生后筆者的一些心得矩父,希望可以給大家提供參考锉桑。
1、代碼邏輯要清楚窍株,函數(shù)參數(shù)命名要語義清晰民轴。一個(gè)參數(shù)就包含了所有需要的數(shù)據(jù)是十分不正確的行為同時(shí)代碼中盡可能多些注釋攻柠。
2、對(duì)線上數(shù)據(jù)充滿敬畏后裸,操作數(shù)據(jù)時(shí)要理清楚業(yè)務(wù)邏輯瑰钮。
3、準(zhǔn)備操作線上數(shù)據(jù)前微驶,盡量先在無緩存環(huán)境下進(jìn)行數(shù)據(jù)預(yù)驗(yàn)證飞涂。
4、人都有可能會(huì)犯錯(cuò)祈搜,所以還需要QA進(jìn)行雙重保證较店。
5、筆者就是吃了緊急需求的虧才導(dǎo)致這兩次事故容燕,其他情況請(qǐng)務(wù)必按照正常流程進(jìn)行數(shù)據(jù)操作梁呈。
6、備份真的很重要蘸秘!這兩次事故后筆者認(rèn)為前文提到的全表數(shù)據(jù)備份方案相對(duì)合理且易恢復(fù)官卡。
最后,衷心希望本文能夠?qū)Ω魑蛔x者有一定的幫助