乐趣区

关于后端:线上数据被回滚两次我都做了哪些不正确的操作

来自公众号:新世界杂货铺

程序猿最大的悲痛是什么!

经验了这两次事变后,笔者感觉最大的悲痛莫过于中午打电话给 DBA 申请帮忙复原数据。程序猿和 PM 之间的战斗往往还有来有回,而笔者碰上 DBA 之后,那可真是求人办事,怎么怂怎么来,只有 DBA 大爷快乐!

为了当前尽量少跪舔 DBA 大爷,笔者将亲身经历的两次事变记录下来以揭示本人。

第一次数据回滚

PM 是需要的生产者,程序猿是需要的消费者,这二者就是典型的生产者与消费者模型。因而本次事变的根因还是 PM 提出了需要,故笔者认为只有 PM 不再提需要就不再有事变。

唉!快醒醒,别做梦了!

回到事变的自身,笔者先形容一下过后的背景。

PM 有大量的数据须要紧急更新到线上。这需要有多紧急呢?PM 要绕过 QA 验证,间接在线上先用大量数据进行测试,大量数据验证通过后就更新所有残余的数据。

联合笔者所在公司的业务场景,笔者依照以下步骤实现了本次数据更新。

1、将须要更新的数据应用 mysqldump 进行备份。

mysqldump --replace -f --single-transaction -t \
-h hostname -u user -P 3936 -p dbname tablename  \
--where="id in (1,2,3)"  > tablename.sql

2、开发一个脚本间接调用线上已有更新数据的接口(开发时笔者曾经在测试环境自测)。

3、在线上先更新大量数据,并更新批改数据局部的缓存,PM 对大量数据进行验证。

4、PM 确认该局部数据验证通过后,开始对残余数据进行线上更新操作。

初看下面的步骤如同没什么大问题,但理论后果却是狠狠地打了笔者的脸。上面,笔者就好好掰扯掰扯到底是哪些起因造成了本次事变。

1、更新接口 逻辑没有理分明,导致线上数据更新谬误。

该接口是一个比拟老的服务且相干文档少,笔者因为没有梳理分明所有逻辑,调用接口时局部数据参数传递有误,导致线上数据更新谬误。

2、更新 接口实现有问题,调用服务后,删除了关联表的数据,所以须要复原。

如果只是上述第一个问题,笔者本人备份的 replace into 语句就可实现数据的复原,但很显著问题不止于此。当事变产生后笔者开始对该服务逻辑进行二次梳理,发现此接口对主表的关联表也进行了更新而且更新逻辑为 先删除关联数据而后插入新的关联数据。只是如此倒也罢了,要害是该接口的实现者将所有申请参数作为一个关联数组并将此关联数组传递给所有函数。好家伙,各个具备不同业务性能的函数传递的参数都是一样的,这导致笔者第一次梳理逻辑时无奈齐全理分明各个业务函数实在须要的数据到底是什么。

关联数据被删除笔者也没有备份,最初只好跪舔 DBA 大大帮忙进行数据回滚。

正告⚠️:代码不清晰,程序猿泪两行!

3、未通过 QA 的保障,就间接在线上测试。

笔者本人尽管在测试环境进行了简略测试,然而程序猿的本职还是开发不能消耗过多的精力去实现 QA 的工作,而 PM 很显著也不够业余,这才在质量保证环节出了错并扩充了线上的谬误范畴。

4、测试时未在无缓存环境下进行验证。

初始,PM 对大量数据的验证后果是没有问题的,然而当所有数据更新实现后缓存曾经开始逐渐重建,数据有误和数据被删的问题就开始裸露了。这是因为笔者只更新了 PM 想要验证的数据的缓存,却没更新关联数据局部的缓存,因而只有等这部分缓存天然生效问题才逐步浮现。

后续

DBA 对数据进行回滚后,批量更新数据还得持续啊!狠心的 PM 愣是逼着笔者大半夜修好问题持续验证,惟一值得快乐的可能就是这次仅更新大量数据第二天持续更新残余数据。最初,笔者修好问题并胜利地更新齐全部数据。

第二次数据回滚

PM 又又又提出批量更新数据的需要了,不过这次笔者信念满满,毕竟这次需要和第一次需要简直一样,惟一的区别是 PM 指定局部数据不须要更新(这部分 PM 给到的数据是有问题的,所以不更新)。

然而人怎么可能不犯错呢,笔者遗记了局部数据不须要更新这个点,最初正确的和不正确的数据都更新至线上。万万没想到,经验了第一次数据回滚之后还能遭逢第二次数据回滚,笔者心态是真的崩了。

事件曾经产生,笔者也只能想方法解决了,上面是笔者基于理论业务场景想到的两个数据恢复计划:

计划一

1、先通过数据 ID 确认哪些数据须要修复(笔者在执行脚本时记录了数据 ID 的 log 日志)。

2、解析备份 SQL 中须要复原的数据并拼接为新的复原 SQL。

3、调用服务删除新增的数据(数据更新接口在批改数据的同时会新增其余关联表的数据)。

4、执行步骤 2 中生成的 SQL 复原数据。

计划二

寻求 DBA 大爷的帮忙复原数据。

计划一能够本人复原数据,而且正确的数据会保留,但操作麻烦且复原过程可能产生新的问题,所以最初还是死皮赖脸地去找 DBA 复原数据。

DBA 复原数据后还给笔者发了上面复原线上数据的 SQL:

alter table table_a rename to table_a_bk_2;
alter table table_a_bk rename to table_a;

好家伙,DBA 暗示曾经这么显著了嘛,笔者二话不说默默地发了一封邮件筹备申请一个具备 DDL 权限的账号。笔者当初想的非常分明,当前再有这种批量更新线上数据的操作肯定好好全表备份数据而不是应用仅有读权限的账号备份 replace into 语句。

-- 全表备份 sql 语句
CREATE TABLE table_a_bk AS SELECT * FROM table_a;

总结

上面是这两次事变产生后笔者的一些心得,心愿能够给大家提供参考。

1、代码逻辑要分明,函数参数命名要语义清晰。一个参数就蕴含了所有须要的数据是非常不正确的行为同时代码中尽可能多些正文。

2、对线上数据充斥敬畏,操作数据时要理分明业务逻辑。

3、筹备操作线上数据前,尽量先在无缓存环境下进行数据预验证。

4、人都有可能会犯错,所以还须要 QA 进行双重保障。

5、笔者就是吃了紧急需要的亏才导致这两次事变,其余状况请务必依照失常流程进行数据操作。

6、备份真的很重要!这两次事变后笔者认为前文提到的全表数据备份计划绝对正当且易复原。

最初,衷心希望本文可能对各位读者有肯定的帮忙

退出移动版