关于架构的思考之三:系统复杂度的来源(可扩展性、低成本、安全和规模)

目录

可扩展性

定义:

how:

预测变化的复杂性源头:

在准确预测变化的前提下,如何应对?

两个主要的复杂性相关的问题

1 系统需要拆分出变化层和稳定层

2. 需要设计变化层和稳定层之间的接口

低成本

安全

1. 功能安全

2. 架构安全

规模

1. 功能越来越多,导致系统复杂度指数级上升

2. 数据越来越多,系统复杂度发生质变



可扩展性

定义:

可扩展性指系统为了应对将来需求变化而提供的一种扩展能力,当有新的需求出现时,系统不需要或者仅需要少量修改就可以支持,无须整个系统重构或者重建。

how:

设计具备良好可扩展性的系统,有两个基本条件:正确预测变化、完美封装变化。

 

预测变化的复杂性源头:

1.不能每个设计点都考虑可扩展性。

2.不能完全不考虑可扩展性。

所有的预测都存在出错的可能性。把握预测的程度和提升预测结果的准确性,是一件很复杂的事情,而且没有通用的标准可以简单套上去,更多是靠自己的经验、直觉,没有明确标准,不同的人理解和判断有偏差,而最终又只能选择一个判断。

在准确预测变化的前提下,如何应对?

一般分为两种形式:

1:变化层--》稳定层    例如,如果系统需要支持 XML、JSON、ProtocolBuffer 三种接入方式

2. 稳定层--》变化层   例如,如果系统需要支持 MySQL、Oracle、DB2 数据库存储

具体需要根据具体业务情况来设计。不变的是将“变化”封装在一个“变化层”,将不变的部分封装在一个独立的“稳定层”  。

两个主要的复杂性相关的问题

因此,出现两个主要的复杂性相关的问题:

1 系统需要拆分出变化层和稳定层

对于哪些属于变化层,哪些属于稳定层,很多时候并不是像前面的示例(不同接口协议或者不同数据库)那样明确,不同的人有不同的理解,导致架构设计评审的时候可能吵翻天。

2. 需要设计变化层和稳定层之间的接口

接口设计同样至关重要,对于稳定层来说,接口肯定是越稳定越好;但对于变化层来说,在有差异的多个实现方式中找出共同点,并且还要保证当加入新的功能时原有的接口设计不需要太大修改,这是一件很复杂的事情。

第二种常见的应对变化的方案是提炼出一个“抽象层”和一个“实现层”。抽象层是稳定的,实现层可以根据具体业务需要定制开发,当加入新的功能时,只需要增加新的实现,无须修改抽象层。这种方案典型的实践就是设计模式和规则引擎。考虑到绝大部分技术人员对设计模式都非常熟悉,我以设计模式为例来说明这种方案的复杂性。

以设计模式的“装饰者”模式来分析,装饰者模式相比传统的继承来实现功能,确实灵活很多。但装饰者模式相对普通的类实现模式,明显要复杂多了。本来一个函数或者一个类就能搞定的事情,现在要拆分成多个类,而且多个类之间必须按照装饰者模式来设计和调用。

低成本

低成本给架构设计带来的主要复杂度体现在,往往只有“创新”才能达到低成本目标。这里的“创新”既包括开创一个全新的技术领域(这个要求对绝大部分公司太高),也包括引入新技术,如果没有找到能够解决自己问题的新技术,那么就真的需要自己创造新技术了。

例如:

NoSQL(Memcache、Redis 等)的出现是为了解决关系型数据库无法应对高并发访问带来的访问压力。

全文搜索引擎(Sphinx、Elasticsearch、Solr)的出现是为了解决关系型数据库 like 搜索的低效的问题。

Hadoop 的出现是为了解决传统文件系统无法应对海量数据存储和计算的问题。

安全

从技术的角度来讲,安全可以分为两类:一类是功能上的安全,一类是架构上的安全。

1. 功能安全

例如,常见的 XSS 攻击、CSRF 攻击、SQL 注入、Windows 漏洞、密码破解等,本质上是因为系统实现有漏洞,功能安全其实就是“防小偷”。

从实现的角度来看,功能安全更多地是和具体的编码相关,与架构关系不大。现在很多开发框架都内嵌了常见的安全功能,能够大大减少安全相关功能的重复开发,但框架只能预防常见的安全漏洞和风险(常见的 XSS 攻击、CSRF 攻击、SQL 注入等),无法预知新的安全问题,而且框架本身很多时候也存在漏洞(例如,流行的 Apache Struts2 就多次爆出了调用远程代码执行的高危漏洞,给整个互联网都造成了一定的恐慌)。所以功能安全是一个逐步完善的过程,而且往往都是在问题出现后才能有针对性的提出解决方案,我们永远无法预测系统下一个漏洞在哪里,也不敢说自己的系统肯定没有任何问题。换句话讲,功能安全其实也是一个“攻”与“防”的矛盾,只能在这种攻防大战中逐步完善,不可能在系统架构设计的时候一劳永逸地解决。

2. 架构安全

如果说功能安全是“防小偷”,那么架构安全就是“防强盗”。强盗很多时候就是故意搞破坏,对系统的影响也大得多。因此架构设计时需要特别关注架构安全,尤其是互联网时代,理论上来说系统部署在互联网上时,全球任何地方都可以发起攻击。

传统的架构安全主要依靠防火墙,防火墙最基本的功能就是隔离网络,通过将网络划分成不同的区域,制定出不同区域之间的访问控制策略来控制不同信任程度区域间传送的数据流。

互联网系统的架构安全目前并没有太好的设计手段来实现,更多地是依靠运营商或者云服务商强大的带宽和流量清洗的能力,较少自己来设计和实现。

规模

规模带来复杂度的主要原因就是“量变引起质变”,当数量超过一定的阈值后,复杂度会发生质的变化。常见的规模带来的复杂度有:

1. 功能越来越多,导致系统复杂度指数级上升

2. 数据越来越多,系统复杂度发生质变

与功能类似,系统数据越来越多时,也会由量变带来质变,最近几年火热的“大数据”就是在这种背景下诞生的。

即使我们的数据没有达到大数据规模,数据的增长也可能给系统带来复杂性。最典型的例子莫过于使用关系数据库存储数据,我以 MySQL 为例,MySQL 单表的数据因不同的业务和应用场景会有不同的最优值,但不管怎样都肯定是有一定的限度的,一般推荐在 5000 万行左右。如果因为业务的发展,单表数据达到了 10 亿行,就会产生很多问题,例如:

添加索引会很慢,可能需要几个小时,这几个小时内数据库表是无法插入数据的,相当于业务停机了。

修改表结构和添加索引存在类似的问题,耗时可能会很长。

即使有索引,索引的性能也可能会很低,因为数据量太大。

数据库备份耗时很长。

……

因此,当 MySQL 单表数据量太大时,我们必须考虑将单表拆分为多表,这个拆分过程也会引入更多复杂性,例如:

拆表的规则是什么?

以用户表为例:是按照用户 id 拆分表,还是按照用户注册时间拆表?

拆完表后查询如何处理?

以用户表为例:假设按照用户 id 拆表,当业务需要查询学历为“本科”以上的用户时,要去很多表查询才能得到最终结果,怎么保证性能?

参考来源:极客时间

猜你喜欢

转载自blog.csdn.net/haponchang/article/details/90478969