悲惨!刚入职没几天,无意间把数据库删了,很尴尬,原因很奇葩
1. offer 收割机,就职新公司
5 年前的就业环境非常好,当时面试了很多家公司,收到了很多 offer。最终我决定入职一家互联网教育公司,新的公司福利非常好,各种零食随便吃,据说还能正点下班,一切都超出我的期望,“可算让我找着神仙公司了”,我的心里一阵窃喜。
在熟悉环境之后,我趁着上厕所的时候,顺便去旁边的零食摊挑了点零食。接下来的一天里,我专注地配置开发环境、阅读新人文档,当然我也不忘兼顾手边的零食。
入职几天后 ,领导给安排了一个小需求,我和同事沟通完技术方案后,就开始开发了。
2. 单元测试有点奇怪
完成开发后,我决定写个单元测试验证下,在研究单元测试代码后,我发现这种单测写法和我之前的写法不太一样。
这家公司的单测好像没有启动整个项目,仅加载了部分类,而且不能访问测试环境数据库~ 于是我决定按照前东家写单测的方式重新写单元测试。
于是我新增了一个单测基类,在单测中启动整个 SpringBoot,直接访问测试环境数据库。然而也并不是很顺利,启动阶段总是会遇到各种异常报错,需要一个一个排查…… 所幸项目排期不紧张,还有充足时间。
我做梦也没有想到,此刻,已经铸成大错。
3. 故障现场
我身边的工位旁慢慢地聚集了越来越多的人,本来我还在安安静静的调试单元测试,注意力不自觉的被吸引了过去。
“测试环境为什么这么多异常,访问不通啊。到处都是 500 报错”,不知道谁在说话。
“嗯,我们还在排查,稍等一下”,我旁边的同事一边认真排查日志,一边轻声回复道。
“为什么数据库报的异常是, 查不到数据呢?” ,同事在小声嘀咕,然后打开 命令行,立即登上 MySQL。
我亲眼看着他在操作,奇怪的是数据库表里的数据全部被删掉了,其他的几个表数据也都被删除了。
简直太奇怪了,此刻的我还处于吃瓜心态。
有一个瞬间我在考虑,是否和我执行的单元测试有关系? 但我很快就否决掉了这个想法,因为我只是在调试单元测试,我没有删数据库啊,单测里也不可能删库啊。 我还在笑话自己 胡思乱想……
很快 DBA 就抱着电脑过来,指着电脑说,你们看这些日志,确实有人把这些表删除了。
"有 IP 吗,定位下是谁删除的, 另外线上环境有问题吗?”,旁边的大组长过来和 DBA 说。
“嗯,我找到 ip 了,我找运维看下,这个 ip 是谁的”。DBA 回复道。
4. 庭审现场
当 DBA 找到我的时候,我感到无辜和无助,我懵逼了,我寻思我啥也没干啊,我怎么可能删库呢。 (他们知道我刚入职,我现在怀疑:那一刻他们可能会怀疑 我是友商派过来的卧底、间谍,执行删库的秘密任务)
经过一系列的掰扯和分析,最终定位 确实是我新增的单元测试把数据库删了。
5. 故障原因
需要明确的是,原单元测试执行时不会删除数据库;测试环境启动时也不会删除数据库。
只要在单元测试中连接测试数据库,就会删除掉数据库的所有数据。为什么呢?
5.1 为什么单元测试删除了所有数据?
原单元测试 使用的是 H2 内存数据库,即 Java 开发的嵌入式(内存级别)数据库,它本身只是一个类库,也就是只有一个 jar 文件,可以直接嵌入到项目中。H2 数据库又被称为内存数据库,因为它支持在内存中创建数据库和表。所以如果我们使用 H2 数据库的内存模式,那么我们创建的数据库和表都只是保存在内存中,一旦应用重启,那么内存中的数据库和表就不存在了。 所以非常适合用来做单元测试。
H2 数据库在启动阶段,需要执行用户指定的 SQL 脚本,脚本中一般包含表创建语句,用来构建需要使用的表。
但是我司的 SQL 脚本除了创建表语句,还包含了删除表语句。即在创建表之前先删除表。 为什么呢? 据他们说,是因为这个 SQL 脚本可能会重复执行,当重复执行时创建表语句 会报错。所以他们在创建表之前,先尝试删除表。这样确保 SQL 脚本可重复执行。( 其实可以用 Create if not exists )
故障的原因就是:测试数据库执行了这个删表再建表的 SQL 脚本,导致所有数据都被清除了。
5.2 为什么测试数据库会执行这条 SQL 脚本呢?
1) 我新建的单元测试把 H2 内存数据库换成了测试数据库。
2) spring.data.initialize=默认值为 true; 默认情况下,会自动执行 sql 脚本。
所以测试数据库 执行了 SQL 脚本。
5.3 为什么在测试环境正常启动时,没有问题,不会删除所有数据呢?
只有单测引入测试数据库才会出问题,在测试环境正常启动项目是没问题的。
当编译项目时,测试目录下的文件、代码和正式代码编译后的结果不会放到一起。因为 SQL 脚本被放在了 测试目录下, 所以正式代码在测试环境启动时,不会执行到这个 SQL 脚本,自然不会有问题。
6. 深刻教训
最终数据被修复了,DBA 有测试数据库的备份,然而快照并非实时的,不可避免地还是丢失了一部分数据。
所幸的是出问题的是测试环境,并非线上环境。 否则,我会不会被起诉,也未可知。
后续的改进措施包括
收回了数据库账户的部分权限,只有管理账户才可以修改数据库表结构。代码中执行 DML 语句的账户不允许执行 DDL 语句。
DBA 盘点测试数据库的快照能力,确保快照间隔足够短,另外新增一个调研课题:删库后如何快速恢复,参照下其他公司的方案。
所有的项目 spring.data.initialize 全部声明为 false。不自动执行 SQL 脚本
SQL 脚本一律不许出现 删除表的语句。SQL 不能重复执行的问题,想其他办法解决。
另外的一个项目急需人手,把新来的那谁 调到其他项目上
这可能是程序员们在技术上越来越保守的原因……不经意的一个调整可能引发无法承受的滔天巨浪
版权声明: 本文为 InfoQ 作者【五阳】的原创文章。
原文链接:【http://xie.infoq.cn/article/4d07c58848f9459219cb293b8】。未经作者许可,禁止转载。
评论