编辑
2026-05-09
记录知识
0

目录

前言
低成本:架构设计的附加约束
安全:功能安全与架构安全
规模:量变引起质变
Python 示例:功能数量与复杂度关系
总结
参考

前言

除了高性能和高可用之外,架构设计还面临三个重要的复杂度来源:低成本、安全和规模。这三者各有其独特的挑战和应对策略。理解它们的本质,有助于架构师在设计中做出更明智的决策。

低成本:架构设计的附加约束

当架构方案只涉及几台或十几台服务器时,成本通常不是重点关注目标。但如果架构方案涉及几百上千甚至上万台服务器,成本就会变成一个非常重要的考虑点。

例如:A 方案需要 10000 台机器,B 方案只需要 8000 台机器,单从比例来看节省了 20%,但从数量来看,B 方案能节省 2000 台机器。按照每台机器每年约 2 万元成本计算,一年就能节省 4000 万元——这笔钱足以给 100 人的团队每人发 40 万元奖金。

低成本的本质:设计"高性能"和"高可用"的架构时,通用手段都是增加更多服务器;而低成本正好与此相反,需要减少服务器数量才能达成。因此,低成本本质上是与高性能和高可用冲突的。

基于这个原因,低成本很多时候不是架构设计的首要目标,而是附加约束。通常的流程是:先根据高性能、高可用的要求设计架构,然后评估是否满足成本目标,如果不行则重新设计。

低成本给架构设计带来的主要复杂度体现在:往往只有"创新"才能达到低成本目标。这里的"创新"包括两种形式:

引入新技术

  • NoSQL(Memcache、Redis 等)——解决关系型数据库无法应对高并发访问的问题
  • 全文搜索引擎(Sphinx、Elasticsearch、Solr)——解决关系型数据库 like 搜索的低效问题
  • Hadoop——解决传统文件系统无法应对海量数据存储和计算的问题

创造新技术

  • Facebook 为解决 PHP 的低效问题,开发了 HipHop PHP(将 PHP 翻译为 C++)和 HHVM(将 PHP 翻译为字节码)
  • 新浪微博将传统的 Redis/MC + MySQL 扩展为 Redis/MC + SSD Cache + MySQL 方式
  • Linkedin 为处理每天 5 千亿的事件,开发了高效的 Kafka 消息系统

引入新技术的主要复杂度在于需要去熟悉新技术,并且将新技术与已有技术结合起来。创造新技术则复杂度更高,需要自己去创造全新的理念和技术,且与旧技术相比需要有质的飞跃。

安全:功能安全与架构安全

安全本身是一个庞大而又复杂的技术领域,一旦出问题,对业务和企业形象影响非常大。

从技术角度来讲,安全可以分为两类:

1. 功能安全("防小偷")

常见的功能安全攻击包括 XSS 攻击、CSRF 攻击、SQL 注入、密码破解等。这些攻击的本质是因为系统实现有漏洞,黑客利用漏洞潜入系统进行破坏或盗取。

功能安全更多是和具体的编码相关,与架构关系不大。虽然现在很多开发框架都内嵌了常见的安全功能,能够减少安全相关功能的重复开发,但框架只能预防常见的安全漏洞,无法预知新的安全问题,而且框架本身也可能存在漏洞(如 Apache Struts2 多次爆出的远程代码执行高危漏洞)。

功能安全是一个"攻"与"防"的矛盾,只能在这种攻防大战中逐步完善,不可能在系统架构设计时一劳永逸地解决。

2. 架构安全("防强盗")

如果说功能安全是"防小偷",那么架构安全就是"防强盗"。强盗会直接用大锤将门砸开,或者用炸药将围墙炸倒——很多时候就是故意搞破坏,对系统的影响要大得多。

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

但在互联网领域,防火墙的应用场景并不多。原因在于:

  • 互联网业务具有海量用户访问和高并发的特点,防火墙性能不足以支撑
  • 互联网领域的 DDoS 攻击规模很大,2016 年知名安全研究人员布莱恩·克莱布斯的安全博客网站遭遇的 DDoS 攻击,带宽达 665Gbps
  • 即使花几百万元买防火墙设备,当出口带宽被耗尽时,业务在用户看来还是不可用的

基于上述原因,互联网系统的架构安全更多依靠运营商或云服务商强大的带宽和流量清洗能力,较少自己来设计和实现。

规模:量变引起质变

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

1. 功能复杂度:指数级增长

以一个简单的抽象模型计算:假设系统间的功能都是两两相关的,系统的复杂度 = 功能数量 + 功能之间的连接数量。

连接数量的计算公式:n × (n-1) / 2

3 个功能的系统复杂度 = 3 + 3 = 6 8 个功能的系统复杂度 = 8 + 28 = 36

复杂度不是线性增长,而是呈指数级增长,主要原因在于随着系统功能数量增多,功能之间的连接呈指数级增长。

2. 数据复杂度:质变临界点

即使数据没有达到大数据规模,增长也可能给系统带来复杂性。以 MySQL 为例,单表数据量一般在 5000 万行左右。如果单表数据达到 10 亿行,会产生很多问题:

  • 添加索引会很慢,可能需要几个小时,期间数据库表无法插入数据
  • 修改表结构和添加索引耗时很长
  • 即使有索引,性能也会很低
  • 数据库备份耗时很长

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

Python 示例:功能数量与复杂度关系

下面通过 Python 示例演示功能数量与复杂度的关系:

python
""" 功能数量与复杂度关系示例 用于演示 n 个功能之间的连接数如何呈指数级增长 复杂度公式:complexity = n + n*(n-1)/2 = n*(n+1)/2 - n: 功能数量 - n*(n-1)/2: 功能间的两两连接数 """ def calculate_complexity(n: int) -> tuple[int, int, int]: """ 计算 n 个功能的复杂度 Returns: (功能数, 连接数, 总复杂度) """ connections = n * (n - 1) // 2 total = n + connections return n, connections, total def print_complexity_table(): """打印复杂度表格""" print("功能数量 | 连接数 | 总复杂度 | 增长趋势") print("-" * 50) prev_total = 0 for n in [3, 5, 8, 10, 15, 20, 30, 50]: n_count, connections, total = calculate_complexity(n) growth = f"+{total - prev_total}" if prev_total > 0 else "" print(f"{n_count:^8} | {connections:^6} | {total:^7} | {growth}") prev_total = total print("\n结论:") print("- 功能数从 3 增长到 50(增长约 17 倍)") print("- 连接数从 3 增长到 1225(增长约 408 倍)") print("- 总复杂度从 6 增长到 1275(增长约 213 倍)") print("- 连接数的增长速度远超功能数,这就是'量变引起质变'") def demo_connection_growth(): """演示连接数的指数级增长""" print("\n=== 连接数增长演示 ===") # 计算不同功能数量下的连接数 test_sizes = [5, 10, 20, 50, 100] print("\n功能数 | 连接数 | 连接数/功能数比值") print("-" * 40) for size in test_sizes: connections = size * (size - 1) // 2 ratio = connections / size print(f"{size:^6} | {connections:^6} | {ratio:^10.1f}") if __name__ == "__main__": print_complexity_table() demo_connection_growth() # 预期输出: # 功能数量 | 连接数 | 总复杂度 | 增长趋势 # ------------------------------------------------- # 3 | 3 | 6 | # 5 | 10 | 15 | +9 # 8 | 28 | 36 | +21 # 10 | 45 | 55 | +19 # 15 | 105 | 120 | +65 # 20 | 190 | 210 | +90 # 30 | 435 | 465 | +255 # 50 | 1225 | 1275 | +810 # # 结论: # - 功能数从 3 增长到 50(增长约 17 倍) # - 连接数从 3 增长到 1225(增长约 408 倍) # - 总复杂度从 6 增长到 1275(增长约 213 倍) # - 连接数的增长速度远超功能数,这就是'量变引起质变'

运行结果:

功能数量 | 连接数 | 总复杂度 | 增长趋势 ------------------------------------------------- 3 | 3 | 6 | 5 | 10 | 15 | +9 8 | 28 | 36 | +21 10 | 45 | 55 | +19 15 | 105 | 120 | +65 20 | 190 | 210 | +90 30 | 435 | 465 | +255 50 | 1225 | 1275 | +810 结论: - 功能数从 3 增长到 50(增长约 17 倍) - 连接数从 3 增长到 1225(增长约 408 倍) - 总复杂度从 6 增长到 1275(增长约 213 倍) - 连接数的增长速度远超功能数,这就是'量变引起质变' === 连接数增长演示 === 功能数 | 连接数 | 连接数/功能数比值 ---------------------------------------- 5 | 10 | 2.0 10 | 45 | 4.5 20 | 190 | 9.5 50 | 1225 | 24.5 100 | 4950 | 49.5

这个示例清晰地展示了功能数量增长带来的复杂度爆炸:当功能数从 10 增长到 100(10 倍),连接数却从 45 增长到 4950(110 倍)。这就是架构设计中"量变引起质变"的典型现象。

总结

  • 低成本本质上是与高性能和高可用冲突的,是架构设计的附加约束而非首要目标
  • 低成本的主要实现手段是"创新",包括引入新技术(NoSQL、搜索引擎等)或创造新技术(Facebook HipHop、Linkedin Kafka 等)
  • 功能安全(防小偷)主要与编码相关,是逐步完善的过程,无法一劳永逸解决
  • 架构安全(防强盗)针对蓄意破坏,防火墙在互联网场景下因性能和成本问题应用不多,互联网更多依靠运营商或云服务商的流量清洗能力
  • 规模带来复杂度的核心原因是"量变引起质变"
  • 功能复杂度呈指数级增长:n 个功能带来 n×(n-1)/2 个连接,总复杂度 = n×(n+1)/2
  • MySQL 单表数据量建议控制在 5000 万行以内,超过需要拆表
  • 数据量过大带来的问题包括:添加索引慢、修改表结构慢、索引性能低、备份耗时长等
  • 拆表会引入新的复杂性:拆表规则选择、查询如何处理等问题
  • 规模问题需要与高性能、高可用、高扩展、高伸缩性统一考虑,这是架构设计中的综合挑战

参考

  • 软件架构基础