对业务系统的可扩展性设计思考
说到扩展性,一般会谈到数据库扩展性和应用扩展性两个方面的内容,当然很多应用的扩展性最终会反馈到数据库本身的扩展性上面来。而对于应用本身的扩展性本身又包括了数据模型的扩展,接口的扩展,业务规则的扩展,流程的扩展几个方面的内容,下面分别对上面内容进行描述。
1
数据库设计的扩展性
对于数据库设计的扩展性准备谈两个方面,一个是数据库本身的拆分问题,一个是数据库设计方面的内容。数据库拆分重点是解决在非集群架构下如何扩展的问题,数据库设计重点是解决在 IT 基础设施架构不变情况下扩展和性能提升。
1.1
数据库的水平拆分和垂直拆分
对于数据库本身的扩展和性能,一个方面就是采用数据库集群架构,另外一个关键方法就是进行数据库拆分,而数据拆分本身分为垂直拆分和水平拆分。
垂直拆分简单来说就是根据业务域将数据库表拆分到不同的数据库上面,而水平拆分则是指对于同一个数据库表按照用户,组织等其它拆分维度将数据水平拆分到不同的数据库上,但是这些数据库本身数据库结构是一样的。
垂直拆分实际上和我们现在谈的微服务架构下的数据库拆分概念一致。
而水平拆分要解决的问题是如果已经进行微服务架构设计,但是单个微服务本身的数据库性能无法达到要求,这个时候就必须考虑水平拆分。例如我们可以按不同区域将用户数据拆分到不同的数据库中,或者按企业本身的多组织结构,将不同组织数据库拆分到不同的数据库中。
水平拆分本身应该对上层应用透明。
即上层应用并不清楚底层进行了数据库水平拆分,而仍然像访问一个没有拆分的数据库一样进行数据库的访问,数据库的 CRUD 操作等。在这个时候就需要在拆分完成的数据库上面构建一层 DaaS 数据访问层,也可以叫做数据库访问中间件,即屏蔽底层拆分的复杂性。
拿 Mysql 数据库来举例,前面几年就出现了很多的 Mysql 数据库中间件,类似最早的 Cobar,Amoeba,到 360 的 Atlas,Mycat 等。
拿 Mycat 数据库中间件来看,具体的架构如下:
但是实际在 DaaS 中间件实现过程中,本身对于上层应用仍然有要求,虽然现在中间件本身已经支持了类似分布式事务处理等功能,但是还是无法支撑完全意义上的全部 Sql 特性。同时在数据库拆分后本身还存在数据如何拆分和分区,具体路由规则等问题。从 Mysql 中间件本身这几年的关注热度来看,实际上本身存在下降趋势。特别是类似 TiDB,阿里的 PolarDB 采用其它技术实现方式更好的来解决了分布式数据库问题。
1.2
数据库设计的扩展性
如果字段扩展极其不确定,或者说针对不同的单据类型实际上扩展的字段数都不一样等情况,你也可以考虑将传统的数据库横表设计模式转变为纵表设计模式。这种扩展性最好,但是涉及到数据库 SQL 查询,关联查询会极其不方便,因此还是要慎用。
其次,我们很多时候还会把业务系统中经常变化的内容转变为配置进行存储,这经常看到的就是各类基础数据表,数据字典表,全局变量配置表,数据编码映射表,编码生成规则表等。这些数据表将业务本身可配置的内容持久化到数据库中以方便进行修改。在一个业务系统本身进行集群化部署的情况下,这些配置信息是不适合放到中间件服务器的配置文件里面的,持久化到数据库表里面反而是一个好的选择。
再次,对于数据库单表容量的扩展本身也是重点要考虑的问题。
拿我们实际项目来说,对于 ESB 服务运行日志的存储,单个服务实例表的数据库行数超过 5 亿条,这个时候对模糊查询性能影响极大,必须对数据库表进行分区。在实际项目中,我们基本是按每天来设计分区表,每天就产生一个分区表。
在采用分区后至少能够满足查区间范围在一天内的数据库会很快,其次就是对于数据库按天进行清理或备份更加容易。但是即使这样数据库模糊查询性能也很难满足要求。因此在这种情况下还需要在数据库上增加一级二级索引来解决问题。
1.3
应用层的扩展性设计
如果一个业务系统采用了流程引擎,那你可以看到一个流程设计本身就很容易扩展,比如增加或删除流程活动节点,选择活动节点的处理角色和参与人等,这些内容本身可配置,扩展没有问题。
一个业务系统本身也分为多个模块或组件,那么模块间的接口本身稳定性就很重要,那么接口如何保证可扩展?对于接口的扩展实际上和数据库表扩展思路类似,我们可以在接口上面启用多个扩展字段,也可以在接口中启用一个存储大 Json 串的扩展字典,在 Json 串中可以灵活定义和传输多个字段内容。
一个业务系统本身的变更往往涉及到数据对象的变更,流程的变更和规则的变更。而这里面最麻烦的就是规则的变更,而规则的变更说到扩展和可配置性,大家容易想到的就是启用规则引擎来进行处理。而实际上对于复杂规则的实现,即使你采用了规则引擎,里面也要写大量的脚本代码,即这部分采用规则引擎本身并没有太大的意义可言。因此对于规则的扩展核心是如何将规则从原有的业务系统单据中剥离出来,即我们首先定义的标准的规则扩展接口,同时在接口中输入你需要使用到的数据对象,当需要扩展的时候我们仅仅对接口内容进行实现。
可以构想一个最简单的场景,对于采购订单创建功能,我们可以考虑在订单创建 Button 事件处理中增加对接口方法的扩展和调用。即订单保存的时候涉及到如下步骤:
//ISaveBefore.Process(Form Data);
//Save();
//ISaveAfter.Process(Form Data);
即当你需要在订单保持前进行预处理,或者是在订单保持成功后进行额外操作的时候,你完全可以对上面两个接口方法进行实现。这两个接口方法实际上对保存事件前后进行了拦截,而拦截到的内容如何处理你完全可以自己进行扩展。
为何要采用上面的方式进行设计?你可以想下如果一个 CRM 系统你面临诸多的客户,每个客户可能都存在会在你的标准 CRM 版本上面进行定制。对于订单创建功能 90%的功能都是标准功能,但是仅仅是前后的处理规则和逻辑有少量差异。那么我们这样设计就很方便针对不同的客户扩展不同的定制代码包,而确保 CRM 产品主干版本只有一套,这种方式可以最大化产品复用度,同时由标准化配置管理工作。
其次当我们实施了上百家客户的 CRM 系统后,你会发现所有用到的规则本身也可以进行标准化,比如标准化为 100 个规则逻辑,而实际上客户在实施的时候是选择要启用哪些规则控制。这个时候你就可以考虑规则本身也可进行灵活配置的事情了。
举例来说。
ISaveBefore.Process(Form Data)
{
//将不同的规则项独立出来
BusinessRule1();
BusinessRule2();
BusinessRuleN();
}
然后我们再需要一个简单的配置表,来配置一个新客户究竟启用哪些规则项就可以了。对于启用的规则项就执行,对于不启用的规则项就不执行。在这种处理方式下,虽然我们没有启用规则引擎,但是规则本身形成了条目化的规则库或规则片段,其次规则片段可以灵活绑定到客户项目上。这种规则处理的灵活性将足够我们在实际实施项目中灵活应对。
通过类似 AOP 的思路,拦截进行扩展,将是应用层扩展的一个关键思路点。这种扩展方式虽然没有实现完全的灵活可配置化,但是却具备了相当的灵活性和可管理性。
2
基于 SOA 思想扩展性设计
再重新强调下 SOA 架构思想的一个关键即上层的应用或流程应该是基于服务组装和编排出来的,当上层应用出现变化的时候,很多时候并不需要底层的服务进行变化或修改,而只需要对服务重新进行组装。
这个和当前我们谈的中台架构思想是一致的,即前端应用会出现业务需求变更或变化,但是大部分时候中台能力并不需要变化,而只需要对前端应用逻辑进行修改。
在私有云 PaaS 平台建设里面,我当时就给出了结合 SOA 架构思想的一个完整的业务组件逻辑设计的一个参考图,具体如下:
在整个架构中,横向分为了平台层,服务层和应用层。对于服务层可以理解为当前中台提供的共享 API 服务能力开放是一个道理。对于应用层,原来叫薄应用,也可以理解当前微服务下的前端应用。整体的架构完全符合我在前面一篇文章谈到的资源+服务+应用的三层架构模式。
对于应用层而言,其中仍然分为数据层、业务逻辑层和展现层的三层架构模式:
2.1
数据层
数据层主要包括了对于主数据等共享数据的访问和读取,也包括了对业务组件模块自己私有数据的 CRUD 操作。数据层可以直接调用 DaaS 服务层能力操作底层数据库,也可以直接调用封装后的领域数据服务能力查询和访问数据。
2.2
业务逻辑层
业务逻辑层和传统业务逻辑层最大的区别是体现了 SOA 服务化的思想。即对于业务流程和功能的业务实现是通过平台层提供的技术服务和业务服务能力进行组合和组装实现的。这既可以通过传统的代码开发和服务调用来实现,也可以通过类似 BPEL 设计和建模工具等可视化的进行灵活配置和实现。
可以看到,对于业务逻辑层的重点就是对已有的各种业务服务,数据服务,技术服务能力进行组合,完成一个关键的业务功能实现。
2.3
展现层
展现层主要是各种前端和界面实现技术,传统的如 JSP,HTML,JQuery 等。也包括当前主流的类似 VUE,React, Angular 等 Web 前端开发框架。展现层通过调用逻辑层的服务能力进行数据的存取和业务规则的实现,同时也包括了界面集成技术实现多个业务组件的界面集成。
3
业务系统可扩展总结
最后再简单总结下一个应用系统的可扩展设计。
其一,可扩展设计涉及到数据库,应用层,业务规则逻辑,界面层的多处可扩展性。必须要各个分层,各个点上都考虑到可扩展性,往往才能够完成一个完整的扩展性设计需求。
其二,可扩展性设计一方面是解决的业务系统并发量增加后的可扩展能力,一个方面重点是解决的业务需求变更的时候系统本身的适应变化度。对于变化的适应本身又分多个层面,即是否需要变更数据库,是否需要变更应用开发代码,是否需要重新编译等。在扩展性设计中最好的就是尽量不要涉及到重新编译和版本部署。
其三,可扩展性设计往往会牺牲性能,因此也不能过度的使用扩展性和冗余设计,导致整体应用架构性能出现明显下降。
原文:https://www.toutiao.com/i6897886759997178372/
往期推荐:
贝壳金服史海峰:凭一口气,点一盏灯;念念不忘,必有回响
史上最烂的开发项目长啥样:苦撑12年,600多万行代码...
阿里技术专家详解DDD系列 第三讲 - Repository模式
祝贺“你假笨”创立的PerfMa再获高瓴创投领投1.5亿A++轮融资
年底项目经理离职理由图鉴!
……
技术琐话
以分布式设计、架构、体系思想为基础,兼论研发相关的点点滴滴,不限于代码、质量体系和研发管理。