电商平台系统架构演进之路
直接扔上来一张系统架构图,刚接触企业项目的人可能会有点懵,觉得怎么这么复杂。但其实这一切都是一步步"被逼"演化出来的。今天,我想从最开始的那个纯粹的单体应用说起,带大家走一遍电商系统架构的完整演进之路。
1. 最初的"大泥球"(单体应用)
这就是我们刚接触编程时写的那些 Java 或 C++ 应用,特点就是All in One。用户交互界面可能是 JavaFx 写的,业务逻辑、数据访问逻辑全都在一个进程里,数据库也是本地的(如文件型数据库)。
本质上,软件开发的核心从来没变过:把用户的输入经过逻辑处理后写入数据库,再把数据库里的数据经过加工后展示给用户。
但单体应用存在两个致命弱点:
- 数据封闭:数据都是本地的,虽然"安全"但无法共享,每个人只能看到自己的数据,无法与别人产生链接。
- 性能瓶颈:所有东西跑在一台机器上,受限于单台机器的 CPU 和内存资源。
2. 第一次进化:数据库分离与 C/S 架构
为了让数据集中且共享,我们将数据库从本地抽离出来,独立部署在一台服务器上,应用程序通过网络连接数据库。
此时,用户设备上只需要安装逻辑代码,不再需要内置数据库。这既实现了数据实时交互,又节省了本地存储空间。
但问题接踵而至:业务功能越来越丰富后,用户需要下载的安装包依然庞大,而且逻辑运算消耗本地 CPU,低配设备容易卡顿甚至闪退。
3. 第二次进化:前后端分离与"瘦客户端"
经过分析,我们发现核心计算逻辑完全可以抽取成公共服务,跑在提供商的云端机器上。用户的设备只需要发送 HTTP 请求获取数据并渲染展示即可。
这便是前后端分离架构的雏形,也就是我们常说的 C/S 架构。
注:B/S 是 C/S 的一种特殊形式,只是 Client 统一变成了浏览器(Browser)。B/S 架构有更好的传播性,因为用户不用额外安装任何客户端软件,打开网页就能用。
4. 第三次进化:分层架构(职责分离)
随着业务逻辑越来越复杂,为了让代码低耦合、易维护,后端内部开始实行分层架构:
- Controller(控制层):负责接收请求、参数校验与响应封装。
- Service(业务层):负责承载核心的业务逻辑,如下单扣库存。
- DAO(数据层):负责与数据库进行 CRUD 交互。
当业务进一步膨胀时,我们还会按照业务领域(如订单、用户、商品)将服务拆分成多个独立的业务模块,模块间通过组合调用。用通俗的话讲就是“专业的事找专业的人”,避免同一个逻辑在代码里到处复制粘贴。
5. 第四次进化:物理解耦与 SOA(面向服务架构)
此时,虽然逻辑上已经分开了,但所有服务依然部署在同一台机器(同一个 JVM 进程)里。
这就有一个致命隐患:物理上高度耦合。
举个例子:订单服务和购物车服务都跑在同一个 JVM 里,订单的重要性远高于购物车。如果双11大促时,大批用户疯狂加购物车导致 CPU 飙高,进而拖垮了整个 JVM 进程,那么下单服务也会跟着一起挂——这将是巨大的经济损失。
因此,我们必须让服务在物理进程上也彻底隔离。每个服务独立部署、独立运行,通过网络(如 RPC 或 HTTP)进行通信。
这便进入了SOA(面向服务架构)阶段,也就是我们常说的"服务化"。SOA 的核心思想是将业务能力封装成标准的服务,服务之间通过定义良好的接口进行通信,强调服务的松耦合和可复用。
6. 第五次进化:微服务架构
然而,SOA 虽然解决了物理隔离,但服务间的调用方式却倒退了。
在单体时代,所有代码都在同一个 JVM 进程里,服务间调用仅仅是本地方法调用(xxxService.method()),就像在同一个房间里喊一嗓子,随叫随到。物理隔离之后,调用变成了跨网络的远程调用,就像两地打电话,要拨号、要等待、还可能断线——复杂性骤然增加。
传统的 SOA 解决方案是把所有服务调用都发送到ESB(企业服务总线),由 ESB 负责协议转换、消息路由、数据格式编排。但这存在一个严重的弊端:ESB 本身成了单点和整个系统的性能瓶颈,而且服务之间依然通过 ESB 产生隐性耦合。
伴随着Docker 容器化技术和DevOps 理念的普及,服务的独立部署与弹性扩缩容成为可能。业界开始反思:既然 ESB 这么重,为什么不干脆去掉它,把治理能力下沉到基础设施中呢?
于是,微服务架构应运而生。
它坚决抛弃了 ESB 这个"重装坦克",转而采用轻量级通讯协议(如 HTTP/REST 或 gRPC),并把 ESB 的功能打散到了各个基础设施中:
- 注册中心做服务发现(代替 ESB 的路由)
- API 网关做对外入口(代替 ESB 的协议转换)
- 熔断器做容错(代替 ESB 的异常处理)
下面是 SOA 与微服务架构的对比表:
| 维度 | 传统 SOA | 微服务 |
|---|---|---|
| 服务粒度 | 较粗,通常是整个业务域(如"ERP系统") | 极细,精确到单个业务能力(如"扣减库存") |
| 通讯协议 | 重,通常依赖 SOAP、WS-* 等复杂协议 | 轻,偏爱 HTTP/REST、gRPC、Dubbo |
| 数据治理 | 中央集权(ESB 统一管理) | 去中心化(每个服务独立管理自己的数据库) |
| 部署方式 | 大多部署在几台大型应用服务器上 | 完全独立部署,每个服务拥有独立进程甚至独立容器 |
进阶思考:微服务强调"去中心化数据管理",即每个服务拥有独立数据库。这彻底解决了数据库耦合问题,但也因此引入了分布式事务和最终一致性的新挑战——这将是架构演进绕不开的下一道坎。欢迎大家评论区聊聊自己对分布式的理解。
7. 第六次进化:BFF——给前端"减负"
微服务拆分得很细之后(订单、商品、库存、优惠券都独立了),后端确实清爽了。但前端的开发却变得极其痛苦。
场景痛点:电商 APP 首页需要同时展示"用户信息 + 商品图片 + 优惠券 + 库存状态 + 活动倒计时"。如果前端直接去调 5 个微服务,就要发 5 次网络请求,在弱网环境下 APP 会卡死。而且,前端开发人员需要熟悉每个微服务的接口参数,沟通成本极高。
架构演进:此时,我们在微服务和前端之间加一层"胶水层"——BFF(Backend for Frontend,服务于前端的后端)。
它的职责是“定制化聚合”:根据前端(APP 端、PC 端、小程序端)的不同需求,把后台多个微服务的数据抓过来,组装成一个刚好满足前端展示的大 JSON 返回回去。
这样一来,前端只需要发1 次请求,BFF 帮它搞定所有数据拼装。前端同学再也不需要关心订单接口怎么调、商品接口什么参数,只需要跟 BFF 对接即可。
8. 第七次进化:服务网格——让治理能力"下沉"
微服务通过注册中心、网关和熔断器解决了服务拆分后的治理问题。但渐渐地,我们发现这些治理能力(如熔断、重试、超时)的代码,依然需要耦合在业务应用里(比如引入 Spring Cloud Netflix 或 Dubbo 的 SDK)。
当公司有 Java、Go、Python 多个技术栈时,每个语言都要维护一套治理逻辑,成本极高——改一个超时配置,三个语言的团队要分别改、分别测、分别发布。
于是,服务网格(Service Mesh)应运而生。
它将微服务的业务逻辑和网络通信治理彻底分离:业务代码只负责处理订单和扣库存,而所有的限流、熔断、鉴权、监控,统统交由部署在业务 Pod 旁的Sidecar(边车代理)处理。
这让业务开发人员可以完全专注于业务,而不必关心底层网络有多复杂。升级熔断策略时,只需要升级 Sidecar,业务代码一行都不用改。
9. 第八次进化:大数据层——让数据"活"起来
微服务、BFF、服务网格都搞定了,系统终于稳定地跑起来了。
但跑起来只是及格。电商平台真正的核心竞争力在于“懂用户”和“快决策”。
然而,我们很快发现一个尴尬的事实:交易数据越积越多,却是一堆"沉睡的金矿"。
- 运营想看一眼实时 GMV(商品交易总额),不敢直连订单库——一个复杂的
group by统计查询就能把数据库 CPU 打满,直接影响用户下单。 - 产品想分析**"加购但未支付"的用户画像**,发现数据散落在订单、商品、用户、优惠券十几个库里,根本无法关联查询。
- 用户搜**“蓝牙耳机”** ,如果只是数据库的
LIKE模糊匹配,搜出来的结果又慢又不准。
这时候,我们必须在业务交易系统(OLTP)之外,单独构建一套大数据系统。两者的分工非常明确:
交易系统负责高并发的"写"(下单、支付),数据系统负责海量数据的"读"(分析、推荐、搜索)。
大数据层内部长什么样?
简单来说,它分为四个环节:
| 环节 | 组件 | 职责 |
|---|---|---|
| 数据接入 | Canal + Kafka | 实时监听业务数据库变更,把数据同步到消息队列 |
| 数据计算 | Flink(实时)/ Spark(离线) | 清洗、聚合、关联计算 |
| 数据存储 | ClickHouse / Elasticsearch / Redis | 存放报表数据、搜索索引、推荐缓存 |
| 数据应用 | 运营大屏 / 商品搜索 / 推荐系统 | 直接面向用户和运营提供服务 |
实际业务场景举例:
- 实时大屏:双11大促时,Flink 每 5 秒聚合一次订单流,实时推送到大屏展示 GMV。
- "猜你喜欢"推荐:算法团队用 Spark 离线训练推荐模型,结果存入 Redis,用户打开 APP 时毫秒级读取。
- 商品搜索:商品信息变更时,Canal 实时更新 Elasticsearch 索引,保证搜索结果最新。
- 运营报表:运营人员分析"浏览→加购→支付"转化率,直接查询 ClickHouse,秒级返回,完全不干扰主业务库。
理解大数据层最关键的一点是:它和微服务业务层是物理隔离的,数据单向流动。
业务库的数据单向同步到大数据平台,但大数据平台绝不反向写入业务库。这种设计确保了即使大数据平台做复杂查询把 CPU 打满了,也不会影响用户正常下单——这就是电商系统稳定性的底线。
写在最后:架构的本质是什么?
我们从单体应用一路走到大数据层,回头看看这条路:
| 阶段 | 演进 | 驱动因素 |
|---|---|---|
| 1 | 单体应用 | 入门,All in One |
| 2 | 数据库分离 | 数据需要共享 |
| 3 | 前后端分离 | 客户端设备性能不足 |
| 4 | 分层架构 | 代码职责混乱,难以维护 |
| 5 | SOA(物理隔离) | 服务互相影响,稳定性差 |
| 6 | 微服务(去 ESB) | ESB 成为单点瓶颈 |
| 7 | BFF | 多端适配,前端调用太复杂 |
| 8 | 服务网格 | 治理能力与业务代码解耦 |
| 9 | 大数据层 | 数据价值需要被挖掘 |
每一次架构演进,本质上都在做同一件事:将复杂性进行有序的分层、解耦与下沉。
那些看起来"过度设计"的复杂架构,其实都是一步步被真实业务场景"逼"出来的。没有一步是多余的,也没有一步是凭空想象的。
架构没有终点,只有不断演进的起点。
大家可以在评论区聊聊:你们的系统现在处在哪个阶段?又遇到了什么新的挑战?
下期预告:为什么你的代码在笔记本上跑得飞快,上了服务器反而变慢了?
我们在这篇文章里聊了电商系统的架构演进,从单体一路走到了大数据层。但不知道你有没有想过一个问题:所有这些微服务、BFF、大数据组件,最终都跑在什么上面?
无论是订单服务还是 Flink 计算任务,它们最终都要被 CPU 执行、在内存里暂存、从硬盘里读取数据。而CPU 的缓存一致性、内存的寻址方式、磁盘的 IO 模型,这些计算机组成原理的基础知识,直接决定了你的系统到底能扛多少 QPS。
下一篇文章,我们把视线从"架构设计"往下沉一层,看看计算机硬件到底是怎么运行你的代码的。你会发现,很多你写代码时遇到的诡异问题——比如"为什么加了缓存反而更慢了"、“为什么多线程程序有时候比单线程还慢”——答案都藏在计算机组成原理里。