
简介一份面向电商系统开发、运维及架构设计人员的PDF文档系统讲解电商平台的整体软件架构。内容围绕中台服务、分布式缓存、消息队列、数据存储与访问接口、运维监控、第三方服务、安全机制及数据库读写分离等模块展开并结合订单从提交、支付、库存扣减到物流签收的完整流程帮助读者理解高并发场景下各组件如何协同。资源共1个PDF文件压缩包大小342KB内容精炼适合快速建立电商架构知识框架。目前已有103人学习下载对于初入电商领域的技术人员或需要梳理架构脉络的开发者是一份可快速阅读的入门参考资料。1. 电商平台软件架构一张图读懂商业系统的骨架做电商系统这些年我拆过不少五花八门的系统最后发现一个反直觉的规律大促能不能扛住技术选型只占一半另一半看服务和数据的边界划得清不清楚。这份《电商平台软件架构》PDF 是一张完整的系统拓扑图从用户端的中台服务、分布式缓存、消息队列一路铺到中央主库、读写分离、OGG 同步和各类第三方接口把一套电商平台该有的模块全部摊开了。适合两类人一是刚接触电商后端、想搞明白服务之间怎么协作的新人二是正在做架构梳理或系统重构、需要一份参考蓝图的开发者。下面按我自己的拆图习惯从服务层、数据层、订单链路三个方向展开。2. 中台服务层拆解订单、库存、支付三大核心的职责与调用边界2.1 为什么电商要抽中台复用、隔离、独立伸缩很多人一听到中台就觉得是过度设计但电商这个场景不太一样。订单、支付、库存、商品这些能力不是只有商城前端在用——客服系统要看订单、运营后台要改商品、App 和 Web 端要走同一套登录注册如果每个端都自己实现一遍光是需求变更就能把人拖死。中台的本质是把这些高频复用的业务能力下沉成独立服务对外只暴露接口对内各自管理数据和状态。这份架构图里的中台服务层列得很典型订单、图片、监控、同步、库存、产品、评论、积分/代金券/卡/余额、支付、购物车、活动促销、用户服务外加分布式缓存和消息队列。我拆图时会先问一个问题哪些服务直接参与交易主链路答案是订单、库存、支付这三个是命脉促销和购物车是流量入口评论和积分是增长辅助监控和同步是运维底座。搞清楚这个主次关系后面做容量规划和故障排查才有优先级。中台带来的第二个好处是独立伸缩。平时订单量平稳但大促时订单和支付的压力是日常的十倍以上商品和评论可能只有两三倍。如果所有逻辑耦合在一个单体应用里扩缩容只能整体操作成本高还容易互相影响。拆成服务后只需要对订单、支付、库存这几个热点服务做横向扩容其他服务维持原样。第三个好处是故障隔离。库存扣慢了不会拖垮支付消息队列堆积了不会让用户登录超时。当然代价是链路变长、数据一致性变复杂这两点后面单独说。2.2 核心服务时序订单不直接扣库存支付只认回调先说订单和库存的边界。很多人第一版做电商会犯一个错用户下单时订单服务直接去数据库扣库存然后生成订单。这样做在低并发下没问题但一旦促销流量上来订单服务和库存操作耦合在同一个事务里数据库连接池很快就被打满。这套架构图的处理方式是提交订单时先写入订单写库同时通过消息队列通知库存服务扣减 Redis 中的预占库存而不是在订单服务里同步操作库存表。我自己的习惯是分成三步走。第一步用户提交订单订单服务校验商品状态和收货地址生成待支付订单写入订单写库第二步通过消息队列发送库存预占消息库存服务在 Redis 中执行扣减如果 Redis 库存不足直接标记订单异常第三步支付回调到达后订单服务把订单状态改为已支付再触发库存实际扣减和订单同步。这里关键是预占和实际扣减分开Redis 管高并发下的快速校验数据库管最终一致。支付服务的边界更明确它只负责接收第三方支付平台的回调并验证金额和订单号然后更新订单支付状态。它不关心库存也不关心物流。我在实际项目里见过把支付逻辑写进订单服务的做法结果每次支付渠道升级订单服务就要跟着发版风险很大。正确姿势是支付服务单独部署回调接口做成幂等的——同一个支付通知重复到达最终效果只能算一次。2.3 支撑型服务如何协同商品、用户、评论、促销、购物车商品服务在架构图里承担了商品静态化的职责。电商商品详情页流量极大如果每次都去查数据库拼装详情数据库压力不可控。常见做法是把商品详情渲染成静态 HTML 丢到 CDN或者做页面片段缓存只有价格、库存这类实时性要求高的字段走接口动态获取。商品服务负责维护商品基础数据和静态化任务的触发时机——商品信息变更时生成新的静态页同步到文件系统和 CDN。用户服务除了登录注册还管理收藏和收货地址。注意架构图里把登录注册、收藏、收货地址归类在同一个服务下这是合理的因为这些数据都围绕用户这个聚合根分开反而要跨服务频繁联表。评论服务带审核流用户提交评论先进待审核运营在中台管理后台审核通过后展示评论写库和读库是分离的读库通过同步服务从主库拉取。促销服务比较特殊它不直接参与交易而是通过促销设置和审核来影响订单和商品的价格计算。订单生成时根据当前生效的促销活动计算优惠这属于典型的读多写少场景促销活动配置可以缓存到 Redis。购物车服务的亮点在架构图里写得很清楚购物车所有信息直接写文件系统而不是每次都走数据库。我一开始觉得这个设计怪后来理解了——购物车的读写比极高、数据量又大并且丢了也不影响核心交易放文件系统加本地缓存是完全够用的。服务之间通过分布式消息队列做异步通信。订单生成后发消息给库存服务、评论服务、积分服务各取所需互不阻塞。消息队列在这里起到削峰填谷的作用大促时的瞬时流量先堆积在队列里下游按自己的消费速度处理不至于把数据库打垮。3. 数据层与缓存主库、读库、写库的读写路径与同步参数3.1 这套数据库分库模型的读写路径架构图里的数据库设计是典型的多库分层模型我把它拆成三条链路来理解。第一条是交易写链路。用户提交订单写入订单写库支付回调更新订单状态也在写库完成写库是交易数据的唯一入口。第二条是数据分发链路。写库的数据通过同步服务汇聚到中央主库中央主库再向读库、TMS 库、WMS 库、BI 库分发。第三条是业务读链路。商城前端、客服系统、运营后台查订单列表和详情时走的都是读库不碰写库。那 TMS、WMS、BI 这些库是干什么的拆分一下库名定位典型数据中央主库全平台交易数据的汇聚点是数据分发的源头订单、用户、商品、库存全量订单写库订单主流程的写入入口接收下单和支付更新订单主表、订单明细读库面向查询场景的只读副本订单宽表、商品快照TMS 库物流运输系统发货记录、签收记录WMS 库仓储管理系统拣货、出库、库存台账EDI 库与外部系统做数据交换订单同步给 WMS 的报文BI 库数据分析与报表订单明细、用户行为、销售汇总读写分离和分库最大的收益不是性能而是互不干扰。运营在后台拉大报表SQL 再烂也只影响 BI 库客服查订单历史最多把读库压住订单写库照常处理新订单。不过这套模型有个前提同步链路必须稳。架构图里用的同步工具是 OGGOracle GoldenGate属于日志级同步性能很好但配置和使用有不少讲究。3.2 OGG 同步的参数配置与踩坑点OGG 做的是数据库日志解析源库把 redo log 里的变更解析成交易记录投递到目标库重放。和直接写应用双写相比OGG 对源库性能影响小也不会侵入业务代码。我见过的电商项目里订单写库到中央主库、中央主库到读库这两条线基本都是 OGG 扛着的。我在测试环境搭建 OGG 同步订单表时的配置大概长这样-- 抽取进程配置 GGSCI EDIT PARAMS EXT_ORD EXTRACT EXT_ORD USERID ogg, PASSWORD ogg EXTTRAIL /u01/ogg/dirdat/er TABLE orders.t_order; TABLE orders.t_order_item; -- 投递进程配置 GGSCI EDIT PARAMS DP_ORD EXTRACT DP_ORD RMTHOST 192.168.10.20, MGRPORT 7809 RMTTRAIL /u01/ogg/dirdat/er TABLE orders.t_order;抽取进程的核心参数有三个EXTRACT指定进程名EXTTRAIL是本地交易文件的落地路径TABLE指定要同步的表。RMTHOST和MGRPORT是目标库的 Manager 进程地址和端口RMTTRAIL是交易文件投递到目标机的路径。这里最容易踩的坑是表名不带用户名前缀OGG 直接报找不到对象另外一个坑是源库和目标库的字符集不一致中文乱码。OGG 同步也有一个天然的短板——延迟。日志解析和网络传输都有开销高峰期五到十秒的延迟很正常。所以在订单流程里同步属于最终一致写库提交后读库不会立刻看到这笔订单。架构图里同步订单、从写库到中央主库再分发到读库这条链路本质上就是在最终一致的约束下保证数据最终能对齐。3.3 Redis 与 Memcache 的选型边界架构图里同时出现了 Redis 和 Memcache 两组缓存服务这是很多人的困惑点。我的理解是Redis 负责需要数据结构支持的缓存场景比如购物车用 Hash 结构存 SKU 和数量库存用 String 结构做原子扣减订单预占用 SETNX 做分布式锁。Memcache 则更适合纯粹的 key-value 缓存比如用户会话、验证码这类简单数据读多写少淘汰策略走 LRU。它的优势是多线程模型在多核服务器上吞吐量不错但对应地Memcache 的数据结构太简单没法做库存扣减这种需要原子操作的复杂逻辑。两者在同一套架构里共存并不冲突——混用缓存没有标准答案按数据结构复杂度分流才是合理的。我习惯给缓存数据统一设置过期时间原则是热点数据设置 30 到 60 分钟过期实时性要求高的库存和价格设 30 秒到 1 分钟。缓存更新走主动失效加延迟双删写操作先更新数据库再删缓存避免并发条件下缓存和数据库不一致。4. 订单主链路实战从提交到签收的状态流转与库存扣减4.1 一条订单从提交到自动审单的完整分支订单主链路是架构图里信息密度最高的部分值得逐句拆解。用户提交订单后第一步是写订单写库并检查写库是否正常写失败直接返回下单失败不进入后续流程。写成功后生产订单并扣减 Redis 库存。接着判断是货到付款还是在线支付。货到付款的分支相对简单不需要等支付回调直接进入中央主库的自动审单逻辑。在线支付则要等支付服务回调如果长时间没收到回调需要定时任务主动查支付平台状态。我的实践是订单支付超时设为 30 分钟超时未支付自动取消并释放 Redis 库存。自动审单通过后修改订单状态并同步到 EDI/WMS 库WMS 系统抓取订单开始发货发货后记录到 TMS 库。关键点在于订单状态同步到 EDI 库→中央主库→读库→BK 记录是严格按顺序执行的前一段同步成功才触发下一段。这个设计是防消息乱序因为订单状态是单向递增的一旦顺序颠倒读库看到的可能是一条已经签收但还没发货的订单。订单签收后流程回到同步服务把签收状态同步到中央主库和读库同时更新中央主库的库存信息。到这里一条订单的生命周期才算真正走完。4.2 库存扣减的并发控制与 Redis 原子操作库存扣减是电商并发问题的重灾区。先看数据层实现我用的是乐观锁加条件更新UPDATE t_stock SET available available - #{num}, version version 1 WHERE sku_id #{skuId} AND available #{num};available #{num}是防超卖的第一道关卡数据库层面保证扣减数量不超过可用库存。version是乐观锁字段并发更新时只有一个事务能成功其他事务返回影响行数为 0业务层捕获后提示用户库存不足。这套 SQL 的问题是数据库行锁压力大高并发下更新排队明显所以架构图里把 Redis 放在了数据库前面做预扣减。Redis 侧用 Lua 脚本保证扣减的原子性local stock tonumber(redis.call(GET, KEYS[1])) if stock and stock tonumber(ARGV[1]) then redis.call(DECRBY, KEYS[1], ARGV[1]) return 1 end return 0脚本的逻辑很直观先读库存库存足够才扣减否则返回 0。因为整个脚本在 Redis 里是原子执行的不会出现多个请求同时读取到相同库存的问题。实际使用时我把KEYS[1]设为库存键名ARGV[1]设为扣减数量下单接口在发送消息队列之前先执行这个脚本成功了才允许订单进入支付环节。Redis 预扣减和数据库实际扣减怎么对齐我的做法是消息队列消费者收到订单支付成功的消息后先执行上面的条件更新 SQL成功则说明扣减无冲突失败则回滚订单状态并联系统重新核对预占库存。分布式缓存和数据库之间的库存差异用定时对账任务兜底每 5 分钟做一次 Redis 与数据库的库存核对差异超过阈值就告警。4.3 多级同步的失败补偿与幂等设计订单状态从写库到读库要经过三跳写库→中央主库→读库。每一跳都可能失败。架构里的同步服务其实就是补偿机制的载体同步服务记录每笔订单的同步状态失败的任务进入重试队列重试次数用尽后发告警给运维人工处理。另一个必须处理的点是幂等。订单同步到 WMSWMS 和电商系统之间网络抖动可能导致同一笔订单同步两次。解决办法是在 EDI 层做唯一键校验用订单号加状态字段做联合唯一索引重复插入直接跳过。支付回调接口同样要走幂等——同一个支付结果通知到达多次第一次更新订单状态、第二次发现状态已变更则直接返回成功。我在一个促销项目里因为没做幂等吃过亏支付平台重试回调订单状态被从已支付改回待支付用户付款成功却看到订单未支付客诉直接爆掉。从那以后我坚持一条铁律所有涉及状态流转的接口先查状态再做更新更新语句必须带当前状态条件。5. 架构落地常见问题与排查缓存、超卖、延迟与堆积5.1 缓存穿透、击穿、雪崩大促前的三座大山现象大促刚开始数据库 CPU 突然飙到 99%大量请求打到 DB缓存形同虚设。线上表现为接口延迟从 50ms 涨到 3 秒随后大面积超时。原因三种情况都可能导致。穿透是查询不存在的 key请求绕开缓存直接砸向数据库击穿是某个热点 key 刚好过期一瞬间海量请求打到数据库雪崩是大面积 key 在同一时段过期数据库压力骤增。解决穿透用布隆过滤器拦截不存在的 key或者在缓存里存空值并设置短过期时间。击穿的核心是热点 key 不过期加互斥重建——缓存里没有就先让一个线程去查库重建其他线程自旋等待。雪崩在设置过期时间时加随机因子比如基础 60 分钟加上 0 到 5 分钟的随机偏移错开所有的 key 的过期时间。这套组合拳下来大促前三件套基本能压住。5.2 库存超卖从 SQL 到 Lua 的两道闸门现象商品库存显示剩余 10 件下单接口并发压测到 50 并发时卖出去了 23 件。页面显示有货付款后却下发缺货通知。原因典型的原因有两个。第一版代码先查库存再扣减这两步之间是非原子操作并发下多个请求读到同一库存值另一个原因是没做数据库条件更新扣减语句没有available #{num}的约束。解决数据库侧把扣减语句改成条件更新影响行数为 0 就直接返回失败。Redis 侧用 Lua 脚本做原子预扣减下单先过 Redis扛住瞬时并发再用数据库条件扣减保证最终一致性。验证方法是写个并发脚本100 个线程同时抢 10 件库存断言最终成功订单数必须等于 10。5.3 订单状态同步延迟先查 OGG 还是先查应用现象用户已支付订单客服在后台查不到最新状态延迟十几分钟才显示。技术排查时发现读库和写库数据不一致。原因同步链路有多个环节——OGG 抽取、网络投递、目标库重放。最常见的问题是 OGG 进程挂掉没人发现或者源库归档日志空间满了导致抽取停滞。也有可能是同步服务处理消息失败后重试队列堆积。解决排查顺序很重要先看 OGG 的 Manager 进程和 Extract 进程是否正常运行用GGSCI INFO ALL查看进程状态再看 OGG 报告的延迟时间确认是传输慢还是重放慢最后才查应用层的同步服务看消息队列有没有堆积、重试任务有没有卡住。我一般会搭一套同步延迟监控延迟超过 60 秒就告警避免等到客服投诉才发现。5.4 消息队列堆积定位消费瓶颈的办法现象大促中段订单消息队列的积压量从几千涨到几十万消费速度跟不上生产速度下游库存和积分服务明显滞后。原因多数不是队列本身的问题而是消费者依赖的资源出现了瓶颈。比如消费者线程池太小、数据库连接被占满、下游服务的接口变慢。把队列吞吐上不去简单归因于消费者数量不够是常见的误判。解决先看每类消息的消费速率和生产速率确认到底是哪个环节拖慢的。再定位到具体消费者实例的日志看耗时集中在哪一步——是查数据库慢、调外部接口慢还是反序列化出错一直重试。如果是外部接口变慢做线程池隔离避免一个慢接口拖死整个消费线程池如果是数据库慢看慢 SQL 和数据库连接池使用率。消息队列的堆积不是靠重启能解决的问题基本都在下游。6. 进阶用一张体检清单反推这套架构是否够用拿到架构图不要急着照抄先做一轮自检。我每次给电商系统做架构评审手里会拿一张固定的清单逐项去套。检查项通过标准不合格信号服务边界订单不直接操作库存表、支付不拼 SQL 改订单跨服务联表查询、一个事务里操作两个服务的数据缓存分层热点数据有 Redis 兜底、会话类数据走 Memcache所有数据直查数据库缓存形同虚设同步链路写库→主库→读库链路清晰有延迟监控读库数据滞后超过 60 秒无告警库存一致性Redis 预扣与数据库扣减对齐有对账任务促销后库存对不上账靠人工改数据幂等保护支付回调、订单同步、WMS 抓单均做幂等同一通知重复处理导致状态回退消息队列生产消费速率有监控堆积有告警消费失败静默重试看不到堆积指标做完清单自检再回到架构图本身去看一个核心问题这套设计最怕什么我认为是中央主库的单点风险。写库和读库都依赖中央主库做数据分发一旦中央主库出问题全链路的同步都会停摆。如果是生产环境我会在主库上做主从高可用或者引入备库承担分发职责。架构图没画这部分但落地时不能省。验证这套架构是否够用的另一个方法是做破坏性演练把 OGG 进程手动停掉、把 Redis 主节点 kill 掉、往消息队列里灌十倍增量数据观察系统行为是否符合预期。说白了架构图是黑匣子只有把故障提前注入进去才知道哪些环节是纸糊的、哪些环节是真能扛的。有一次演练停了 OGG读库数据滞后了半小时但我们提前备好了手工触发同步的脚本运营侧无感。从那以后我每次动同步链路都强制走一遍备份校验和回滚演练多花一小时在演练上好过大促当天熬夜救火。希望这套拆解思路也能帮到你。本文还有配套的精品资源点击获取