周六,我删除了公司数据库。

科技   2024-05-28 22:12   上海  
将 "数据与人" 设为 "星标⭐"
第一时间收到文章更新


这本是一个安静的星期六。

突然间,我收到了来自支持团队的一条信息,说我们有一个客户遇到了棘手的问题。看了下问题,我意识到这个问题的重要性,认为有必要立即着手进行调试。

经过15分钟的仔细排查,我确认了问题的根源——数据库中存在若干损坏的订单,这些订单需要被及时删除。

听起来似乎并不是什么大难题。

事故还原

如果你不给创业公司打工,请不要嘲笑我 😅
有几百个订单需要删除,所以我决定不手动操作,而是编写一个简单的 SQL 查询语句(警告 🚩)
实际上比这复杂一些,但这里简化一下:
UPDATE ordersSET is_deleted = true
WHERE id in (1, 2, 3)
你大概已经猜到这场灾难的规模了...
我按下了CTRL + ENTER键,并执行了那条命令。当操作耗时超过一秒钟时,我意识到了事态的严重性。
我的客户端 DBeaver 看到空的第三行,并忽略了第四行。
是的,我删除了数据库中所有的订单 😢
我整个人都不好了。

恢复

深呼一口气之后,我意识到自己必须迅速行动。不能再因为犯错而白白浪费时间了。

恢复工作的完成情况很好。

  1. 停止系统 - 约 5 分钟

  2. 创建变更前数据库(幸运的是我们有 PITR)的克隆 - 约 20 分钟

  3. 在等待期间给我的老板打电话 😨

  4. 根据克隆更新生产数据库的信息* - 约 15 分钟

  5. 启动系统 - 约 5 分钟

我决定不将整个数据库恢复到原始状态,因为我们的系统有多个独立部分,无法全部停止。我不想在恢复过程中丢失任何已经做出的修改。

我们使用的是GCP提供的托管PostgreSQL服务,所以我创建了一个新的克隆,这个克隆是在更新之前的数据库状态。然后,我只导出了克隆中的id和is_deleted两列的数据,并将这些数据导入到生产数据库中。之后,我只需要使用简单的update和select语句就能完成更新操作。

所以,很明显,这45分钟的停机时间其实是可以避免的。

发生了什么

这可能听起来像是一个你永远不会犯的愚蠢错误(甚至在大公司中,根本不能犯)。
没错,问题的根源并不在于那条错误的SQL语句本身。实际上,那些小小的、看似无足轻重的人为失误从来都不是问题的本质。我执行那个命令只是最终导致失败的一系列事件的最后一环。

为什么要选择在周末这样的非工作时间去处理生产环境的问题呢?从实际情况来看,这次的事情其实并没有那么紧急到需要立即解决。也没有人明确要求我必须马上修复它。我完全可以选择等到星期一再进行处理。

又有谁会愚蠢到直接在生产数据库上做出更改,而不先在测试环境进行测试呢?

我为什么会选择直接手动编辑数据库,而不是通过调用API这种更安全、更可控的方式来进行操作呢?

退一步讲,就算没有API可用,那我为什么没有先打电话给队友,在这么敏感的操作上进行双重确认和检查呢?

最要命的是,我为什么没有使用事务管理来确保操作的原子性?其实只要简单地使用Begin(开始事务)命令,万一出现错误时就可以使用Rollback(回滚事务)来撤销之前的更改,从而避免更大的损失。

这些错误像一层层叠加的积木,只要避免了其中任何一个,整个事情就不会发生。其实大多数问题的答案都很简单:我太过于自信,以至于忽略了应有的谨慎和防范。

不过还好,我们有一套完善的恢复程序,最终阻止了可能发生的连锁反应。试想一下,如果无法将数据库恢复到正确的状态,那将会带来怎样的灾难性后果。

这与切尔诺贝利有什么关系

数月前,我翻阅了《切尔诺贝利:一部悲剧史》一书。

书中叙述的一系列失误让我不禁联想起了那个我至今仍无法忘怀的周末(我并没有打算将那次事件与切尔诺贝利灾难相提并论,只是两者在某种程度上的确有所相似)。

RBMK反应堆本身就存在着严重的技术隐患。

然而,这个问题并没有得到应有的重视和妥善处理。在此之前,其实已有一些相关的事故发生,但切尔诺贝利团队的成员对此却并不十分了解。

在进行安全检查时,团队成员并没有严格按照既定的程序进行操作。

事故发生后,苏联政府试图掩盖真相,结果反而使得损害的程度进一步加剧。

那么,究竟谁应该为这场灾难负责呢?

是反应堆的设计师吗?还是那些未能及时将他们所遇到的问题传达给切尔诺贝利团队的电厂团队?抑或是切尔诺贝利团队的成员自身?还是那试图掩盖真相的苏联政府?

实际上,每个人都应该为此承担一定的责任。因为灾难往往并非由单一的一个错误引发,而是一系列错误相互交织、相互影响的结果。我们的任务就是要尽可能早地发现并打断这个错误的链条,尽我们所能做到最好。

后续

对于周一与老板的对话,我原本并没有抱太大的期望。

然而他却给了我一个意外的回应:“确保这种情况不再发生。但我更欣赏的是——你之所以犯错,是因为你专注并且热衷于迅速行动。尝试得越多,当然也会有更多的失败。”

这正是我想听到的。如果他以那种过于“宽容”的语气说:“没关系,别放在心上,谢谢你解决了问题!”我反而会觉得那是虚伪的安慰。另一方面,我已经感到非常懊悔了,所以也不需要再对我进行过多的批评。

自那之后:

我们减少了对数据库的直接访问需求,并且开始建立相关的API。

我始终会在测试环境中先运行查询(这不是很明显吗?毕竟没有什么比亲身经历过的灾难更能让人吸取教训了)。

我与产品经理进行了深入的沟通,以便更清楚地了解哪些事项是真正紧急的,哪些可以稍后再处理。

任何涉及生产环境的增删改操作,现在都需要两个人共同完成。这样的做法实际上有效地防止了其他错误的发生!

我也开始使用事务处理机制来确保操作的原子性。

经验教训

事情发生后,我毫无保留地与团队分享了整个事件的经过,既没有隐瞒任何细节,也没有试图掩盖我在其中的过失。

在批评他人与选择宽容之间,存在着一种难以捉摸的均衡点。

当你犯下错误时,这恰恰是一个传递正确信息、传递正确价值观的绝佳机会。

如果你反复道歉上千次,他们可能会认为,当类似的事情发生在他们身上时,你也期望他们做出同样的回应。

如果你只是轻描淡写地一笑而过,完全不重视其可能带来的负面影响,他们可能会觉得这样的行为是可以被接受的。

相反,如果你勇于承担责任,从错误中学习并努力提升自己,他们也会受到你的影响,以同样的方式去面对和解决问题。



更多精彩内容,关注我们▼▼

数据与人
聚焦技术和人文,分享干货,共同成长。
 最新文章