对于问题,常说的即是问题定义,分析,解决的完整问题管理生命周期,麦肯锡的问题分析七步法。在我谈思考框架逻辑中,一个重要的分支即问题分析和解决。 对于问题分析和解决,常用的往往是非结构化的问题分析方法。 即首先是定义清楚问题,然后提出最可能的假设方案,在提出假设后通过实验或试验的方式验证假设是否成立,如果没有成立则再换一个新的假设持续迭代。 当我重新思考问题分析解决的时候,实际上包括了两个关键点,即问题定义的核心是将问题进行细粒度分解;问题分析解决的重点是匹配逻辑;同时在前期已有的知识经验积累又是你去进行模式匹配的前提和基础。解决问题的几个关键点 记得在前期实施一个集团大项目的时候,自己写了大量的技术问题分析和解决的文章,这些问题基本也是来源于真实的项目实践,即使到现在有些问题也没有完全得到定位和最终解决,包括我们找了Oracle的专家和顾问,也不是说马上就能够满足我们解决掉该技术问题 简单来说,如果一个技术问题,你能够直接快速地在网上搜索到相关的答案,这种问题都谈不上是真正有挑战的技术问题。 对于技术问题的解决,基于前面实践的问题定位,分析和解决的思路,我还是想谈下在解决技术问题中的一些关键点和思考逻辑方面的内容。 个人已有的知识技能和大量实践经验的积累 这个点相当重要,任何知识库,搜索都代替不了个人已有的知识经验积累。 为什么说工作经验很值钱,往往就是你在一个专业领域有大量实践经验积累,大量问题分析解决经验积累。这些经验可以帮助在遇到问题的时候快速地对问题进行预判和定位,包括提出最可能的假设路径。 我们现在解决问题,很多都是非结构化解决问题方法,即是优先提出最可能的假设,然后再去验证假设是否能够真正解决问题。那么有经验的人往往就最容易提出最可能的假设路径,而减少对各种不可能弯路的尝试。一个问题本身有A到E五个独立假设路径,而最可能路径是A,你解决问题的往往就是你最后才假设和尝试到A路径并解决问题,而有经验的人往往一开始就选择了假设A进行验证。 要积累这种经验,一定不是纯粹的理论知识学习,而是必须真实现场大项目实践,实践才是真正积累经验的关键;其次就是任何技术问题解决后个人也必须进一步的复盘,问题解决后不复盘,那么解决问题的方法也无法进一步上升为通用经验和方法论。 问题定位的重点就是缩小范围和确定边界 一个问题出现了最重要的就是快速的定位,比如一个业务系统查询故障,要快速的定位是基础设施资源的问题,还是数据库和中间件的问题,还是说程序的问题。如果是程序的问题,又需要马上定位到究竟是前端的问题,还是逻辑层的问题或数据库的问题。只有快速的确定边界和定位问题,往往才能够有针对性地去解决问题。 要注意的是,任何问题的定位都是追溯到引发问题的根源,而不是解决问题的表象,类似头痛医头脚痛医脚。 如何缩小范围和快速的确定边界,比如我们假设一个最简单的场景,问题产生在S1或S2两个关键过程节点。那么如何快速的确定问题是在S1阶段产生的还是在S2阶段产生的呢? 对于这个问题,我们有如下的定位方法和思路可以参考和借鉴:替换法:比如将S1替换为Z1,如果问题消失,那么说明问题出在S1这个阶段。断点法:已经S1阶段完成后的输出应该为x,那么就设置断点监控是否是x,如果不是则问题出在S1阶段。假设法:假设S1阶段有问题,对S1阶段的参数进行调整并观察问题是否解决,如果解决问题可能出在S1。 当然还有其他很多的问题定位方法,但是对于所有问题定位和确定边界的方法中,最有效的仍然是类似于快速查找中的二分法,通过二分法可以快速的帮助我们缩小范围和定位问题。 问题解决的重点是模式匹配 问题解决的重点是模式匹配,即你进行详细问题定义后的问题域和你的知识经验库之间的模式匹配。一个人的分析和解决问题能力强,很大原因就是在这种模式匹配的能力强。那么这种模式匹配能力是否可以进一步模型化表达? 对于这个问题模型,已经是在思维的逻辑更加下一层的形式化表达模型,在前面的思考中也确实没有太仔细去思考这个问题。只是更加强调了几个方面的内容:对于输入的问题需要进行分解,因为只有细粒度上才容易匹配。对于日常的学习和实践,需要更多地进行总结和复盘,将经验变化为一个个可以复用的知识点。通过大量的实践不断的积累自己的模式库,即在什么场景下应该用什么方法工具解决什么问题。 以上这三个点可以说是模式匹配的核心,但是这里面仍然有一个假设,即在细粒度匹配的时候必须是我们已经遇到过或熟知的场景才可以,其次就是我们应该知道如何去分解,还有就是要知道如何去积累确保积累的知识点是可以复用的。而真正要做到如上三点并不是容易的事情。 在以知识经验进行模式匹配的时候,又出现一个关键问题,即匹配的层次和粒度问题,是粗粒度匹配,还是细粒度匹配? 在将SOA概念的时候我们经常会引用印刷术这个例子,即当你要印刷一篇文章的时候,如果现在只有1个个独立的铅字,那么我们在编排印刷板的时候往往花费大量时间。由于这个原因我们在后期增加了常用词和词组,这样我们在编排的时候明显加快。 我们写文章也是同样的道理。 如果你脑子里面全是一个个独立的汉字,你很难快速的写出一篇文章,当你脑子里面已经形成了大量的常用词组,常用语法,句型的时候,你就能够快速的进行组装。 也就是说细粒度匹配容易,但是匹配工作量巨大,匹配速度慢;而粗粒度的匹配相当来说匹配上概率小,但是匹配速度快,匹配效率高。深度优先和广度优先 在上一个大项目里面,当时遇到一个JVM内存溢出的问题,这个问题实际花费我大量的时间进行分析和诊断。 当遇到这个问题的时候,我仍然采用的提出最优假设再去逐步验证的思路。 而对于这个问题,基于自己原有项目经验,我第一时间就定位在了接口服务出现了大并发和大数据量调用,导致了JVM内存溢出。当时提出这个假设的原因有两个。 其一是平台已经稳定运行了一段时间,不是上线就出现该问题,而是在近期才出现该问题。其二就是在前期项目中,我们出现过类似情况,即由于大并发调用导致内存溢出。 正是因为这个经验积累,我将大量的时间都用于排查具体出现大并发调用的时间段,大并发调用的服务接口,并且对大并发调用进行了限流处理等。 但是问题并没有彻底解决,还是会出现JVM内存溢出的问题,最终经过多次排查,最终发现是代码程序有内存泄漏导致。 对于这个问题的定位定义来讲,实际上大并发调用和代码存在缺陷导致内存泄漏,就是两个关键的假设和分支。 如果确定是大并发调用并一直去深入找原因就是深度优先,但是如果是大并发调用发现快速验证你的思路的时候,快速的去排查代码缺陷这个分支就是广度优先。 深度优先和广度优先 在解决问题的时候,往往一个问题可能的解决方法有多个途径,对于非结构化问题解决方法里面,谈到最多的就是首先根据自我已有经验积累,提出最优假设的解决方法之一,然后再对提出的假设进行逐步的验证以最终确认问题是否解决,如果最终不能够解决问题,则往往还需要选择新的假设并继续验证。 一个人,往往解决问题的效率最关键的就是是否能够在第一次就准确地找到最可能解决问题的假设,即根据经验得出最优假设的能力往往是最关键的,而对假设的验证往往仅仅是工作量的问题。 基于以上思路,则可以看到非结构化解决问题的思路是一种类似二叉树遍历中的深度优先搜索模式。即首先基于最优假设,一直进行论证和深度搜索,只有最终验证为无法解决问题的时候再选择次优假设并进行进一步论证。 那么对于这种非结构化解决问题的方法可能存在如下问题,即: 首先,当你面对一个问题的时候,如果可行的几个方案解决该问题的概率都是相同,如何选择路径?你可以看到在这种情况下,你往往只能够使进行随机性的选择,那么这种选择和完全无目的的全部遍历没有任何区别,你最终解决问题的效率往往取决于你的运气。 其次,当我们选择了一个假设的时候,如果这个假设的最终论证需要10个大阶段或步骤,那么一定需要搜索到最底层吗?我们碰壁回头的地方究竟应该选择在哪个深度? 正如前面我们分析JVM内存溢出问题一样,固有思维导致认死理,不断朝死胡同的深度里面钻,这直接影响了我们问题解决的效率和速度。 由于深度优先本身的问题,我们会发现在非结构问题解决中,基于假设驱动并实验验证的方式往往还存在一定的改进和扩展空间。 而这种改进空间往往就会涉及到引入广度优先搜索方式。对于广度优先搜索的方式,其一个重点就是: 如果一个问题有三个解决思路,每个思路可能都涉及到35个验证步骤,那么我们会优先对三个解决方案的步骤1进行验证。通过迭代1验证完成后我们会进一步评估最可能的解决方案是哪个,然后再进一步对最可能方案展开广度遍历。 广度优先搜索的适用场景就是对于多个解决方案往往可行性概率相差无几的时候,我们为了避免一开始就陷入某一个解决方案的深度中,而在死胡同里面越走越深,我们就必须通过迭代的思路,对多个方案的关键步骤进行尝试,以进一步确定最优概率,然后再基于最优概率点向下搜索。 如上图,如果最终答案是在N点,对于假设的三个分支没有开始任何验证步骤前解决问题概率相同。 如果是深度优先: 最少需要5次:CGHMN 中间为10次:BEKLFCGHMN 最多为13次:BEKLFDIJCGHMN 如果是广度优先: 则需要次数为7次:BCDGHMN 即如果你对于刚开始提出的假设没有绝对的把握是在C这个分支上的时候,采用广度优先往往是最佳适用的方法。广度优先本身是和概率结合的方法,即通过对所有分支的广度遍历,进一步明确最大概率分支,然后才是展开第二层的广度搜索。 引入广度优先本身就是一种敏捷迭代的思路,即通过广度对原有模糊的内容进一步清晰化,以方便我们决策。 那我们再来看另外一个问题。 如果刚开始通过分析,解决方案在B分支概率为80,C分支为60,D分支为40。 那我们优先进行B分支的深入优先遍历和假设验证,但是当我们验证到第二层深度的时候,我们可能会发现该分支解决问题的可能性已经降低到50,你应该是及时回头优先去验证C方案,还是说一直将B分支验证完再来考虑对C分支的假设验证? 这个问题本身值得思考,这直接影响到我们平时解决问题的效率问题,或者说我们平时很少去考虑过在提出假设并逐步验证的方法中究竟应该如何回头?或者说当我们面对一个问题的时候,我们往往并不会一开始就构思多个解决方案,并评估每个解决方案可能解决问题的概率。 因此,究竟是深度还是广度优先并不觉得,当你走向下个步骤的时候你随时都应该时刻你去执行这个步骤或假设判断能够解决问题的概率究竟提升了没有。深度优先广度优先两种方法结合概率的综合使用才是非结构化解决问题方法的敏捷迭代思路。