数据出问题了!
一次偶发的端上问题。
排查日志、监控、数据、代码逻辑。
服务没有添加事务性保障,不可避免的数据不一致:缓存有数据,数据库没数据或者相反;有A阶段数据,没有B阶段数据;该有的数据没有,不该有的数据却存在。
主从机制遇到了强一致性需求,偶发的缓存不一致。
服务被埋下了错误的逻辑,日积月累全量的错误数据。
... ...
不管是哪种情景,总之是数据变脏了。
怎么办?
一、处理当前问题的数据
对的,不是所有问题数据,是当前问题的数据,我们通常称这种为紧急的问题,重不重要分情景另说。
用户的问题诉求,总是需要第一时间响应的,这个关系大公司产品的使用体验及公司形象,不容推延。
当然,在此之前,还是需要保留现场的,这些现场就是你排查的经过,每一步印证问题所提取的信息。
经验需要积累,教训也要留存。
二、修复引发问题的诱因
已经排查了问题的所在,也已经处理了个例的问题。那么接下来需要做的就是去修正服务的问题。
1、事务性保障问题
首先确定问题点是不是关键核心模块,是不是可以容忍。
类如,交易系统,订单、库存、账户余额等关联,是一点错乱都无法忍受的,这就要从全方位进行数据一致性、正确性的保障。
服务逻辑层面的接口幂等性,业务操作的事务性,补偿、重试保障可靠性;消息中间件层面的或涉及消息的持久化、镜像容错等;数据存储层面的分布式缓存、数据库主从等,不一例举。
类如,阅读数,点赞数此等,很少人会去关注具体的多一个或者少一个(较真儿的除外),某大V写了篇文章,瞬间阅读数就查过了几万,转发上千,大V不会去关心本该有30001的阅读数只有30000。
此类数据,或者求个最终一致性,或者偶尔的遗失数据也可以忽略。也就不必要去添加额外的耗损性能的事务性保障。
2、主从与强一致性
主从与强一致性就像两个同极的磁铁,通常都是无法共存的。
以前遇到过有同事在提到“刚写进去又查出来”总会发笑,那种认为可笑的发笑。
而往往此时,我没有笑,只有疑惑和思考。疑惑是笑的同事,为什么会笑;思考是,是不是只有这样才能达到目的,有没有更好的替代方式。
通常现实中的业务都是大业务套着小业务,小业务套着更小的业务,环环相扣,一环连着一环。也如,一个人背着一打木板过河,放下一个踩上去,然后再放下下一个,有二必先有一。
所以,固化前一个变更,然后进行下一个变更,这,很正常。
然而,随着信息时代的爆炸式发展,信息服务的载重和载压一年便是一个量级,这就不断演化了现如今的许多多般复杂信息服务。而在这其中,便是这“主从”应运而生,横亘至今。
主从是一种分治的思想,读场景和写场景,量级不同,处理逻辑不同,保障性不同,所以涉及数据层面,就可以采用不同的提供方式。
缓存或者数据库,从主节点分离出一个或多个从节点专门用于数据的查询,从节点实时同步主节点的数据变更,数据会有一定的延迟,但是基本符合现实的应用场景,我们追求的是不那么强的最终一致性。
主节点则主要负责对数据变更的处理,以及,必要的数据查询,对于需要强一致性的场景。
在实际的应用开发中,要特别注意筛选出对数据延迟零容忍的的业务逻辑,结合实际,应用主节点进行数据查询或者其它手段来保障数据的强一致性需求。
3.避免不必要的低级逻辑错误
这里,我们强调的是因为疏忽,非因个人能力而导致逻辑错误。
以前读过卡耐基的人性的弱点,人是情感的生物,一生都在与自己的弱点作斗争。人类自大,容易烦躁,又缺乏耐心。而这,往往会导致一些不必要的的失误。
属性赋值错了,未赋值;空值未判断; 特殊状态值未过滤等等,细枝末节往往影响深远。
人不可能不犯错误,我们需要做的是怎么去避免不必要的错误。
这里需要提一下就是 Code Review 的重要性。一个人很难发现自己错误,这其中有主观情感因素及客观认知问题。旁观者清,Code Review 的过程就是一个发现,纠正,优化改进的过程,每个人的关注点不同,或全局的架构设计,或细节的变量命名,层层滤网过滤之后,将极大的减少出错的可能。有一点需要指出的是,很多初次接触 Code Review 的开发人员,往往会有抵触情绪,或认为不必要,后羞于漏洞与众人,团队间在此方面需要建设共同的认知,意识,规约是非常有必要的。
三、处理脏数据
数据脏了怎么办?洗洗就好了!
脏数据好处理吗?好处理。问题是脏数据在哪里?
单个用户问题的数据可以针对性的去处理。而那些隐藏的脏数据则需要去定位清洗。
1、限定影响范围
哪些数据受影响了?从什么时候开始?影响是什么?
限定业务逻辑范围,影响的数据面,比如,粉丝+好友+群组关联的数据群,A关注了B,A多了一个好友,B多了一个粉丝,A把B放在了特定的群组里。
限定时间范围:从什么时候开始上线了或者从什么时候触发了有问题的业务逻辑,比如,对 X 对象的变更修改调整起始于 1 号,那么就需要查找所有 1 号至今的所有与 X 对象修改相关的活动数据。
限定影响:确定对数据的影响是什么,才能进一步决定下一步怎么处理,比如漏掉了一个属性赋值,那么是否可以根据其它状态属性进行修正?改删除的数据没有删除,是否直接删除就能解决问题?
2、筛查数据
有了第一步的基础,这一步需要做的就是找出问题数据。one by one,一个一个与校准值进行对比过滤。通常,我们会添加一个临时的处理接口进行实时的校验。
这里,需要指出的是,不同量级的数据的处理筛查方式上会有所区别。
如果,你的数据量是万级别的,那么单个顺序过滤就行了,不需要做什么额外的处理;
如果,你的数据量是十万级别的,那么你可能需要在处理接口上添加批处理。
如果,你的数据量是百万级别的,那么除了接口批处理外,脚本的多线程处理也会需要。
如果,你的数据量是千万级别的,临时扩展一些数据处理节点也会大大提高处理效率。
3、处理脏数据
脏数据总归不是大量级的,处理之前,必要的校验,验证不可或缺。
选取测试数据或者从脏数据中选择少许进行处理结果验证。充分认证后,再进行全量处理。