背景
只有解决问题才能创造价值。许多问题不是我们不能发现、不能解决,而是思维定势限制了我们,不能系统性的解决
作为一名程序员,我们每天都会遇到大大小小各种各样的问题,也在不断地解决各种各种的问题,同时也在解决问题的过程中不断成长。然而如果我们只是单纯的遇到问题,解决问题,那成长是有天花板的,而且这个天花板可能会很低,这也是我在工作中遇到的比较困扰自己的问题,同时我发现团队中其他人也存在同样的困扰,在经过一段时间的思考和实践后,我把自己的一点经验分享出来,希望能为同样和我一样处在困扰中的人提供一点思路。
方法论
其实我个人并不喜欢"方法论"这个词,提到"xx论"总觉得是一些虚头巴脑的理论,跟实践比较脱节,实际上是一些通用的方法和思维模型
系统性的解决问题,首先要搞清楚两个概念:1.什么是问题 2.什么是系统性
什么是问题
广泛的定义,理想与现实之间的差距就是问题,从这个定义来讲,我们遇到的问题太多了:线上bug是问题,项目延期是问题,效率不高是问题,收益不行更是问题。甚至我们整个业务都是围绕问题解决在运转,就是在解决理想与现实之间的差距。
这样看来,似乎定义问题发现问题并不难,两个关键点:1.定清目标 2.理清现状。 做到这两点之后,问题自然会浮出水面。但是要从这众多问题中找到有价值的问题,并非易事,却很值得,这需要我们结合自己的过往经验和价值观来判断,能够识别关键问题。我们羡慕的大佬、LD,他们的一个重要能力就是能准确识别到关键问题。
什么是系统性
既然要系统性的解决问题,首先要理解,什么是系统性。这个问题就像奥古斯丁在《西方哲学史》中所说的,"至于什么是时间,在没有人问我时,我非常清楚,可一旦要向别人解释,我就有点糊涂了"。我对于系统性概念的理解也是如此,感觉自己懂,但是却不能给别人很好的讲出来。我理解的系统性,应该包含两个层次的含义:
全面性:所谓全面性,不是点对点,而是从点到线再到面的过程,能够从更全面的视角认识并解决问题,需要我们去分析整体与各部分的关系,关注各部分之间的相互作用,理解问题的全貌,最终找到更全面的解决方案
彻底性:所谓彻底性,就是要思考问题的根因,探索问题的本质,从更深的层次分析问题,找到一劳永逸的解决方案。
系统性的典型体现是:一张大图 + 关键要素
系统性描述问题
如何系统性描述问题,也就是明确问题时是什么,这是系统性解决问题的第一步。
美国通用汽车公司管理顾问查尔斯·吉德林曾说过,"能够把问题描述清楚,这个问题已经解决了一半",只有先认清问题,才能很好地解决问题。这个说法并不夸张,他的原理在于当我们把问题清晰地写下来,我们会更容易理清思路,减少混乱和纷杂的思维。当我们开始有条理地思考问题时,我们可以更容易地找到解决问题的方案,而不是在杂乱无章的思维中浪费时间和精力。
清楚的描述问题,按照上面问题的定义,无非就是把现状和理想状态描述清楚,我认为需要做到以下三点:
- 问题定义
能够给问题一个准确的定义,通过一句话简洁的描述出来。比如我们拿稳定性来举例,什么是稳定性问题呢,系统稳定性是指系统在外界影响下表现出的稳定程度,具备抵抗干扰的能力。
- 问题量化
当前问题有没有指标可以量化,比如对稳定性来讲,当前系统事故频率怎么样,治理后怎么样,当前系统sla多少,预期应该达到多少
- 影响评估
这个问题会带来怎么样的后果,同样以稳定性来举例,如果出现了稳定性事故,可能会造成的资金损失是多少,用户反馈投诉有多少等
系统性思考问题
系统性思考:决策者处理复杂问题的核心技能
能够清晰的描述问题之后,接下来我们要做的就是针对发现的问题,进行系统性的思考,这是我们最终能否系统性解决问题的最关键一步。市面上有很多讲解系统思考及思维模型的书籍,这里提炼一些关键信息和方法,系统性思考有三个维度:
暂时无法在飞书文档外展示此内容
- 深度思考
不能停留在现象的表面思考,而要从现象深入到背后,直击本质。《教父》中说,"花半秒钟就能看透事物本质的人,和花一辈子都看不清事物本质的人,注定是截然不同的命运"。然而怎么样看清事事物的本质,才是真正困扰我们的。在《直击本质》书中,作者提出了三种思考问题本质的方法:
- 思考事物的根本属性,一个事物之所以成为它的根本原因
思考和解决一个问题的起点,正是对事物根本属性的思考与理解,如果不理解事物的根本属性,就无法回答"是什么"的问题,不明白是什么,也就很难回答"为什么"和"怎么做"。比如商品是用来交换的劳动产品,这就是商品的根本属性。
- 思考问题的根源:导致问题发生得根本原因
就像我们每次事故之后复盘一样,都要找到事故的根因并加以治理,才能真正的解决问题。
- 思考现象背后的底层逻辑:隐藏在各种现象背后不变的规律
比如对于直播打赏,我们普通人看来就是内容生产者与消费者各取所需的一种模式,而高手们看到的是情感能量的孵化,能够从人性的维度去解释直播打赏的动机,至于大佬们看到的是啥,我也不知道,但是肯定比我们认识的更深刻
- 全局思考
不能单点、局部的看待问题,要从更高的视角,更全面的思考问题,在纷繁复杂的关系中看到系统各个模块之间的相互关系,看到问题的全貌,从中找到规律、利用规律、做出最有决策。
我们工作中也经常会被挑战"到底有多少个场景"、"这些事做完系统就稳定了吗"、"这些事做完系统就没有隐私合规问题了吗",这里挑战的本意是全面性,防止有遗漏。如果我们在解决问题的时候就能提前全局思考,那我们自然不怕这些挑战,甚至别人没想到的,我们也想到了。
- 动态思考
不能停留在某个时刻看问题,要理解每个人、每个业务都是动态变化的,在《如何系统思考》一书中提到,动态思考具备以下两个特质:
- 不只是看到当下的因果关系,而应该拉长思考的时间维度,看到事物发展的动态变化和可能性
- 不只是看到单向的、线性结果关联,而是应该认识到所谓的"因"与"果"之间可能是环形互动的。
复杂的业务难题,从来不是一个静止的点或面,而是动态发展系统。比如在今天遇到的内存不够、CPU受限等问题,在未来几年或许根本不是问题。
坦白讲,对于动态思考,我还没有理解的很深入,也没有能够在实践中有很多思考
系统性思考解决实际问题案例
在了解系统性思考之后,遇到问题能够深入、全面、动态的去思考并寻找解决方案,那么自然能达到系统性的解决问题的目的。这里给一个我自己实践中的例子:
问题描述
- 问题定义:在一次DB迁移过程中,新增查询没有命中索引,出现慢查询,最后导致整个DB挂掉
- 影响评估:整个库挂掉,导致所有跟这个库相关的多个直接业务以及上游业务都受影响,C端主播中心页面出现服务器打瞌睡,B端工单系统不可用
- 目标量化:DB打挂的故障永不发生
思考过程
- 深度思考:夺命连环问,寻找根因
Q: 为什么DB会被打挂?
A: 因为新增查询没有命中索引,出现慢查询
Q: 为什么出现慢查询,就会被打挂,出现慢查询就一定会被打挂吗
A: 出现慢查询不一定会被打挂,只有在高流量下出现慢查询才可能会被打挂
Q: 到底多大流量算高流量,我们的DB能承受多少 QPS ?
A: 没有固定阈值,DB能承受的QPS跟DB的硬件设施(内存、核数)有关,也与单次查询耗时有关,对于固定硬件设施的DB,整体的吞吐大致是跟"QPS * 单次查询耗时"成正比
Q: 为什么新增查询没有命中索引,技术方案中没有考虑到吗
A: 有考虑,但是本次变更新增多个查询,有遗漏,没有确保每个新增查询都能命中索引
Q: 切流过程中没有发现吗,为什么全量切流完之后才发现
A: 小流量阶段因为流量小,慢SQL在小流量下并未影响到DB的查询
Q: 切流前在新库上做过实验吗,有没有发现DB异常?
A: 切流前在新库上做过双写测试,当时没发现问题,因为当时老库中的历史数据还未同步到新库,数据量较小,即使没有索引,查询也不会很慢,所以没有触发异常
Q: 怎么样能确保后期新增查询都能命中索引,不会出现类似的问题呢
A: 1.人工保障,提升研发质量意识,新增查询都能主动确认是否有索引 2.系统保障:系统在发现未命中索引之后立马发送提醒给研发,这样如果新增没有命中索引的查询,在ppe阶段就能发现
- 全局思考,穷尽可能性
Q: 还有没有其他可能也会导致DB被打挂
A: 1.DB数据量大,QPS高,即便是查询都命中索引,也可能会打挂 2.出现漫长的锁等待,单次查询耗时也会升高,可能会导致DB被打挂 3.如果出现大事务,会锁住更多资源部释放,容易导致锁等待及连接池被打满
Q: 对这些可能,我们的预防措施是什么
A: 1.限流,对BD做一层保护 2.如果可以,加缓存,减少到DB的流量 3.读写分离,让更多的读流量都打到读库 4.数据量很大的时候,考虑分库分表 4.对于锁等待,增加锁等待时长告警,出现漫长锁等待可以报警,也可以配置直接kill 5.可以整体对DB配置慢查询kill,防止拖垮整个库
Q: 做了以上策略就能保证DB不会被打挂码
A: 做到以上策略,在我的认知里已经是可以保证DB不会被打挂
Q: 其他服务会不会也存在类似的问题
A: 可能会有,我们对这次DB的故障的防护治理要覆盖到我们所负责的所有的业务
Q: 万一我们还是没有做好防护,DB还是被打挂了呢,该怎么及时处理止损
A: 1.如果是变更导致的,回滚变更 2.如果没有限流或者限流阈值不合理,及时调整限流 3.第一时间找DBA,专业的事情交给专业的人处理
- 动态思考,拉长时间线看问题
Q: 影响DB故障的多个因素之间互相作用是什么,有没有隐形环路?
A: DB自身性能以及流量和单次查询耗时共同决定了DB的稳定性,不能单独定论说我们的DB可以承受多少多少QPS,这种一定是扯淡的
Q: 一年后两年后甚至多年后,这些保护措施仍然是有效的吗,有没有新的可能出现
A: 随着时间推移,数据量肯定会越来越大,流量大概率也会越来越大,我们遇到的挑战也会越来越多,但是这些防护措施仍然是有效的,做好防微杜渐是关键
通过以上深度思考和全局思考,我们对于DB被打挂这个问题,有了一个深入且全面的认识,也能够针对各种可能做出有效的预防措施。
DB故障 | 原因分析 | 事前防护 | 事中应急 |
---|---|---|---|
流量大 | 缓存 | - 如果是变更导致的,回滚变更- 没有限流或者限流阈值不合理,及时调整限流- 第一时间找DBA,专业的事情交给专业的人处理 | |
限流 | |||
读写分离 | |||
分库分表 | |||
慢查询 | 未命中索引 | 1.提升研发质量意识2.系统索引检测提醒 | |
数据量过大 | 分库分表 | ||
漫长锁等待 | 1.锁等待时长告警2.慢查询kill | ||
大事务 | 1.提升研发意识,设计阶段避免大事务出现2.慢查询kill |
结束语
以上是一些个人通过阅读提炼、实践总结的系统性解决问题的思考和经验,我仍然相信任何理论都不可能完美的解决所有问题,只能提供一些参考意义,否则一两本书就可能把大家的问题解决完了。