由 "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的日志, 就要单独打印出源数据以备后用。 此外, 最好不要偷懒, 因为每一点偷懒都会对后面某个时候造成障碍, 而稍微做的便利一点, 就会对后续产生有益的用处。 这都是活生生的教训。
由 "select *" 引发的“惨案”的更多相关文章
- try{s.send(t.hasContent&&t.data||null)}catch(e){if(n)throw e}}引发的惨案
如题,ajax请求报错:try{s.send(t.hasContent&&t.data||null)}catch(e){if(n)throw e}}引发的惨案 要么是404,要么是40 ...
- 记一次真实的线上事故:一个update引发的惨案!
目录 前言 项目背景介绍 要命的update 结语 前言 从事互联网开发这几年,参与了许多项目的架构分析,数据库设计,改过的bug不计其数,写过的sql数以万计,从未出现重大纰漏,但常在河边走,哪 ...
- 视图合并、hash join连接列数据分布不均匀引发的惨案
表大小 SQL> select count(*) from agent.TB_AGENT_INFO; COUNT(*) ---------- 1751 SQL> select count( ...
- 一个purge参数引发的惨案——从线上hbase数据被删事故说起
在写这篇blog前,我的心情久久不能平静,虽然明白运维工作如履薄冰,但没有料到这么一个细小的疏漏会带来如此严重的灾难.这是一起其他公司误用puppet参数引发的事故,而且这个参数我也曾被“坑过”. ...
- 一次dns缓存引发的惨案
时间2015年的某个周六凌晨5点,公司官方的QQ群有用户反馈官网打不开了,但有的用户反馈可以打开,客服爬起来自己用电脑试了一下没有问题,就给客户反馈说,可能是自己网络的问题,请过会在试试.早点8点,越 ...
- 由select引发的思考
一.前言 网络编程里一个经典的问题,selec,poll和epoll的区别?这个问题刚学习编程时就接触了,当时看了材料很不明白,许多概念和思想没有体会,现在在这个阶段,再重新回头看这个问题,有一种豁然 ...
- ogg:Extract 进程遇长事务执行 Forcestop 引发的惨案
http://www.linuxidc.com/Linux/2015-04/115777.htm SQL> select t.addr,t.START_DATE from v$transacti ...
- kafka的maxPollIntervalMs设置太小引发的惨案 (转)
本地启动kafka后,不断报一下信息: 表示本地consumer节点在不断的重新加入group,并且不断伴随着offset commit失败. 具体原因是因为ConsumerCoordinator没有 ...
- MySQL left join 引发的惨案
当我用这个进行更改值时,type未控制order表 其他数据被更改 还好备份数据表了(这里就体现了备份的重要性) UPDATE expense_order as a left join ( SELEC ...
随机推荐
- js - 驼峰命名
1. // 驼峰命名 console.log(hump('border-bottom-color')) function hump( str) { if (typeof str != 'string' ...
- 深入css中的margin
深入css中的margin 第一:margin-top css代码(元素没有任何定位的情况下,并且元素默认为block) <style type="text/css"> ...
- Android Studio IDE 主题设置
1.界面主题设置,如下图: 2.代码字体设置,如下图:
- Android --ListView分页
参考博客:Android ListView分页加载(服务端+android端)Demo 监听OnScrollListener事件 class OnListScrollListener implemen ...
- 开发API文档相关问题(*.chm)
在网上下了一个chm的文件,打开后只有目录,右侧不显示内容. 不知道是文件有问题,还是系统有问题. google了一下.解决了. 右键点击文件–属性 看到 最下面有一个提示 说是这个文件是来自其他计算 ...
- D3D标注动态避让
原来也思考了该如何实现标注动态避让,认为必须得计算字符串所占的大小.如果在屏幕上要计算屏幕象素之类的东西. 今天总算找到了实现方法,在C# WorldWind中的KMLImporter类中有.关键是F ...
- nagios监控linux主机监控内存脚本
说明 工作包括两部分监控端(一二三)和被监控端(四) 一.nrpe.cfg中添加脚本 nrpe.cfg中添加命令索引 command[check_used_mem]=/usr/local/nagios ...
- Hello,HTML 到 HTML5
HTML5是WEB应用将发展到一个水平必要的技术. 下面是WEB发展的时间轴: 1991 - HTML 1994 - HTML2 1996 - CSS1 + JAVASCRIPT 1997 - HTM ...
- 更改Mysql数据库中的数据出现乱码问题
数据库服务器环境:windows 7 专业版 Mysql版本:5.5.36 出现问题:搭完工程之后,在做保存和插入操作时,涉及的数据在数据库中为变为乱码. 解决方案: MySQL数据库 ...
- 数据库执行监控,除了Profiler的方案
怎么查找库中的数据库相关信息: Select * from sys.databases; 返回字段: name(数据库名称) database_id(数据库id) .... Select sqlTex ...