45 架构重构内功心法第一式:有的放矢
在专栏第8期“架构设计三原则”中的演化原则部分,我提到了系统的架构是不断演化的,少部分架构演化可能需要推倒重来进行重写,但绝大部分的架构演化都是通过架构重构来实现的。相比全新的架构设计来说,架构重构对架构师的要求更高,主要体现在:
- 业务已经上线,不能停下来
架构重构时,业务已经上线运行了,重构既需要尽量保证业务继续往前发展,又要完成架构调整,这就好比“给飞行中的波音747换引擎”;而如果是新设计架构,业务还没有上线,则即使做砸了对业务也不会有太大影响。
- 关联方众多,牵一发动全身
架构重构涉及的业务关联方很多,不同关联方的资源投入程度、业务发展速度、对架构痛点的敏感度等有很大差异,如何尽量减少对关联方的影响,或者协调关联方统一行动,是一项很大的挑战;而如果是新设计架构,则在新架构上线前,对关联方没有影响。
- 旧架构的约束
架构重构需要在旧的架构基础上进行,这是一个很强的约束,会限制架构师的技术选择范围;而如果是新设计架构,则架构师的技术选择余地大得多。
即使是我们决定推倒到重来,完全抛弃旧的架构而去设计新的架构,新架构也会受到旧架构的约束和影响,因为业务在旧架构上产生的数据是不能推倒重来的,新架构必须考虑如何将旧架构产生的数据转换过来。
因此,架构重构对架构师的综合能力要求非常高,业务上要求架构师能够说服产品经理暂缓甚至暂停业务来进行架构重构;团队上需要架构师能够与其他团队达成一致的架构重构计划和步骤;技术上需要架构师给出让技术团队认可的架构重构方案。
总之,架构重构需要架构师既要说得动老板,也要镇得住同事;既要技术攻关,又要协调资源;既要保证业务正常发展,又要在指定时间内完成目标……总之就是十八般武艺要样样精通。
说了那么多架构重构的难度,千万不要被困难所吓倒,架构师正是需要在原来一团乱麻中找到线索,然后重新穿针引线,帮助业务进一步腾飞发展。接下来我将分3期传授我的架构重构内功心法,今天先来看第一式:有的放矢。
通常情况下,当系统架构不满足业务的发展时,其表现形式是系统不断出现各种问题,轻微一点的如系统响应慢、数据错误、某些用户访问失败等,严重的可能是宕机、数据库瘫痪、数据丢失等,或者系统的开发效率很低。开始的时候,技术团队可能只针对具体的问题去解决,解决一个算一个,但如果持续时间较长,例如持续了半年甚至一年情况都不见好转,此时可能有人想到了系统的架构是否存在问题,讨论是否是因为架构原因导致了各种问题。一旦确定需要进行架构重构,就会由架构师牵头来进行架构重构的分析。
当架构师真正开始进行架构重构分析时,就会发现自己好像进了一个迷雾森林,到处都是问题,每个问题都需要解决,不知道出路在哪里,感觉如果要解决所有这些问题,架构重构其实也无能为力。有的架构师一上来搜集了系统当前存在的问题,然后汇总成一个100行的Excel表格,看到这样一个表格就懵了:这么多问题,要到猴年马月才能全部解决完啊?
期望通过架构重构来解决所有问题当然是不现实的,所以架构师的首要任务是从一大堆纷繁复杂的问题中识别出真正要通过架构重构来解决的问题,集中力量快速解决,而不是想着通过架构重构来解决所有的问题。否则就会陷入人少事多头绪乱的处境,团队累死累活弄个大半年,最后发现好像什么都做了,但每个问题都依然存在。尤其是对于刚接手一个新系统的架构师或者技术主管来说,一定要控制住“新官上任三把火”的冲动,避免摊大饼式或者运动式的重构和优化。
我们来看几个具体的重构案例。
1.后台系统重构:解决不合理的耦合
M系统是一个后台管理系统,负责管理所有游戏相关的数据,重构的主要原因是因为系统耦合了P业务独有的数据和所有业务公用的数据,导致可扩展性比较差。其大概架构如下图所示:
举一个简单的例子:数据库中的某张表,一部分字段是所有业务公用的“游戏数据”,一部分字段是P业务系统“独有的数据”,开发时如果要改这张表,代码和逻辑都很复杂,改起来效率很低。
针对M系统存在的问题,重构目标就是将游戏数据和业务数据拆分,解开两者的耦合,使得两个系统都能够独立快速发展。重构的方案如下图所示:
重构后的效果非常明显,重构后的M系统和P业务后台系统每月上线版本数是重构前的4倍!
2.游戏接入系统重构:解决全局单点的可用性问题
S系统是游戏接入的核心系统,一旦S系统故障,大量游戏玩家就不能登录游戏。而S系统并不具备多中心的能力,一旦主机房宕机,整个S系统业务就不可用了。其大概架构如下图所示,可以看出数据库主库是全局单点,一旦数据库主库不可用,两个集群的写业务都不可用了。
针对S系统存在的问题,重构目标就是实现双中心,使得任意一个机房都能够提供完整的服务,在某个机房故障时,另外一个机房能够全部接管所有业务。重构方案如下图所示:
重构后系统的可用性从3个9提升到4个9,重构前最夸张的一个月有4次较大的线上故障,重构后虽然也经历了机房交换机宕机、运营商线路故障、机柜断电等问题,但对业务都没有什么大的影响。
3.X系统:解决大系统带来的开发效率问题
X系统是创新业务的主系统,之前在业务快速尝试和快速发展期间,怎么方便怎么操作,怎么快速怎么做,系统设计并未投入太多精力和时间,很多东西都“塞”到同一个系统中,导致到了现在已经改不动了。做一个新功能或者新业务,需要花费大量的时间来讨论和梳理各种业务逻辑,一不小心就踩个大坑。X系统的架构如下图所示:
X系统的问题看起来和M系统比较类似,都是可扩展性存在问题,但其实根本原因不一样:M系统是因为耦合了不同业务的数据导致系统可扩展性不足,而X系统是因为将业务相关的所有功能都放在同一个系统中,导致系统可扩展性不足;同时,所有功能都在一个系统中,也可能导致一个功能出问题,整站不可用。比如说某个功能把数据库拖慢了,整站所有业务跟着都慢了。
针对X系统存在的问题,重构目标是将各个功能拆分到不同的子系统中,降低单个系统的复杂度。重构后的架构如下图所示(仅仅是示例,实际架构远比下图复杂):
重构后各个系统之间通过接口交互,虽然看似增加了接口的工作量,但整体来说,各系统的发展和开发速度比原来快了很多,系统也相对更加简单,也不会出现某个子系统有问题,所有业务都有问题。
这三个系统重构的方案,现在回过头来看,感觉是理所当然的,但实际上当时做分析和决策时,远远没有这么简单。以M系统为例,当时我们接手后遇到的问题有很多,例如:
- 数据经常出错。
- M系统是单机,单机宕机后所有后台操作就不能进行了。
- 性能比较差,有的操作耗时好久。
- 界面比较丑,操作不人性化。
- 历史上经过几手转接,代码比较混乱。
- 业务数据和游戏数据耦合,开发效率很低。
从这么多问题中识别出重构的目标,并不是一目了然的;而如果想一下全部解决所有这些问题,人力和时间又不够!所以架构师需要透过问题表象看到问题本质,找出真正需要通过架构重构解决的核心问题,从而做到有的放矢,既不会耗费大量的人力和时间投入,又能够解决核心问题。这对架构师的分析和判断能力要求非常高,既不能看到问题就想到要架构重构,也不能只是针对问题进行系统优化,判断到底是采取架构重构还是采取系统优化,可能不同的架构师和团队都有不同的看法。这里分享一个简单的做法:假设我们现在需要从0开始设计当前系统,新架构和老架构是否类似?如果差异不大,说明采取系统优化即可;如果差异很大,那可能就要进行系统重构了。
那原来发现的那些非架构重构问题怎么办呢?当然不能放任不管。以M系统为例,我们在重构完成后,又启动了多个优化的项目去优化这些问题,但此时的优化主要由团队内部完成即可,和其他团队没有太多关联,优化的速度是很快的。如果没有重构就进行优化,则每次优化都要拉一大堆关联业务的团队来讨论方案,效率非常低下!
小结
今天我为你讲了架构重构的时候需要做到有的放矢,避免像通过架构重构来解决所有问题,希望对你有所帮助。
这就是今天的全部内容,留一道思考题给你吧,分析一下你目前开发的系统,你觉得需要架构重构吗?原因和理由是什么?
欢迎你把答案写到留言区,和我一起讨论。相信经过深度思考的回答,也会让你对知识的理解更加深刻。(编辑乱入:精彩的留言有机会获得丰厚福利哦!)
- 孙振超 👍(80) 💬(9)
思考了下当前负责的系统,感觉更多的是需要优化而不是重构,主要原因是如果从新来过还是会采用当前的结构,只是功能需要进一步完善和改进。 之前做过一次缓存架构的重构(自认为的):我们的系统是3地5中心的部署架构,我们数据的写入是固定在一个机房进行单点写入,然后同步到其他的集群去,db层面的同步是通过数据库自身的主从同步机制实现的;而缓存的同步则是通过消息中间件来进行的,写入集群发送消息,其他集群接收消息而后写入到各地的缓存集群中。写入时是db写入成功就认为写入成功,返回调用方成功。这种实现方式虽然有数据同步延迟和少量的缓存db数据不一致,但对于绝大多数场景都是可以接受。 后来随着业务的发展,出现了写入后立即读的场景(读和写不在同城),并且不允许读到的数据和写入的数据不一致。在这种情况下原有的缓存更新机制就有问题了:db写入成功缓存消息处理失败或者读请求时消息还没有到达异地机房,这时都会导致缓存和db数据不一致,引起业务异常。 面对此种场景,最初提出的解决方案是让业务方在调用写服务成功后再调用读服务,如果读到的数据和写入的数据一致才进行后续的操作,但这种方案实现总体成本比较高:如果有5个调用方,5个调用方都需要这样修改,重复工作,并且做了不需要自己关注的内容;同时在调用读请求时还要考虑重试机制,实现方案也有些复杂。这个方案提出来后没有一个调用方按照这个做的。 后面进行review的时候决定还是应该自己来做这件事,对外屏蔽复杂性。因而对这一块进行了重构:将缓存内容根据缓存key按照一定的规则进行分区存储,对于实时性和一致性要求高的数据采取同步写缓存操作,如果缓存写失败,直接返回业务方本次写操作失败,由业务方重试。同时本地发送一个删除缓存的消息,异步将缓存删除,确保之后有机会从db中读取最新的数据。这样改造完成后,整体的请求耗时(因为要跨区同步写缓存)和业务失败率有所增加,但满足业务方的需求。 这个改造简要而言是将业务从pa模式改为了pc模式。
2018-10-06 - 吴科🍀 👍(37) 💬(2)
系统优化,我们公司就是一个字,拆,拆数据库,拆服务,没有依赖就没有伤害
2018-08-09 - 宋岩 👍(20) 💬(6)
老师您好,我们现在面临一个运行18年的.net业务系统,业务一刻不能停止,大约2000张oracle表,大量的存储过程函数等实现的业务逻辑,现在要用java来重构,面临的问题就是好多大表都在Oracle中,想去o就涉及到很多表关联,做不到垂直拆分,业务耦合太紧,您有什么好的建议或者我们该从哪方面切入?
2018-08-10 - 彼得.林 👍(20) 💬(1)
总之,架构重构需要架构师既要说得动老板,也要镇得住同事;既要技术攻关,又要协调资源;既要保证业务正常发展,又要在指定时间内完成目标……总之就是十八般武艺要样样精通。 老师,这些都是架构师的软技能吧?市场上的课都偏技术,可以讲讲这些吗?谢谢
2018-08-09 - one day 👍(17) 💬(3)
我公司正巧在重构系统。定期3个月。电商系统。原来是dubbo 服务间调用 ,redis 缓存,mysql,模块化开发,云平台,svn。问题,关联查询太多,dubbo部署的相关模块太多,业务复杂度太高,业务调用同步慢,一搞活动,数据库就爆,业务逻辑懂的人换了一茬又一茬。目标spring cloud,docker,k8s,git,禁止关联查询即单表,解藕慢操作即异步,接口细粒度,层次调用分明,服务隔离能缓存就缓存,业务大拆解。最终会是什么样,静待。学了老师这么多,手术台上实习了,哈哈. 缓存,异步,事务消息最终一致,强制单表查询,业务边界,规范。牵一发动全身,业务等待,加班加班……
2018-08-09 - Geek_58ezrw 👍(15) 💬(2)
老师,我们现在公司遇到的问题和你说的X系统很相似,现在也在逐步将那些热门业务做成微服务,拆分出去,但是涉及到的数据表,拆到其它RDS上去,涉及到的跨库查询很麻烦。现在只能做成接口了
2018-08-12 - 空档滑行 👍(13) 💬(1)
当前的系统刚刚做过一次重构,当时选择重构的原因有这么几个,原系统有一个大的面向客户的web系统和多个单独的进程组成,前后端在一个war包,后端服务单机远行,两者通过db交换数据。问题有这么几个,web系统累积了5年的代码,改个bug已经随时搞挂整个web端。后端服务单点问题。数据库压力很大,一个功能的sql拖垮整个库。 重构的思路是,先将单体后端进程服务化,服务化过程中非核心数据从主表分离出来。web系统结构不动,上线一个服务就从原来的查db改成调用服务。服务梳理差不多了将web端重新实现前后端分离。做完这些后将数据库结构重新设计。总的来说跟文中的第三个例子有点像。
2018-08-09 - 波波安 👍(12) 💬(2)
我们系统目前的瓶颈在数据存储,之前数据的存储都是oracle加上redis做缓存。现在活跃用户在500万左右。拿卡券业务来说,每个月产生的优惠券数量都在6000万左右,导致现在需要运维每两个月就要对表做瘦身,并且前端用户也只能查询两个月内的卡券。 目前讨论的技术方案是使用mycat做分库分表,数据库使用mysql。遇到的问题有事务控制的问题,典型的就是卡券转赠,之前在oracle就直接利用数据库事务就可以解决。现在由于转赠记录和受赠记录不会落在同一个库,所以需要程序做策略来保证最终一致性。
2018-09-04 - lyonger 👍(8) 💬(1)
架构师的要求好高啊,既要对业务非常熟悉,也能精通各种技术,其次还要能hold住人心。
2020-03-28 - 黑小子在路上 👍(6) 💬(3)
目前系统采用类似微服务模式。如有1后台管理,2商家pc后台,3商家app接口服务,4server,都是独立部署。前面的1 2 3都依赖4。目前的问题是1也会访问数据库,server也访问数据库,有一部分重复。2 和3 也有重复逻辑。目前一个小需求,都可能要改多个端,由多个人介入,开发成本比较高。 近期准备重构,使用多模块形式来聚合工程,提取公用服务,但会保留现有独立部署模式,以此来达到提效的目的:-O
2018-08-10 - Leon Wong 👍(5) 💬(2)
双中心如何同步数据?比如我在A中心写入了数据,此时未同步到B,并且发生了宕机,B此时是没有这个数据,还允许被再写入一次,这种场景如何避免数据不一致呢?
2018-11-05 - 一叶 👍(5) 💬(2)
华哥请问一下文中ab系统双中心改造,是数据分区还是两个中心都有完整数据。 1.如果是分区,那其中一个中心宕掉之后,部分人或业务在另外一个中心是没法使用的? 2.如果是完整的数据,当网络出现异常的时候,不是出现脑裂?
2018-10-04 - 文竹 👍(5) 💬(1)
目前开发的系统最大的复杂度在于大数据量计算。数据目前是保存到ES中的,但发现ES对于大数据聚合计算表现得无能为力,未来考虑使用hadoop中spark来做计算。
2018-08-26 - 钱 👍(4) 💬(6)
课后思考及问题 我们的系统正在重构中,并且已经搞了一年左右,重构的系统在验证中,由于老系统本身业务逻辑比较复杂又是0级系统,一旦有问题秒秒钟就会影响大批订单,所以,不完全验证OK不敢上。 重构的原因: 第一业务运维困难,特别是大促、节假日时,页面配置复杂,并且不灵活 第二系统已经运行5年+补丁太多,代码臃肿,理解、开发、测试困难,同时存在第一代及第三代的代码,原来的老代码不敢删除,各种分支判断令人眼花缭乱,有些逻辑已经不知道为啥那样写 第三有人想要更好的KPI 上不了的原因: 第一系统太过复杂,想想看一个方法像老鼠洞一样一层套一层,看完一个方法需要一两天,另外,重构的也不是一个系统,有负责页面配置的系统,有专门负责刷新缓存的系统,有专门负责对外提供服务的系统,有专门负责核心业务逻辑计算的系统,有专门负责持久化数据的系统等 第二改了逻辑但还需要覆盖原来的业务,验证困难 第三领导核心关注点在后台业务逻辑,页面也非常多和复杂,但是压根没有测试,目前业务验证自然会发现很多bug 第四主产品离职了,另外小组长也离职了,他俩级别挺高,组长听说是家庭原因,主产品比较奇怪,他不但级别高工资高而且资格老没人管,他也离职啦 第五团队氛围不佳,安排活都给你细到小时,管人的方式采用雪橇犬理论的方式 第五想一次重构结构N个问题,已不是最初的业务运维困难的痛点了,这就好像和华仔讲的相反——无的放矢 没有聚焦解决一个问题,后面问题扩大化啦 抱歉,借华仔的地盘发一下牢骚哈😄 1:本文核心观点 1-1:重构系统的基本原则——从一大堆纷繁复杂的问题中识别出真正要通过架构重构来解决的问题,集中力量快速解决,而不是想着通过架构重构来解决所有的问题 1-2:重构时架构师要做的事情——架构师需要透过问题表象看到问题本质,找出真正需要通过架构重构解决的核心问题,从而做到有的放矢,既不会耗费大量的人力和时间投入,又能够解决核心问题。 1-3:如何判断系统实现需要重构——假设我们现在需要从 0 开始设计当前系统,新架构和老架构是否类似?如果差异不大,说明采取系统优化即可;如果差异很大,那可能就要进行系统重构了。 有的放矢,如果提炼一下有两点: 第一定位出问题的真正根源是什么 第二聚焦解决主要矛盾的主要方面
2019-09-05 - 张玮(大圣) 👍(4) 💬(1)
确定是不是要重构挺难的,那就等到问题频出,改到想吐的时候做重构吧, 大公司在创新,小公司也在创新,一般2.3年的系统设计不会太差吧,除非影响业务了,影响老板心情了, 归纳下:能优化的别修补,能修补的别重构,保证核心业务OK,更好的支撑创新业务线发展吧 哈哈 如果要重构,拉都拉不住,那就做好预备方案,保证新旧系统并行,时间可控,做好切换时风险控制,要不然,萌萌哒,😄
2018-08-09