背景
大家好,我是逐日,1月份刚过去两周,但是这是非常忙碌的两周。这两周真的是一直在处理各种线上问题,上周周六还连带着公司一堆人搞了一天,深圳的架构师都连夜飞过来了,而这一系列事情的开端却是在元旦前两天上线了一个需求开启的。
另外,还有一周就过年了,年前本来想着再去骑一次车,结果因为昨天晚上部门聚餐,酒喝多了,今天起来还有点头疼;再加上成都降温、阳康也还不足一个月,想想还是在家里写文章吧,记录下最近这一连串的事故。
12月29号,周四,居家办公
很平常的一天,组内小伙伴的一个需求上线。我们的员工办公app里,之前只有分支机构的人可以利用它来移动考勤打卡,而总部的人员是使用不了的,使用不了的原因是一些字段有差异,所以这个需求的核心就是也要能支持总部人员移动打卡。
这个小伙伴比我早入职,只是年限低一点,所以我入职后,由我来带着他搞事情。在我入职前,他就开始开发这个需求了,搞了好一阵时间,测试也通过了,就准备趁着年底,上个线,给2022年记个工作成绩。
这个需求上线呢,说起来简单,实际上业务上的弯弯绕绕不少,还有些周边功能,听着就头疼那种,当时也在忙其他事情,想着这个都搞了这么长时间了,既然测试也过了,我也就没把这小伙伴拉过来具体聊聊这个需求到底怎么回事,技术方案怎么做的等等,那我也就在上线流程里给过了。
其实这个系统,还涉及一个下游系统,就是考勤数据要传给下游的考勤分析系统,那边会根据考勤流水计算考勤状态:员工是旷工还是迟到早退。数据怎么给下游呢,是通过数仓同步的方式,也就是把我们的考勤记录表,直接通过数仓同步到下游系统的库里。下游分析完了后,数仓还会把分析的考勤状态,再同步到我们这边的一个表A里。
app基本就是从表A里获取考勤状态:正常、旷工等。
这次升级涉及到三方:我方、下游的考勤分析系统、数仓组需要建立表之间的同步任务。
当天,我们上线的内容,包括了:建表、建视图;再上传程序jar包、改配置、重启。为啥还在用视图呢,因为是个老系统了,之前的分支机构打卡就是这么玩的,所以我们小伙伴就跟着这么弄了,看来,代码是有自己的惯性的。
另外,下游的考勤分析系统也同步更新了。
数仓同步的同步任务那边,暂时还没更新(这个暂时和这次问题无关,只是提一下),因为他们那边上线流程太慢了,暂时等不了他们了。
我们的升级包里,建视图的语句长啥样呢,大概如下(原语句过于复杂,做了简化):
代码语言:javascript复制CREATE OR REPLACE FORCE VIEW ATTENDANCE.V_KQ_EVENT AS
select t1.col1,t1.col2,t2.col1 from t1 left join t2 where condition1
union all
select t3.col1,t3.col2,t4.col1 from t3 left join t4 where condition2
union all
select xxxx(省略)
反正呢,当时升级包的内容,也看了,但没仔细想有什么风险,没把小伙伴拉过来了解需求/技术设计等等。
这天,上线后,我们试了下功能,没问题。
12月29日,周五,居家办公
早上在分支机构的群里,开始有人反馈,周四上下班打卡都是正常的,为啥考勤状态是:旷工。由于分支机构的同事的收入可能是和这个强相关的,所以他们反馈这类问题非常及时和热烈。
我们的产品同事、开发小伙伴、下游系统的开发,在群里开始讨论这个问题;我当时在忙别的,暂时没介入,也看不懂他们说的东西(这所有前文写的东西都是事后了解的)。
经过他们一上午的分析讨论后,大概知道了原因。
是因为,小伙伴的那个建视图的sql语句,会把线上的已经存在的视图V_KQ_EVENT给覆盖掉:
代码语言:javascript复制CREATE OR REPLACE FORCE VIEW ATTENDANCE.V_KQ_EVENT
为啥会覆盖呢,因为小伙伴以为这个视图没人在用了,这次出了问题才知道,分支机构人员的考勤状态原来是这么计算出来的,是绕了一大圈才计算出来的:
我方V_KQ_EVENT视图--》数仓同步到下游--》下游的KQ_EVENT表--》考勤分析程序分析KQ_EVENT表,考勤状态写入到他们的KQ_EVENT_ANALYSIS表。
下游KQ_EVENT_ANALYSIS --》 数仓同步到我方 --》 我方的表A。
这就他么的属于认知之外的bug了,开发人员自己都不知道,我怎么把控风险,拉过来问估计都没用,再说,我这次还没怎么把控。
覆盖的后果是啥,原来的视图,字段是key1,key2,key3,这次覆盖后,字段变成了key1,key2,key4.
数仓同步的时候,一查询视图,查出来就是key1,key2,key4,同步到下游系统时,就缺了key3,key3就是null,而下游分析系统是禁止key3为null的,就会导致下游插入报错,同步就失败了。
所以,周三一点多上线,上线后,视图一变,周三的数仓同步就失败了,失败了也没个告警,分析半天才知道是同步这里出了问题。
既然是改视图出问题的,那就改回去不就完了吗,不好意思,运维同事操作的时候,并没有备份原视图,咨询dba,也说原视图找不回来了;我们的小伙伴,之前也早就把开发、测试环境也全覆盖了。
于是,我们的小伙伴按照自己的想法,回忆了下之前的视图,然后在周五下班前,把这个视图“还原”回去了,然后电话跟我说:之前不知道还有这么一条路径,和下游沟通后才知道,现在问题解决了,就是要把视图还原,说给运维发了一份新的视图sql,只要执行了就好了;但是执行后,因为考勤分析只分析前一天的,所以,考勤分析程序在周六凌晨执行,只会分析周五的考勤状态,周四的没办法,所以周四的状态还是有问题。
当时他是说,总部人员的会有问题,但没说分支机构的也有问题,但我当时实在不了解这块需求,听着有点蒙,也没听出问题,居家办公沟通起来也有点不方便,于是,上线了这么一个“还原”了的视图后,大家一起开心地度过了一个三天的元旦。
1月3日,周二
元旦,好多公司都收到了通知,要求恢复现场办公。于是,开始现场办公。
按理说,周五都修复了视图了,应该就不会有问题了,除了周四那天。结果,早上群里依然反馈说,周四周五的考勤状态不对:旷工。
领导也在那个分支机构的群里,问这个怎么回事,于是,我正式介入排查。
下游系统的开发在深圳,交流也不太方便,扯了半天,我们开始对两边的数据。
下游那边,
代码语言:javascript复制26日:4439
27日:4455
28日:4492
29日:2104
30日:20
31日:31
1日:33
2日:34
我方这边,拿着那个小伙伴自己想出来的还原后的视图,数据很是对不上。
后边我就有疑问了,29,30先不说,按理说,30号恢复了视图,元旦三天的数据怎么都应该对得上啊。
我们就开始对元旦3天的数据,先让对方把元旦第一天31号的数据查出来,我们挨条对,发现数据还是对得上一些;但我们这边查出来其实是55条,后来才发现,下游那个31条,是加了过滤条件的,不加where的话,实质上确实有55条。
image-20230114125331514
那就这几天是对上了,所以认为就是节前两天有问题,需要修复;但是后面去了趟数仓那边,就想看看之前同步任务失败的详情,结果数仓同事说,只能看最近三天的,看不了节前的,超过三天了;于是看了下当天的,发现当天的同步任务还是报错,就很奇怪,我们就发现,这个“还原”后的视图,查出来假设1000行,但可能前200行的col3字段没问题,但201行的col3还是null,导致只插入了200条,报错了,下游就只收到200条。
于是我们开始检查这个视图,发现这个视图确实是有问题的,但是,到处都没有备份,几乎陷入绝境。下午的时候,产品同事说,他那边有一份,应该是之前用这个来梳理业务逻辑时候存下来的。
万幸,于是我们拿这个正确的视图sql找运维上线,同时,用这个sql,改了下参数,写了29号、30号的数据的视图;然后找数仓同事,让数仓那边建了个临时任务,单独同步29/30号的视图到下游,于是,这个数据就算是丢给下游了。
到了下午5点,我正在微信里准备给领导汇报情况,还没写完,领导就在群里说话了,意思是这个问题今天必须搞定。然后我说了下目前进度:
- 29/30号的分支机构人员的考勤数据,到了下游了,下游那边要晚上定时同步过去,明天早上就能看到修复后的考勤状态数据。
- 总部人员的状态异常问题,要等数仓那边帮我们上新的同步任务(没错,我们这边和下游,在节前就上了,这边数仓的同步任务还没给我们搞),然后数仓同事说今天搞不了,要次日,那今天的数据就没法同步过去,今天的考勤状态肯定是不对的,这个是预期内会出现的问题(预期内的问题,肯定得先给领导说清楚)
然后,领导就直接叫我去办公室咨询这块事情了,因为这个加上元旦,给人感觉拖了好多天了,必须尽快解决,于是,马上给数仓打电话,要求今晚必须把新的数仓同步任务搞上去;然后叫了一堆负责人(数仓、运维、dba等)到办公室,专门了解这个问题,最终,就是专门一个微信群,晚上相关人员加班处理这个事情。
针对我汇报的两个事情,一个是分支机构的考勤问题,一个是总部的。
针对分支机构问题,主要是要求下游不要等定时任务,先手动执行,进行验证,确保次日万无一失;
针对总部人员问题,主要是要求数仓加班开发新的同步任务,结果开发的时候又发现,上下游的字段类型对不上(比如我们的时间字段是datetime,下游是字符串,这种时候,数仓就会没办法对应),最终是让下游改,下游拉着dba一顿操作,改好字段类型后,数仓同步任务建好,执行同步任务把总部人员的考勤数据同步到下游,下游马上手动执行分析程序。
当天晚上,结果下游太矬了,折腾到11点多,手动执行开始和自动任务有冲突,导致数据库锁表等各种问题,于是最终还是等着自动程序来执行。
1月4日,周三
经群里反馈,分支机构的问题基本解决了;但总部人员的29/30号的依然是旷工状态。经过下游分析发现,是1月3号用正确的视图生成的数据里,某个字段又不对了。
为啥会不对呢,因为这个视图,依赖某个表。这个表,也是另一个系统利用数仓同步给我们的,这个表,在2023年1月1号,做了变更,比如,字段a,本来存的数据是中文名字,过了1月1日后,存的数据变成了英文名。
导致我们在1月3号,利用这个表跑出来的历史数据(29和30日)又不对了,用这个数据给了下游后,下游的分析程序处理不了,导致总部人员的29/30号的考勤状态依然不正确。
于是,这天就变成了下游想办法来基于我们错误的数据来做处理(因为我们已经没办法拿到正确数据了,那个依赖的表已经完全变了),又是精彩的一天。
但这个问题,也只是下游系统忙活为主了,1月5号,这个考勤状态问题基本又都正常了;但是,这只是开始,1月5号,将迎来更大的问题。
经验教训总结
线上的东西,要加就加,要改就改,不要搞这种创建或更新的语句,尤其是还是force强制的
代码语言:javascript复制CREATE OR REPLACE FORCE VIEW ATTENDANCE.V_KQ_EVENT
不要用视图
线上东西,不清楚的时候不要随便动,尽量采用新增的方式
上线前还是需要好好评估,每个人都有自己的认知范围,认知范围之外的东西很难考虑到;而其他人参与评估的话,可能由于经历过类似事情,就能把可能的风险点找出来,大家一起评估
不要在年底或者月底之类的特殊时刻做线上变更,比如本次就是1月1日,视图依赖的表就发生了变化
尽量不要在节假日前变更,出了问题会导致解决时间延长
上线前,做好备份,不要靠别人给你备份,万一就像这次一样,运维同事也忘了备份呢(毕竟我们也没在上线文档里写需要备份),最终受影响的不还是自己吗?