当前位置: 首页 > news >正文

一个单机架构的14次演进之路

1. 概述:从一百到千万级并发的蜕变

在互联网诞生之初,软件架构远没有今天这般复杂。当一个网站的日活用户从一百人增长到千万级,其后端架构会经历一场翻天覆地的变革。本文将以淘宝为例(仅为便于说明演进过程,并非真实淘宝技术路径),带你走完这趟从“石器时代”到“云原生时代”的演进之旅。

基本概念扫盲
在开始演进之前,我们需要先统一几个核心认知:

  • 分布式:系统中的不同模块部署在不同的服务器上。

  • 集群:多台服务器协同工作,对外表现为一个整体。

  • 高可用:部分节点失效时,系统依然能提供服务。

  • 负载均衡:将请求均匀分发到多个节点上。

  • 反向代理:代理服务器对外暴露,转发请求给内部服务器。

2. 第一阶段:单机架构(All in One)

示意图描述:一台服务器,框内同时运行着Tomcat(应用服务器)和Database(数据库)。浏览器通过DNS解析域名后,直接访问这台服务器的IP。

📜 阶段背景
在网站最初期,用户量极少,业务逻辑简单。为了快速开发和上线,我们将应用程序(Tomcat)和数据库(MySQL)部署在同一台服务器上。

🔍 面临的问题

  • 资源竞争:Tomcat和数据库会互相竞争CPU、内存和I/O资源。

  • 性能瓶颈:单机性能有限,随着用户增长,响应逐渐变慢。

  • 无高可用:服务器宕机,服务全挂。

3. 第二次演进:应用与数据分离

示意图描述:两台服务器,通过网络连接。左边是应用服务器(Tomcat),右边是数据库服务器(MySQL)。

📜 演进动机
Tomcat和数据库抢夺资源导致双方都跑不快。最简单的解决办法就是:各自独占一台服务器

✅ 解决的问题

  • 解除了资源竞争,应用服务器专注于计算,数据库专注于存储和查询,性能均得到显著提升。

🔍 新的问题
随着用户数进一步增长,数据库的连接数和读写压力越来越大,数据库成为新的瓶颈

4. 第三次演进:引入本地缓存与分布式缓存

示意图描述:应用服务器旁边增加“本地缓存”图标;同时在应用服务器集群之外,增加一个独立的“分布式缓存集群(如Redis)”图标。

📜 演进动机
数据库的访问速度受限于磁盘I/O。80%的业务请求集中在20%的数据上(如热门商品信息)。如果能将这些热点数据存放在内存中,就能极大减轻数据库压力。

✅ 解决方案

  • 本地缓存(如Caffeine/Guava Cache):在应用服务器内存中缓存热点数据,速度最快,但数据不一致风险高。

  • 分布式缓存(如Redis/Memcached):独立的缓存层,供所有应用服务器共享,有效降低数据库读压力。

🔍 引入的新挑战

  • 缓存击穿/穿透/雪崩:需要制定缓存预热、永不过期、布隆过滤器等策略。

  • 缓存一致性:数据库更新后,如何保证缓存失效或更新。

5. 第四次演进:引入反向代理与负载均衡

示意图描述:用户请求不再直达Tomcat,而是先经过一个“反向代理层(Nginx)”。Nginx背后挂着多个Tomcat服务器。

📜 演进动机
单机Tomcat的并发处理能力有限(假设100-200),缓存只能减缓数据库压力,但无法解决应用服务器自身的处理能力瓶颈。用户请求一多,Tomcat线程池被占满,响应变慢。

✅ 解决方案
引入反向代理服务器(如Nginx),将请求分发给多个Tomcat实例。

  • 水平扩展:Tomcat从1台变成多台。

  • 高性能:Nginx处理静态文件的能力极强,也可以直接缓存静态资源。

🔍 引入的新挑战

  • Session共享问题:用户第一次请求分配到Tomcat A登录,第二次请求分配到Tomcat B,B没有Session信息,导致用户需要重新登录。解决方案:Session集中存储(如存到Redis)。

6. 第五次演进:数据库读写分离

示意图描述:数据库拆分为“主库(写)”和“从库(读)”。数据之间存在一条箭头指向从库表示“主从复制”。应用服务器根据请求类型(读/写)路由到不同的库。

📜 演进动机
随着并发量增大,数据库虽然有了缓存保护,但写操作(更新、插入、删除)和复杂读操作依然会争夺数据库锁资源。此时,大部分业务场景都是读多写少

✅ 解决方案

  • 主从复制:主库负责写操作,实时将binlog同步给从库。

  • 读写分离:应用或数据库中间件(如Mycat, ShardingSphere)将读请求分发到从库,写请求发到主库。

🔍 引入的新挑战

  • 数据同步延迟:刚写入主库的数据,立即去从库读可能读不到。通常通过强制读主库或缓存解决。

  • 数据一致性问题

7. 第六次演进:数据库按业务分库

示意图描述:原先的一个数据库被拆分为“用户库”、“订单库”、“商品库”等多个物理数据库。

📜 演进动机
随着业务发展,数据库中包含了用户、商品、订单、评价等各种业务表。这些表互相竞争数据库的I/O、CPU和连接数。例如,一次复杂的报表统计查询可能会拖慢整个订单交易流程。

✅ 解决方案
将不同业务的数据拆分到不同的物理数据库中,实现业务层面的解耦

🔍 引入的新挑战

  • 跨库Join问题:无法再进行数据库层面的关联查询,需要在应用层或通过大宽表解决。

8. 第七次演进:大表水平分表(分库分表)

示意图描述:一个“订单库”内部,不再是一张订单表,而是拆分为“订单_0”、“订单_1”...“订单_9”共10张物理表,通过取模或范围划分数据。

📜 演进动机
即使是一个单独的订单库,单表数据量过大(如超过千万级别),索引维护成本变高,B+树层级变深,写入和查询性能急剧下降。

✅ 解决方案

  • 水平拆分:按照某个字段(如用户ID、订单ID)的Hash值,将数据分散到多张结构相同的表中。

  • 数据库中间件:引入如Mycat、ShardingSphere-JDBC等中间件,屏蔽底层分库分表的复杂性,让应用层像操作单库一样。

🔍 引入的新挑战

  • 跨节点聚合与排序:分页、排序、聚合运算变得复杂。

  • 分布式ID:数据库自增ID不再适用,需要分布式ID生成器(如雪花算法)。

9. 第八次演进:引入LVS/F5使Nginx高可用

示意图描述:最上层是“DNS”指向一个“虚拟IP(VIP)”。VIP由两台“LVS(主备)”持有,通过Keepalived心跳检测。LVS下层挂着多个“Nginx”,Nginx下层是Tomcat集群。

📜 演进动机
Nginx虽然性能很高,但它本身成了整个系统的单点故障(SPOF)。如果Nginx挂了,整个应用都不可访问。此外,单台Nginx能支撑的并发也有限(约5万),当Tomcat节点扩展到几百上千台时,Nginx也会成为瓶颈。

✅ 解决方案

  • 四层负载均衡(LVS/F5):引入工作在操作系统内核态的LVS(软件)或F5(硬件)。它们性能极高(可支撑几十万并发),转发效率远高于Nginx。

  • 高可用(Keepalived):通过Keepalived实现主备LVS,自动故障转移。

10. 第九次演进:DNS轮询实现多机房容灾

示意图描述:DNS解析一个域名,返回多个IP地址,分别对应“华北机房”、“华东机房”、“华南机房”的LVS入口。

📜 演进动机
当系统规模达到千万级甚至亿级用户时,单一机房存在物理风险(如光纤被挖断、电力故障、自然灾害)。同时,跨地域的访问延迟也影响用户体验。

✅ 解决方案

  • DNS轮询/智能DNS:在DNS层面配置同一个域名对应多个机房的IP入口,实现机房间的流量分发。

  • 多活/灾备:系统具备机房级容灾能力。

🔍 引入的新挑战

  • 跨机房数据同步:数据库跨机房同步延迟极大,通常只能做到最终一致性。

11. 第十次演进:引入NoSQL和搜索引擎

示意图描述:在原有数据库集群之外,增加了“Elasticsearch集群”、“HBase集群”、“HDFS集群”等,它们与数据库之间通过数据同步通道(如Canal)连接。

📜 演进动机
关系型数据库(MySQL)在处理模糊搜索、海量数据存储、多维度复杂聚合分析时力不从心。

✅ 解决方案

  • 引入搜索引擎(Elasticsearch):解决商品搜索、日志检索问题。

  • 引入NoSQL(HBase, Redis, MongoDB):解决海量key-value存储、高并发读写场景。

  • 引入大数据平台(Hadoop, Hive):解决离线统计、数据分析问题。

12. 第十一次演进:应用拆分(分布式/微服务雏形)

示意图描述:原有的“大WAR包”拆分为多个独立的Web应用:“前端流程中心”、“交易中心”、“用户中心”、“订单中心”。各个中心有自己的数据库,中心之间通过HTTP/RPC通信。

📜 演进动机
随着业务越来越复杂,代码库变得庞大无比。几十甚至上百人维护同一个代码库,合并代码冲突频繁,每次发布都牵一发而动全身。

✅ 解决方案

  • 垂直拆分:按业务边界(Domain)将大单体应用拆分为多个独立的小应用。

  • 独立数据库:每个小应用独享自己的数据库(或独占数据库中的表)。

13. 第十二次演进:微服务化与抽离通用能力

示意图描述:各个业务应用中公用的“用户服务”、“短信服务”、“支付服务”被独立出来,形成一个个微服务。业务应用通过轻量级协议调用这些服务。此时引入了“注册中心(如Eureka/Zookeeper)”和“RPC框架(如Dubbo/Spring Cloud)”来管理服务调用。

📜 演进动机
拆分应用后,发现“用户鉴权”、“短信发送”、“支付”等功能在多个应用中重复实现,代码冗余。每次修改通用逻辑,所有相关应用都要重新发布。

✅ 解决方案

  • 微服务拆分:将高复用的功能彻底独立成服务。

  • 服务注册与发现:服务启动时将自己注册到注册中心,消费者通过注册中心找到提供者。

  • 配置中心:集中管理配置(如Apollo, Nacos)。

  • RPC框架:高效的内部服务调用(如Dubbo, gRPC)。

14. 第十三次演进:引入ESB服务总线(SOA阶段)

示意图描述:在微服务之间,插入了一层“ESB(企业服务总线)”。不同协议(HTTP, RPC, JMS)的服务都接入ESB,由ESB进行协议转换、路由和编排。

📜 演进动机
随着服务越来越多,服务间的通信协议不统一(有的WebService,有的HTTP,有的RPC),接口混乱。同时,企业希望对服务进行统一的治理,如流量控制、协议转换、服务编排。

✅ 解决方案
引入ESB(企业服务总线),作为沟通各个异构系统的桥梁,屏蔽复杂性。这也就是经典的SOA架构思想。

🔍 引入的新挑战
ESB本身可能变成中心化的瓶颈单点故障

15. 第十四次演进:容器化与云原生

示意图描述:底层不再是物理机或虚拟机,而是“容器化(Docker)”。容器由“Kubernetes”进行编排管理。Pod中运行着微服务,结合“服务网格(Service Mesh)”边车代理,以及底层的“云平台(IaaS/PaaS)”支撑。

📜 演进动机
传统部署方式下,运维复杂的服务依赖、环境不一致、弹性伸缩慢。微服务架构虽好,但带来了运维和治理的巨大复杂性。

✅ 终极解决方案

  • 容器化(Docker):提供标准化的运行环境,实现“一次构建,到处运行”。

  • 容器编排(Kubernetes):自动化容器的部署、伸缩、服务发现和负载均衡。

  • 服务网格(Service Mesh,如Istio):将服务治理(熔断、限流、追踪)能力下沉到基础设施,与应用代码解耦。

  • 云平台:最终,系统运行在云上,充分利用弹性计算、对象存储、云数据库等能力。

16. 演进之路总结表

演进次数核心演进动作解决的核心问题引入的关键技术
起点单机架构快速上线,简单LAMP, Tomcat+MySQL
1应用与数据分离资源竞争两台独立服务器
2引入缓存数据库读压力大Redis, Memcached
3负载均衡应用服务器处理能力不足Nginx, 反向代理
4读写分离数据库锁竞争,高并发读Mycat, 主从复制
5业务分库业务间资源争抢垂直拆分
6分库分表单表数据量过大ShardingSphere, 雪花算法
7LVS多级负载均衡入口层单点故障与性能瓶颈LVS, Keepalived, F5
8多机房部署机房级容灾, 就近访问DNS轮询, 异地多活
9引入NoSQL/ES复杂搜索与海量存储Elasticsearch, HBase
10应用拆分代码臃肿,团队协作困难分布式架构
11微服务拆分功能复用,独立部署Spring Cloud, Dubbo
12ESB服务总线异构系统整合,服务治理SOA, 企业服务总线
13容器化与云原生环境一致性,弹性伸缩,运维复杂Docker, Kubernetes

17. 结语:架构设计的哲学

回顾这14次演进,我们不难发现一些普适的架构设计原则

  1. 合适的才是最好的:不要为了微服务而微服务。初创公司单机架构足以应对,强行上微服务只会拖慢开发效率。

  2. 先利旧再升级:遇到性能问题,优先考虑优化代码和SQL,其次才是增加缓存,最后才是重构架构。

  3. 拆分是核心:架构演进的历史,就是一部拆分的艺术史。从横向分层(OSI七层模型),到纵向拆分(业务分库),再到代码拆分(微服务),甚至逻辑与基础设施拆分(Service Mesh)。

  4. 权衡的艺术:高性能与高可用之间需要权衡(CAP理论),一致性与性能之间需要权衡。

http://www.jsqmd.com/news/398785/

相关文章:

  • 基于占空比优化的异步电机模型预测转矩控制探索
  • 如何保障消息中间件 100% 消息投递成功?如何保证消息幂等性?
  • 学术江湖的“智能剑客”:书匠策AI如何用六大绝技重塑论文写作规则
  • type、__new__与一次对对象生命周期的全面接管
  • Java 8 新特性全景指南:从入门到实战
  • 低代码写游戏:我用积木块拼出俄罗斯方块,然后在“旋转”函数前跪了一夜
  • 详解自动化安全扫描:用 OWASP ZAP 与 Nuclei 体检你的 CSP/MIME 配置
  • 大规模资产扫描性能调优:并发、流控与资源管理的实战艺术
  • 基于 Nuclei 的漏洞扫描实践:YAML 模板语法与高级工作流
  • 在 React / Vue 里安全插入动态脚本:一文读懂 nonce 的正确用法
  • vue基于python的计算机类专业考研择校推荐系统开发
  • vue基于python的高考调档线查询系统的设计与实现
  • 【算法提高篇】(七)权值线段树 + 离散化:值域爆炸?这波操作直接拿捏!
  • 纠结,有必要和领导发拜年短信吗?
  • 计算机毕业设计|基于springboot + vue社区智慧消防管理系统(源码+数据库+文档)
  • postgresql跨数据库建view
  • 物理理论终极全景图
  • 覆盖率的陷阱:100% 代码覆盖率不等于没有 Bug
  • 为什么 MySQL 不推荐默认值为 null ?
  • Text1:Vscode ESP32S3 IDF WIFI OTA升级
  • 2026别错过!深得人心的降AI率网站 —— 千笔AI
  • 对比一圈后 10个降AI率平台深度测评与推荐——专科生必看
  • 让大模型学会“教人做事“:How2Everything从98万网页中挖出35万份操作指南
  • 如何选择可靠的手表维修点?2026年广州贝伦斯维修服务推荐与评测 - 十大品牌推荐
  • 用数据说话 8个AI论文工具测评:自考毕业论文写作必备神器
  • 世界各大洲河流分布图
  • Qwen3-ASR-1.7B对比测试:复杂环境下的语音识别王者
  • 奇异搞笑
  • 2026年手表维修中心推荐:多场景服务评测,针对走时不准与保养难题详解 - 十大品牌推荐
  • 【C++】=自动生成比较操作符