由 "select *" 引发的“惨案”

 

      今天凌晨做发布, 要合并多个分数据库的表数据到主数据库中, 有 30+ 分数据库。 前面都比较顺利, 在临近结束时,突然发现一个字段的值插入错误。 有一个表 T,字段分别为 (f1, f2, f3, gmt_create, gmt_modify, name) 。 假设分数据库为 a1, a2, ..., a30 , 主数据库为 A 。 合并的逻辑是: 从 a1, a2, ..., a30 取出对应的字段, 依次插入到 主数据库中。

        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:  这里会将 allTuples 分成 1000 个元组一片进行提交执行。 不过不影响此处的理解。

      那么, 这会有什么问题呢? 初看上去似乎没什么, 但是“惨案” 就这样发生了。 

      有一个集群 ai 的表 T 的字段顺序跟其他集群的略有不同。其表字段顺序是 (f1, f2, f3, name , gmt_create, gmt_modify) 。 这样从 ai 取出的数据 (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 均为字符串, 因此没有报错。 验证的时候, 由于另外一个地方因其他原因报了大量错误,掩盖了这个问题。

      解决的办法很简单: 将 "select * from T" 改为 "select f1, f2, f3, gmt_create , gmt_modify, name  from T"

      教训: 在做“逐字段取出-插入” 的数据库操作时, 切忌使用 “select * ”

 

       发现数据插入错误之后, 马上进行清空和重新执行。 这时, 更糟糕的事情发生了。 由于清空操作要考虑将对 T.A 的新改动(考虑到发布过程中会有外部调用修改T.A的数据)同步到 T.ai 。 而上述已经对 T.A 进行了大量改动,  因此会以 T.A 的数据为准, 对 T.ai 的相应记录进行回写, 结果将 ai 的原数据覆盖了, 且没有预先做表备份。 ai 的数据就这样丢失了!  

       教训:  合并过程中, 最好不要回写源数据库, 降低复杂性; 如果一定要回写源数据库, 要单独做一个脚本, 取名更明显, 且要做表备份操作。  

     

       现在必须马上恢复数据! 当时差点忘了, 由于 db.executeMany 接口没办法获取到直接执行的SQL, 因此昨天早上思虑再三,新添了一行代码使用了 log.info(allTuples) , 记录下了所要插入的源数据。 万一出问题, 避免从 DB 中取执行SQL的麻烦。  不过, 由于偷了一点懒, 打印出的 allTuples 的格式相当难以解析, 费了不少劲才将 <primarykey, name> 的关系取出来, 重新做了订正。 昨天早上的那行代码成了今天早上的救命稻草之一。

      教训:  对数据库的insert, update, delete 操作一定要加日志。 如果数据库接口不方便直接打印SQL的日志, 就要单独打印出源数据以备后用。 此外, 最好不要偷懒, 因为每一点偷懒都会对后面某个时候造成障碍, 而稍微做的便利一点, 就会对后续产生有益的用处。 这都是活生生的教训。 

 

作者:@琴水玉

转载请注明出处:https://www.cnblogs.com/lovesqcc/p/4195432.html

微信扫一扫下面的二维码,关注我的公众号 编程大观园 :)


 
posted @   琴水玉  阅读(385)  评论(0编辑  收藏  举报
编辑推荐:
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示