行业资讯

架构设计六小原则

计算机软件是一个复杂的系统。复杂的系统总是似而不同,在宏观上类似,在细节的地方各有千秋。因为细节决定成败,复杂的系统很难有屡试不爽的解法。尽管如此,解决的多了也会遗留一些经验。这些经验再加以重复验证就形成了原则。这些原则有其特殊的历史背景,不一定放之四海而皆准。但是不听老人言,吃亏在眼前,在能自行顿悟之前建议能熟诵于胸,以防不测。

 

架构六则

  1、 状态多具象,读写本不同。

   2、状态常迁移,一致有成本。

   3、优化守本分,变化相对论。

   4、 熟诵六小计,锦囊随身行。

 

 

状态多具象

 

计算机数据分为正源和衍生两种。正源数据指的是官方钦定的最原始的数据,所以有时候也叫原始数据。衍生数据是基于正源数据派生而来,对正源数据进行二次加工,是对正源数据的一个特殊的视角。常见的衍生数据生成方式有筛选、聚合、统计等等。有一些数据属于衍生数据,但是并不那么直观。一个例子是数据库索引。数据库索引是为了加速数据查询而生成的特殊数据结构,是由数据库表衍生出来的一份数据。另一个例子是网页前端界面显示。一般的网页前端界面显示的流程是这样的:

1、后端服务器生成业务数据。 

2、业务数据变成与前端通讯的格式,比如json、xml等,通过网络协议发送至网页浏览器。

3、网页代码将数据变成特定的浏览器Javascript对象。

4、浏览器将Javascript对象变为计算机显卡能识别的数据。

5、显卡在显示器上展示数据。

 

以上每一步流程都是数据的处理和加工过程。从每个加工组件的角度来看,输入是正源数据,输出是衍生数据。从上到下完整的来看,后端服务器上的是正源数据,其余数据都是衍生而来。

 

判断数据是正源还是衍生的办法很简单:如果数据不见了,是否能够基于其它数据重新生成出来。比如前面提到的数据库索引数据。数据库有索引的删除和表的索引重建操作,表删除了就没有了,所以索引是衍生数据,数据库表是正源数据。对于网页显示的例子,浏览器刷新之后,网页显示会先消失然后重新展示出来,但是如果后端服务器宕机就没办法再展示网页了。所以前端显示是衍生数据,后端数据是正源数据。

 

 

在架构设计中,因为衍生数据可以基于正源数据重复产生,衍生数据的容灾度较高,恢复的方法较多。相对而言正源数据只有一份,损坏之后无法轻易恢复,因此需要针对业务对容灾的需求进行精心的设计。

 

1.读写本不同

 

对于每个人来说,读是一个内容输入的过程,写是一个内容输出的过程。一般会认为这是两种不同的个人能力,不一定同时都很优秀或者都不优秀,比如有人可能阅读能力强但是写作能力弱,也有可能写作能力强但是阅读能力弱。同理,计算机的读写也是两个不同的行为。计算机的读和写的对象都是数据,但是读操作是数据的输入过程,写操作是数据的输出过程。这里提到的读写本不同的第一个不同点在于操作的不同。数据的输入和输出可能会面临不同的吞吐量、延时、容灾等需求,需要分别对待。

 

读写本不同的第二个不同点非常难以发觉,那就是数据有可能也是不同的。在状态多具象一节里我们介绍了数据分为正源数据和衍生数据两大类。之所以会有衍生数据的一个原因是数据的使用,即数据的读取,可能需要有特殊的优化方式。为了读性能的原因需要对正源数据做一些预处理,比如建索引。所以对于数据库的例子来说写的数据是正源数据,读的数据是衍生数据,所以读写的操作是有可能基于两份不同的数据进行的。

 

总结一下。读写的不同在于读写的数据不同以及数据的流向不同。在架构设计时需要将读写分开考虑,分开优化。

 

2.状态常迁移

 

计算机程序会用到一些数据结构,比如List,Map,Set等等。在面向对象编程的过程中还会使用到类的对象。这些数据结构和对象其实都是一种状态,计算机程序需要正确合理的维护这些状态。

 

通常这些状态都是在内存之中,机器一旦断电就会全部消失。所以需要将这些内存信息保存到可持久化的存储介质上,比如硬盘和网络存储。这个保存的过程其实是内存状态的迁移过程。但是硬盘也会出现机械故障,造成数据丢失。因此更为稳妥的方法是不要将鸡蛋放在一个篮子里,将硬盘的数据再备份到其它的地方。这个数据备份的过程也是状态的迁移过程。

 

除了解决数据丢失的问题需要状态迁移以外,状态迁移也会被用来解决物理局限。一台机器内存的大小是有限的,所以当内存里的数据大到装不下的时候,需要将内存里的数据分散到其它机器上,这些机器作为一组集群提供一个分布式的数据结构。一台机器的硬盘大小也是有上限的,同理,当硬盘不够时也可以将硬盘数据分散到由一组集群组成的分布式文件系统里。对于应用程序来说,它还是在通过同样的接口访问数据结构或者文件系统,只不过这些内容可能分散在了网络上的其它机器。

 

3.一致有成本

 

前面提到了状态常迁移。如果迁移过程中数据出了问题怎么办?比如在数据备份过程中网线断了,或者数据中心停电了。这时候需要有一套应急措施来保证在基础架构恢复之后再次启动数据备份流程。数据的重发是一种成本。

 

数据的全部丢失比较容易处理,相对挑战高一点的是数据部分丢失。比如在数据传输过程中掉了部分数据,或者由于宇宙射线的影响导致账户金额由正变负。这时候除了需要有数据重发功能以外还需要有数据正确性验证的功能。数据的正确性验证也是一种成本。

 

在分布式系统中还有一些更为复杂的情况。比如我们将数据备份至北上广三个数据中心。当我们刚发出最新的数据备份指令的时候本地数据中心掉电,完全停止工作。这时候我们需要在北上广三个数据中心中选择一个来做业务的实时切换。那么我们怎么判断选择出来的数据中心有最新的数据呢?这个问题的难点在于我们需要判断某些数据和一个无法访问的数据是否一致。这个问题的解决需要数据的发送方和接收方一起通过既定的分布式协议来解决。

 

我们也可以把问题简化一点。假设我们不需要判断北上广是否有最新的数据,我们只要选择当中数据最多的一个就可以了。显然可以尝试通过两两比较的方式来判断谁的数据更多。但是如果在比较的过程中网络出现了隔断,比如上海的数据中心成为了一个被隔离的信息孤岛,这时该如何处理呢?分布式系统中还有更为少见的单向通讯故障,一般是在防火墙出现错误配置的时候会出现。假设北京可以访问上海数据,上海可以访问广州数据,广州可以访问北京数据,但是上海不可以访问北京,广州不可以访问上海,北京不可以访问广州。这时每个数据中心都知道剩下两个数据中心的一个信息,但是不知道另一个的信息。这时候应该怎么判断谁的信息更全面呢?这也需要分布式协议来解决。

 

更为悲观的是理论科学家证明了在一定假设情况下,只要网络集群有一个节点有可能出现故障,整个集群便不可能达到一致。这就是著名的FLP结论。我们会在之后详细讲解这个定理。因为机器会出故障,所以一致性的解决就不是成本高低的问题,而是不可能解决的问题了,换句话说想要达到一致性的成本是无限高。

 

所幸在实际操作过程中FLP论文中提到的假设是很容易通过一些方法来规避的,架构师还是有可能设计出一个具有一致性的软件架构。这也表明一致性的解决还有一个很高的学习成本。

 

4.优化守本分

 

如章首所引,提前优化是万恶之源。不提前的优化也需要注意。每个程序都有既定的功能,这就是在软件工程里提到的功能性需求。程序还有一些对于运行效率相关的要求,比如吞吐量、响应时间等。这些的学名叫非功能性需求。优化是会改变程序的行为,但是这种改变只能影响非功能性需求,不能影响功能性需求。这是优化的一道红线,不能僭越。优化守本分就是要守住这根红线。

 

在实际过程中会出现无论怎么优化,非功能性需求都无法达到的情况,比如需要达到非常高的吞吐量。这时候会通过放弃一些业务的要求来简化系统实现。常见的方法有最终一致性、事务补偿等。这时候同时出现了系统优化和功能改变,但由于功能改变是因,系统优化是果,因果关系不同,并没有违反原则。

 

5.变化相对论

 

辩证唯物主义认为,事物的运动发展是变与不变的统一。显然变与不变的相对关系是一个偏哲学的问题。由于我们是介绍实践为主,不深入做理论分析,所以在这里不做过多的阐述,只举几个例子。

 

一个例子是关系型数据库的查询。关系型数据库访问的方法一般是通过SQL结构化查询语句。不同人会有不同的查询内容和方式。在这种场景下数据库里的数据是相对不变的,SQL查询语句是变化的。但是随着互联网的发展,人们对查询结果的实时性要求越来越高。比如在大型的搜索网站、电商或者新闻网站的搜索界面,当输入一个查询条件之后,网站会不断的推送最新的查询结果。在这种情况下查询语句是不变的,数据是变化的。数据和查询在这两种情况下的实现也不相同。在传统关系型数据库的情况下,为了加快查询结果,数据库会对表建立索引。在实时查询结果推送的情况下,数据系统会对查询语句做索引。

 

另一个例子是对数据的操作。金融数据和所有数据一样,一旦修改就变化的和以前不一样了。这样金融行业的审计人员怎么知道几个月或者几年前数据是怎样的呢?一般为了应付审计,系统开发人员会在审计周期开始时做一个全量的数据拷贝,并将访问权限设为只读。这样就有了一个不变的数据版本。这种设计下数据是一直在变化的。另一种系统设计的思路稍有不同。对数据的修改并不会改变数据,而是生成一份新的数据,或者说是数据的一个新的版本。在这种设计下数据是永远不变的,变化的是数据的个数。这两种设计都能解决同一个问题,但是难易程度不同,孰优孰劣高下立见。

 


提交成功!非常感谢您的反馈,我们会继续努力做到更好!

这条文档是否有帮助解决问题?

非常抱歉未能帮助到您。为了给您提供更好的服务,我们很需要您进一步的反馈信息:

在文档使用中是否遇到以下问题: