1. 用户上网形式发展

早期1.0 , 用户通过浏览器输入网址访问网站,这些网站多为静态页面,包含HTML、JavaScript和CSS等内容,没有与用户进行交互,是单向的。

--- config: look: handDrawn theme: forest --- graph LR A[用户] --> B[浏览器] B --> C[服务器<br/>HTML, JS, CSS]

早期服务器与用户仅为单向交互,服务器仅向用户提供内容查阅。进阶到 2.0 阶段时,用户通过浏览器访问服务器,此时服务器引入数据库,用户与服务器实现了双向交互 —— 用户能够对数据进行增删改操作,相关数据会存储至数据库中。

--- config: look: handDrawn theme: forest --- graph LR A[用户] <--> B[浏览器] B <--> C[服务器<br/>HTML, JS, CSS] C <--> D[数据库]

2. 单体架构

这一模式随后演进到单体架构阶段,用户仍通过访问服务器与系统交互。

在服务器中,我们会开发一个项目,项目打包后会生成一个 WAR 包。将这个 WAR 包部署到服务器中,它包含了之前提到的 HTML、JavaScript 和 CSS 样式等内容。

同时,处理用户请求的相关部分,如 Model、View 和 Controller,也都包含在内,这其实就是早期传统 Java Web 开发的内容,本质上是一个 Servlet,也可称为 MVC 模式。后续还会用到 SSH、SSM 等架构。

早期,这类网站访问量较小,一台服务器就能满足需求。这台服务器除了运行应用,还集成了文件服务器和数据库。文件服务器为用户提供文件存储服务,用户头像、上传的文件等都会存放在这里;数据库则用于存储所有用户数据。这就是早期传统 Java Web 采用的单体架构模式。

--- config: look: handDrawn theme: forest --- graph LR A[用户] <--> B(服务器) B <--> C[文件服务器] B <--> D[数据库] subgraph B MVC[Model View Controller] Frontend[HTML<br/>JS<br/>CSS] War[war] end

随着网站业务发展,单台服务器逐渐无法满足用户的访问需求。流量持续增加会导致服务器性能下降,最典型的问题是存储空间不足,图片存储和数据库处理能力都会面临瓶颈。此外,单体架构存在单点故障风险 ——一旦服务器宕机,所有文件和数据库数据均无法访问。

为解决这些问题,可将服务器功能进行拆分:

用户访问的网站服务器仅部署应用程序,用户上传的头像、文件等数据由独立的文件服务器存储,数据库则单独部署到另一台服务器。这种分布式部署模式实现了网站、文件服务、数据库的完全分离,每个节点独立运行并承担特定功能。

--- config: look: handDrawn theme: forest --- graph LR A[用户] <--> B(服务器) B <--> C[文件服务器] B <--> D[数据库] subgraph B MVC[Model View Controller] Frontend[HTML<br/>JS<br/>CSS] War[war] end

分离的优势在于:降低了单点故障的影响(如网站服务器宕机时,文件服务器和数据库仍可正常工作),同时提升了网站的并发处理能力和数据库的存储扩展空间,避免了单体架构对企业业务发展的限制。

3. 引入缓存中间件

随着时间推移,用户请求量成倍增长,企业网站架构面临新问题:数据库压力增大,导致用户访问请求延迟。大量用户查询请求直接落在数据库上,加重了其负担。

为解决这一问题,可引入缓存中间件作为防护机制。用户进行数据查询时,首先会在缓存中间件中查找相关数据。若缓存中有数据,则直接返回;若没有,才会去访问数据库。

--- config: look: handDrawn theme: forest --- graph LR A[负载均衡] <--> B[应用集群<br/>tomcat] B <--> C[文件服务器集群] B <--> D[缓存集群] D <--> E[数据库]

若用户请求变慢,体验变差,就会造成用户流失。因此,引入缓存中间件十分必要。这样,绝大多数查询请求会落在缓存库中,而非直接作用于数据库,有效减轻了数据库压力,提升了用户体验,加快了请求响应速度。

4. 引入集群和负载均衡

当前所有节点均为单节点部署,这在用户流量持续激增时会引发严重问题 —— 一旦某个节点宕机,用户将无法访问服务,对企业而言可能造成毁灭性影响。单节点部署成为系统瓶颈,因此需要引入集群负载均衡机制。

--- config: look: handDrawn theme: forest --- graph LR A[负载均衡] <--> B[应用集群<br/>tomcat] B <--> C[文件服务器集群] B <--> D[缓存集群] D <--> E[数据库]

应用集群通过将相同的项目副本部署到多个服务器上,形成一个整体服务单元,显著提升系统的并发处理能力和可用性。负载均衡技术则通过特定算法(如轮询、权重、最小连接数等)将用户请求均匀分配到集群中的各个节点,避免单点过载。

不仅是应用服务器,文件服务器和缓存中间件也可以构建为集群模式,通过分布式部署消除单点故障,提升资源利用率。例如,缓存集群可分担数据查询压力,文件服务器集群可增强文件存储和访问的稳定性。

此时,系统架构中的应用、文件服务、缓存均以集群形态运行,但数据库仍为单库部署,尚未涉及集群化扩展。

6. 数据库的分库和分表

尽管已引入缓存机制,但仍有部分读操作及全部写操作直接作用于数据库。当网站用户量达到百万甚至千万级别时,数据库负载会成为架构瓶颈。

根据二八原则,约70%-80%的请求为读操作,20%-30%为写操作,读写混合导致数据库压力巨大。

为此,可采用数据库读写分离架构:部署主库(Master)与从库(Slave),主库负责处理所有写操作,从库承接读请求。通过主从数据同步机制,主库定时将数据同步到从库,确保两者数据一致性。

--- config: look: handDrawn theme: forest --- graph LR A[负载均衡] <--> B[应用集群<br/>tomcat] B -- "写入" --> C[主 - 数据库] B <--> D[缓存集群] D -- "读取" --> E[从 - 数据库] E --> D C <--> E

这种设计将读写流量分离:写操作集中在主库,读操作分散到从库(结合缓存进一步分流),显著降低单一数据库的负载压力。

对于用户而言,读写分离是透明的——用户无需感知背后的架构细节,仅需通过统一接口访问,即可获得高效响应。该架构在提升数据库吞吐量的同时,也为后续扩展(如增加从库数量)奠定了基础。

尽管实施了数据库读写分离,但大型网站业务的急剧增长仍可能使数据库不堪重负。

此时,需对数据库进行分库和分表处理。

graph LR A[应用集群<br/>tomcat] -- "写入" --> B[主 - 数据库集群] A <--> C[缓存集群] C -- "读取" --> D[从 - 数据库集群] D --> C B <--> D

我们将主库拆分为多个,例如拆成三个,形成主数据库集群;读取操作的数据则从从数据库集群获取。和之前的主从分离一样,在数据库形成集群后,也需要做好集群间的数据同步工作

分库分表,就是把单台数据库拆分成多个库,同一张表的数据会依据特定算法和规则分散存储在不同数据库中,这种方式构建的就是分布式数据库。

它是数据库拆分的终极手段,通常只有在数据库规模极为庞大时才会考虑采用。一般而言,当单表数据量达到700万至800万条时,就应着手考虑分库分表,因为此时数据库性能会急剧下降。

使用分布式数据库时,要特别注意:一旦进行了分表操作,表中数据就不能再使用自增长主键,而必须采用分布式主键,也就是全局唯一的主键,以确保数据在分布式环境下的唯一性和一致性

7. 引入搜索引擎

随着网站业务持续发展,用户数据检索需求日益多样化,传统数据库的模糊查询已难以满足复杂搜索场景。

此时需引入专业搜索引擎(如 Solr、Elasticsearch)来应对:这类工具专为海量数据的高效检索设计,能支持分词、全文检索、复杂条件查询等功能,使搜索请求无需直达数据库即可完成。

--- config: look: handDrawn theme: forest --- flowchart LR subgraph s1["应用集群"] direction LR T1["tomcat"] T2["tomcat"] T3["tomcat"] end subgraph s2["搜索引擎技术"] direction LR S1["搜索服务1"] S2["搜索服务2"] end A["负载均衡"] --> B{"应用集群"} B --> A B -- tomcat --> C{"搜索引擎技术"} C --> B C -.-> D["数据库"]

在架构层面,搜索引擎承接了原本由数据库处理的搜索流量(图中对应请求链路以虚线示意),既满足了用户对精准、高效搜索的需求,又避免了复杂查询对数据库性能的影响。

这种设计在提升用户检索体验的同时,为数据库构建了额外的流量屏障,尤其适用于千万级以上数据量的搜索场景,从架构层面实现了功能解耦与性能优化。

8. 业务拆分

大型网站业务通常极为复杂,遵循 “分而治之” 的架构设计原则,当业务复杂度达到一定程度时,需将庞大的业务拆解为独立的产品线 —— 每个业务模块剥离为独立的子系统。以电商平台为例。

image-20250507010608213

可将商品、订单、用户、库存等核心模块拆分:

  • 商品子系统:聚焦商品管理,包含商品信息、规格、图片等服务;

  • 订单子系统:专注订单处理,涵盖下单、支付、物流等流程;

  • 此外还有用户中心、库存管理等类似拆分的独立服务。

业务拆分标志着架构进入微服务阶段,此时数据库需要与业务架构同步拆分:

  • 商品相关表(商品主表、规格表、图片表等)整合为独立的商品数据库

  • 订单相关表(订单主表、明细表、状态表等)形成独立的订单数据库

每个微服务的数据库可延续此前的优化策略(如主从分离、分库分表),进一步提升垂直领域的性能与扩展性。拆分后,各子系统及对应的数据库可交由不同团队独立维护,实现 “业务解耦、责任分离”,从根本上解决单体架构中业务复杂度与维护成本激增的问题。

将业务拆分为独立子系统后,各模块可由不同团队独立维护。这种架构对开发、测试、运维提出了更高要求 —— 复杂的业务交互需要多团队协同,各独立系统的整合构成了庞大的分布式架构体系。

优势:

  • 负载分散:业务垂直拆分后,单个子系统的功能边界清晰,避免了单体架构中多模块竞争资源的问题,系统整体负载能力显著提升;

  • 分工明确:开发团队可聚焦特定业务领域,降低模块间耦合度,提升开发效率与代码可维护性;

  • 灵活扩展:各子系统可根据自身业务特点独立优化(如数据库分库分表、集群扩容),避免 “牵一发而动全身”。

挑战:

  • 复杂度上升:分布式架构下,服务间调用、数据同步、版本管理等逻辑变得复杂,代码维护难度显著增加;

  • 运维成本高:多套独立运行的子系统需要统一监控、日志管理和故障排查,对运维体系的完整性提出更高要求;

  • 分布式事务问题:当用户请求涉及多个子系统(如电商场景中 “下单 - 扣库存 - 更新用户积分”),需确保跨服务操作的原子性。若某一环节失败,需通过事务补偿机制(如 TCC、Saga 模式)回滚所有操作,避免数据不一致。

分布式事务是微服务架构中无法回避的核心问题,其解决方案直接影响系统的可靠性和数据一致性,是架构设计时必须重点攻克的难点。

那么,在一个系统里面,我们往往会有一些非常通用的一个接口。

image-20250507010932000

用户的话其实是可以在。访问我们的一个每个子系统的时候去调用一些相应的一些公用的服务,比方说,我们有短信邮件推送。

image-20250507011006083

对于这些公共的服务资源的话,其实我们在服务和服务之间啊,进行相互通用的时候,我们都会使用到一些相应的分布式的服务中间件来实现服务间的协调。 比如说:

  • 分布式锁:当多个服务实例需要互斥访问共享资源(如库存扣减、订单编号生成)时,通过分布式锁(如基于 Redis 的SETNX、ZooKeeper 的临时有序节点)确保同一时刻仅有一个实例操作资源,避免数据不一致。

  • 服务注册与发现(如 Nacos、Eureka):实现服务节点的动态管理,确保各子系统能高效发现并调用公共服务接口,降低服务间耦合。

  • 分布式配置中心(如 Apollo、Nacos):统一管理各子系统的配置参数,支持动态更新,避免重复配置与版本混乱。

在分布式系统中,分布式会话管理是基础支撑模块。当系统间进行交互时,通常会采用异步通信机制,通过引入消息队列(MQ)处理异步请求,以此解耦服务间的直接依赖并削峰填谷。

image-20250507011514308

此外,分布式锁、分布式事务、分布式会话等技术,都是分布式系统中必须解决的核心问题。

image-20250507011533959

9. 架构优化

当网站架构演进至此,已能应对绝大多数技术挑战,但仍需持续优化——例如通过日志分析对数据库性能进行调优,这要求架构师具备扎实的系统调优能力。

值得注意的是,整个架构演变过程并非严格遵循固定顺序,而是应紧密围绕企业当前业务需求动态调整。前文所述的技术引入路径,仅为多数企业实践中常见的演进逻辑,实际应用时需根据场景灵活适配。

至此,我们完整梳理了从单体架构到分布式微服务架构的核心演变过程,核心在于根据业务规模与复杂度,逐步引入分层解耦、分布式协作、异步处理等机制,最终构建出高可用、可扩展的技术体系。