由 select * 引发的“惨案”
?
? ? ??今天凌晨做發(fā)布, 要合并多個(gè)分?jǐn)?shù)據(jù)庫的表數(shù)據(jù)到主數(shù)據(jù)庫中, 有 30+ 分?jǐn)?shù)據(jù)庫。 前面都比較順利, 在臨近結(jié)束時(shí),突然發(fā)現(xiàn)一個(gè)字段的值插入錯(cuò)誤。 有一個(gè)表 T,字段分別為 (f1, f2, f3, gmt_create, gmt_modify, name) 。 假設(shè)分?jǐn)?shù)據(jù)庫為 a1, a2, ..., a30 , 主數(shù)據(jù)庫為 A 。 合并的邏輯是: 從 a1, a2, ..., a30 取出對(duì)應(yīng)的字段, 依次插入到 主數(shù)據(jù)庫中。
aRet = adb.query("select * from T")allTuples = []for (f1, f2, f3, gmt_create , gmt_modify, name) in aRet:allTuples.append((f1, f2, f3, gmt_create, gmt_modify, name))Adb.executeMany("insert into T(f1, f2, f3, gmt_create, gmt_modify, name) values(%s, %s, %s, %s, %s, %s)", allTuples)log.info(allTuples)Adb.commit()
? ? ? NOTE: ?這里會(huì)將 allTuples 分成 1000 個(gè)元組一片進(jìn)行提交執(zhí)行。 不過不影響此處的理解。
? ? ? 那么, 這會(huì)有什么問題呢? 初看上去似乎沒什么, 但是“慘案” 就這樣發(fā)生了。?
? ? ? 有一個(gè)集群 ai 的表 T 的字段順序跟其他集群的略有不同。其表字段順序是 (f1, f2, f3, name , gmt_create, gmt_modify) 。 這樣從 ai 取出的數(shù)據(jù) (f1, f2, f3, name, gmt_create, gmt_modify) 將插入到 A 的 (f1, f2, f3, gmt_create, gmt_modify, name) , 也就是說, a1.name 插入到 A.gmt_create, ?a1.gmt_create 插入到 A.gmt_modify, ?a1.gmt_modify 插入了 A.name ?, ? 由于 gmt_create , ?gmt_modify, name 均為字符串, 因此沒有報(bào)錯(cuò)。 驗(yàn)證的時(shí)候, 由于另外一個(gè)地方因其他原因報(bào)了大量錯(cuò)誤,掩蓋了這個(gè)問題。
? ? ? 解決的辦法很簡單: 將 "select * from T" 改為 "select?f1, f2, f3, gmt_create , gmt_modify, name ?from T"
? ? ? 教訓(xùn): 在做“逐字段取出-插入” 的數(shù)據(jù)庫操作時(shí), 切忌使用 “select * ”
?
? ? ? ?發(fā)現(xiàn)數(shù)據(jù)插入錯(cuò)誤之后, 馬上進(jìn)行清空和重新執(zhí)行。 這時(shí), 更糟糕的事情發(fā)生了。 由于清空操作要考慮將對(duì) T.A 的新改動(dòng)(考慮到發(fā)布過程中會(huì)有外部調(diào)用修改T.A的數(shù)據(jù))同步到 T.ai 。 而上述已經(jīng)對(duì) T.A 進(jìn)行了大量改動(dòng), ?因此會(huì)以 T.A 的數(shù)據(jù)為準(zhǔn), 對(duì) T.ai 的相應(yīng)記錄進(jìn)行回寫, 結(jié)果將 ai 的原數(shù)據(jù)覆蓋了, 且沒有預(yù)先做表備份。 ai 的數(shù)據(jù)就這樣丟失了! ?
? ? ? ?教訓(xùn): ?合并過程中, 最好不要回寫源數(shù)據(jù)庫, 降低復(fù)雜性; 如果一定要回寫源數(shù)據(jù)庫, 要單獨(dú)做一個(gè)腳本, 取名更明顯, 且要做表備份操作。 ?
? ? ?
? ? ? ?現(xiàn)在必須馬上恢復(fù)數(shù)據(jù)! 當(dāng)時(shí)差點(diǎn)忘了, 由于?db.executeMany 接口沒辦法獲取到直接執(zhí)行的SQL, 因此昨天早上思慮再三,新添了一行代碼使用了 log.info(allTuples) , 記錄下了所要插入的源數(shù)據(jù)。 萬一出問題, 避免從 DB 中取執(zhí)行SQL的麻煩。 ?不過, 由于偷了一點(diǎn)懶, 打印出的 allTuples 的格式相當(dāng)難以解析, 費(fèi)了不少勁才將 <primarykey, name> 的關(guān)系取出來, 重新做了訂正。 昨天早上的那行代碼成了今天早上的救命稻草之一。
? ? ? 教訓(xùn): ?對(duì)數(shù)據(jù)庫的insert, update, delete 操作一定要加日志。 如果數(shù)據(jù)庫接口不方便直接打印SQL的日志, 就要單獨(dú)打印出源數(shù)據(jù)以備后用。 此外, 最好不要偷懶, 因?yàn)槊恳稽c(diǎn)偷懶都會(huì)對(duì)后面某個(gè)時(shí)候造成障礙, 而稍微做的便利一點(diǎn), 就會(huì)對(duì)后續(xù)產(chǎn)生有益的用處。 這都是活生生的教訓(xùn)。?
?
轉(zhuǎn)載于:https://www.cnblogs.com/lovesqcc/p/4195432.html
與50位技術(shù)專家面對(duì)面20年技術(shù)見證,附贈(zèng)技術(shù)全景圖總結(jié)
以上是生活随笔為你收集整理的由 select * 引发的“惨案”的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 深入浅出设计模式——组合模式(Compo
- 下一篇: 初学ctypes:打开进程并返回相关信息