2026年再回看自己早年搭过的几套小程序商城,最开始图快,把商品图、接口服务、数据库全塞在一台云主机上,商品一多、活动一压,首页打开都转圈。这几年的做法变了:不再追求一台机器扛所有,而是按动静把商城拆到云组件上——图片和静态资源归对象存储,结构化业务数据归关系型数据库,购物车和热点库存归缓存,异步任务和回调交给函数计算。这篇不聊产品对比,只讲这套拆分的工程结构和每一块为什么这么放。
一、动静分离:商品图和详情别进关系库
商品主图、详情页里的富文本图片、小程序前端安装包,这类大二进制文件,塞关系型数据库是最常见的性能坑。数据库行里挂个二进制字段,备份慢、查询慢、还占连接。
工程上的分法是:所有文件统一放对象存储 OSS,数据库里只存一个 object key 和元数据(宽高、格式、上传时间)。商品详情要展示哪张图,前端拿 key 拼访问地址去取。这样 RDS 只扛结构化数据,体积小、备份快、查询不被大字段拖慢。按商品或商户分目录管理,清理下架商品资源时按目录批量删,不用回表里捞二进制。
二、CDN 加速:让全国用户就近拿到图片
商城用户遍布各地,详情页首屏要加载几十张商品图,每次都回源站拉,跨城时延扛不住。
OSS 前面挂一层 CDN 加速,图片、静态 JS、样式都缓存到全国边缘节点。用户请求先命中离他近的节点,命中了直接返回,不回源;没命中才回源拉一次再缓存。缓存策略按资源类型配:图片这种更新不频繁的缓存时间设长一点,商品信息这种变的就短一点或带版本号刷新。改了商品图想立刻生效,按版本号刷新缓存,不用等自然过期。
三、RDS 承载商品与订单主数据
商品 SPU、SKU、订单、结算后的订单明细,这些强一致的结构化数据,放 RDS MySQL 这类关系库。
建表时就把后面的坑预留了:订单表冗余下单时的价格快照,别让后来商品改价影响历史订单;高频查询的字段(商户、状态、下单时间)建索引;读写分离开起来,列表查询走只读实例,下单写主库。连接池大小按规格设,别一上来把连接开爆。单表到千万级之前,先别急着分库分表,把慢查询和索引调优做掉更划算。
四、Redis 做购物车与库存预热
购物车是高频读写、可丢的数据,天然适合缓存。用 hash 结构存,用户加购、改数量、勾选结算都在缓存里完成,不落库。
库存预热是大促前的关键动作。活动开始前,把热点商品的库存余量提前加载进 Redis,下单扣减在缓存里用 Lua 脚本原子完成,判断余量和扣减不拆开,避免并发超卖。支付成功后再异步回写数据库,定时对账。这样在线请求不直接压数据库行锁,连接池不会被瞬时并发打满。
五、函数计算:支付回调与定时关单
支付渠道的结果通知是个 HTTP 回调,订单超时关单是个周期动作,这两件事都没必要常驻一台机器守着。
支付回调直接交给函数计算:渠道一通知,触发一次函数执行,做幂等校验、更新订单状态、回写库存,跑完就释放,不占常驻资源。订单超时关单、每日库存对账、会员权益扫描这类周期任务,用函数计算加定时触发器,到点拉起、算完回收。
好处是平时不养这部分算力,事件来才拉起,峰值按需并发;回调和定时任务故障隔离,函数挂了不影响在线交易主流程。
网关层再补一层:小程序接口统一走 API 网关做鉴权和限流,下单接口在大促前单独配限流阈值,突发流量被挡在网关,不会直接打到后端服务。用户登录态、接口签名校验都在网关这层做掉,业务函数只关心自己的逻辑。
六、适用规模与技术局限
这套动静拆分——文件在 OSS、访问走 CDN、主数据在 RDS、购物车库存在 Redis、异步在函数计算——撑一个日单量几千到几万的小程序商城是顺的。
局限也说清楚:商品和订单单表到千万级,RDS 单库压力上来,要按商户或时间分库,或者切更高规格的分布式关系库;库存缓存和数据库的对账不能省,一旦缓存节点故障,要能从数据库库存重建缓存;函数计算适合短小的回调和批任务,真有长耗时的视频处理、报表导出,那是另一类计算服务的活。小团队初期别过度设计,先按这套拆,量级到了再升级。
监控跟着拆。对象存储的请求数、CDN 的缓存命中率、RDS 的慢查询、Redis 的内存水位、函数计算的错误率,各自看各自的看板,哪块抖了一眼能定位,别等用户投诉才发现。拆分的好处不只是性能,是每一层出问题都能单独看、单独扩,不互相拖累。
几个常被问到的点
问:为什么不把图片直接放服务器磁盘?答:多实例部署时磁盘不共享,扩容和备份都麻烦,对象存储天然多副本、带 CDN 接入。
问:购物车为什么不直接写数据库?答:加购是高频临时态,写库放大压力,缓存扛住、结算落一次库就够。
问:支付回调为什么用函数计算不用常驻服务?答:回调是事件触发,按量执行,省掉常驻机器,故障还隔离。