电商系统开发:产品经理管理方法:把数据库设计转化为保障高峰性能
电商系统开发中,最容易被低估的性能问题,往往不是服务器规格不够,而是产品经理在需求阶段没有把“高峰场景”翻译成数据库能够承受的访问方式。一次大促复盘中,我见过一个库存接口在日常压测中每秒处理 1800 次请求,业务方认为已经足够;但活动开始后,用户不断刷新商品页、提交订单和查询物流,数据库连接池在 11 分钟内耗尽,最终真正拖垮系统的不是下单写入,而是商品详情页里一条未经约束的库存查询。
这件事让我形成一个判断:数据库设计不是研发阶段的技术附件,而是产品经理管理峰值性能的业务合同。产品经理需要在需求评审前回答清楚,哪些数据必须实时、哪些数据允许延迟、哪些查询会被重复调用、哪些写入必须保持强一致,以及峰值到来时系统应该优先保护什么。
很多团队谈数据库设计时,首先讨论字段、索引、分库分表和缓存,却没有先确认业务动作。数据库性能的本质并不取决于表名是否规范,而取决于同一时间有多少人、以什么频率、按照什么条件访问这些数据。
例如,“展示商品库存”看起来只是一个字段,但它可能对应四种完全不同的产品需求:展示一个近似库存数字、展示可售库存、下单时校验库存、扣减库存并记录流水。四种需求对实时性、锁竞争、读写比例和故障处理的要求完全不同。如果产品经理只写一句“页面显示库存”,研发只能自行猜测,后续必然出现性能与体验之间的冲突。
我通常会把每个高频数据需求拆成五个问题:谁在访问、什么时候访问、一次访问返回多少数据、允许多旧的数据、失败时业务是否还能继续。只要这五个问题没有答案,数据库方案就不应该进入开发排期。
| 产品需求表述 | 隐含的数据动作 | 主要性能风险 | 产品经理必须补充的约束 |
|---|---|---|---|
| 展示实时库存 | 高频读取库存或库存快照 | 读请求集中,缓存失效时形成数据库洪峰 | 实时的定义、允许误差、刷新频率 |
| 支持优惠券领取 | 资格判断、额度扣减、领取记录写入 | 热点行争用、重复领取、瞬时写入激增 | 是否允许排队、是否需要强一致、失败提示 |
| 查询订单列表 | 按用户、状态、时间分页查询 | 深分页、复杂排序、历史数据扫描 | 默认时间范围、最大页数、历史订单查询边界 |
| 实时显示销量 | 订单写入后同步聚合或实时统计 | 每笔订单触发聚合更新,写放大明显 | 销量口径、延迟容忍度、是否展示近似值 |
这张表的重点不在于字段如何命名,而在于把一句产品语言转换成访问模型。只有访问模型确定后,研发才有依据选择缓存、异步队列、聚合表、索引和数据分层。

高峰期不可能让所有功能都保持同等实时性。商品详情、搜索、推荐、营销弹窗、订单提交、支付状态和售后查询,应该拥有不同的保护等级。产品经理如果没有提前定义优先级,系统发生拥塞时,研发只能临时决定哪些请求限流,结果往往是关键交易和低价值展示请求一起变慢。
我建议把功能划分为四类。第一类是交易生命线,包括创建订单、库存校验、支付回调和订单状态变更;第二类是交易辅助,包括购物车、优惠券试算和地址校验;第三类是体验增强,包括推荐、实时销量、个性化标签和营销动画;第四类是可延后功能,包括报表、消息推送、行为分析和非关键同步。
数据库设计的核心不是让所有请求都快,而是在资源不足时仍然让高价值请求可用。这意味着产品需求文档中必须写清楚降级方案,而不是只写正常流程。
高峰性能至少要用四个数字表达:峰值并发用户数、每秒请求数、每秒写入数和热点集中度。并发用户数描述有多少人停留在系统中,QPS 描述请求到达速度,写入量决定数据库事务压力,热点集中度则决定是否出现少数商品、少数用户或少数数据行被集中访问。
在一次服饰电商活动中,业务方预计有 30 万人参与,但这个数字对数据库没有直接意义。我们进一步拆解发现,真正的瞬时在线用户约 5.2 万,商品详情请求峰值约 1.8 万次/秒,下单请求峰值约 620 次/秒,前 20 个商品贡献了 64% 的详情访问。于是,系统最需要解决的不是“支撑 30 万用户”,而是“如何保护 20 个热点商品的读取和 620 次/秒的关键写入”。
| 指标 | 业务含义 | 产品侧需要确认的问题 | 建议的验收方式 |
|---|---|---|---|
| 峰值在线用户 | 同一时刻停留在页面或流程中的用户数 | 是否包含后台、爬虫和重复设备 | 按活动时间窗口统计并去重 |
| 峰值 QPS | 单位时间内的请求到达量 | 是否区分静态资源、查询和写入 | 按接口和业务动作拆分监控 |
| 峰值写入 TPS | 数据库事务或写操作的瞬时压力 | 一笔业务是否会产生多条写记录 | 统计订单、库存、流水的实际写入数 |
| 热点集中度 | 流量是否集中在少数商品或数据行 | 是否存在爆款、秒杀品或单一活动 | 计算前 1%、前 10% 对总流量的贡献 |

许多系统在日常环境中表现良好,是因为流量、数据量和并发写入都没有达到暴露问题的阈值。一条扫描数万行的查询,在开发环境里可能只需要几十毫秒;当订单表增长到数千万行,再叠加排序、分页和多个筛选条件,耗时就可能从几十毫秒变成数秒。
更危险的是,日常环境无法模拟热点。假设 90% 的用户分散访问不同商品,数据库即使采用行锁,也不容易出现明显冲突。但活动期间,用户同时抢购同一个商品,所有事务都要争抢同一库存记录,单条 SQL 的平均耗时会迅速失真。
我在评审时会特别关注“平均耗时”之外的 P95、P99 和错误率。平均值看起来很漂亮,不代表用户体验稳定。对电商交易来说,1% 的慢请求可能正好对应最着急下单的用户,而 P99 的抖动往往比平均耗时更早暴露连接池、锁等待和线程池问题。
读放大是指一次用户动作触发了大量重复读取。例如用户打开商品页,前端同时请求商品基本信息、库存、价格、优惠、评论、推荐和物流估算。如果一个页面触发 8 个接口,而每个接口又各自访问数据库,那么页面访问量放大后,数据库承受的请求数会远高于用户行为本身。
写放大则发生在一次业务操作对应多次数据库写入的情况下。一笔订单可能同时写入订单主表、订单明细表、库存流水、优惠券使用记录、积分变动、营销归因和操作日志。如果所有写入都放在同步事务里,数据库不仅要处理订单创建,还要承担大量可延后的附属动作。
产品经理不能只问“用户点击一次会调用几个接口”,还要问“这个接口会读取几张表、写入几张表、是否会触发同步下游动作”。只有把调用链画出来,才能识别真正的压力放大点。
库存超卖通常被归咎于并发控制,但我认为它也经常是产品规则不完整造成的。例如,商品详情展示的是总库存,订单校验使用的是可售库存,预占库存又在支付后才扣减,取消订单则由异步任务释放。不同环节使用了不同库存口径,系统即使没有技术故障,也可能出现用户看到有货却无法下单的体验。
因此,产品经理需要先定义库存状态机:可售、预占、已支付、已取消、已释放分别代表什么;每个状态由谁改变;状态改变是否要求立即生效;重复请求是否幂等;超过多久未支付才释放。数据库的字段和事务边界,都应该从这套状态机推导出来。

索引能够减少读取扫描,但不是免费午餐。每增加一个索引,相关写入就要维护更多索引结构,磁盘空间、缓存命中和写入耗时都会受到影响。特别是订单、库存和支付流水等高频写表,索引过多可能让查询变快,却让写入和锁竞争变得更糟。
我见过一个订单列表页面,为了支持状态、渠道、支付时间、发货时间、店铺和用户等筛选条件,研发在主表上增加了十多个组合索引。上线后,后台查询确实变快,但订单写入的平均耗时增加了约 35%,高峰期数据库 CPU 长时间超过 85%。真正的解决方法不是继续加索引,而是限制默认查询范围,并把运营分析类查询转移到独立的数据集市。
产品经理需要参与索引决策,但不必直接决定索引字段。产品侧应该确认哪些筛选组合是高频核心路径,哪些只是偶尔使用;研发再根据执行计划和写入代价决定是否建索引。
“实时”是需求文档中最容易造成成本失控的词。库存扣减、支付状态和风控结果通常需要较强实时性;商品销量、评价数、访客数、推荐热度和运营报表,很多时候允许延迟几秒、几十秒甚至几分钟。
如果产品经理把所有指标都定义为实时,研发可能采用同步聚合、频繁刷新和强一致查询,最终导致数据库被大量低价值读取拖慢。更合理的方式是为每个数据字段定义新鲜度等级:毫秒级、秒级、分钟级、小时级或日级,并明确过期后的展示策略。
| 数据类型 | 建议新鲜度 | 高峰期处理方式 | 不建议的做法 |
|---|---|---|---|
| 支付结果 | 秒级或事件驱动 | 以支付回调和订单状态机为准 | 前端频繁轮询主订单表 |
| 可售库存 | 下单校验强一致,展示可近似 | 展示库存使用快照,下单再次校验 | 把展示值直接当作扣减依据 |
| 商品销量 | 秒级至分钟级 | 异步聚合,允许短暂延迟 | 每笔订单同步更新统计表 |
| 评价数量 | 分钟级 | 批量汇总或缓存读取 | 每次打开详情页实时 count |
| 经营报表 | 小时级或日级 | 进入分析库或报表层 | 直接扫描交易主库 |
缓存可以减少数据库读取,但不能修复错误的数据模型和不合理的调用链。缓存最常见的三个坑是缓存击穿、缓存雪崩和缓存与数据库之间的数据不一致。高峰期如果热点商品缓存同时过期,大量请求会在同一时刻回源;如果回源逻辑没有互斥控制,数据库会瞬间承受比平时更大的压力。
另一个容易忽略的问题是缓存键设计。将商品编号、地区、用户身份、优惠资格和设备信息全部拼进缓存键,可能导致缓存命中率很低;而只使用商品编号,又可能把不应该共享的个性化结果返回给其他用户。缓存键必须由产品的展示口径和数据隔离规则共同决定。
我的做法是要求需求评审中新增一栏“缓存失效后的行为”。如果缓存不可用,页面是返回上一次快照、降级为默认值、暂时隐藏模块,还是阻断交易?这个答案会直接影响回源策略和数据库保护等级。
单接口压测能够发现某个接口的吞吐上限,但电商高峰很少只有一个接口被调用。用户浏览、搜索、加入购物车、提交订单、支付和查询订单之间存在真实比例。单独把下单接口压到很高,不代表详情页、优惠计算和库存服务同时运行时仍然稳定。
业务链路压测至少要模拟三种用户:只浏览不购买的流量型用户、反复刷新和抢购的热点用户、完成下单并查询状态的交易型用户。三类用户对数据库的读写比例不同,混合后才能观察连接池、锁等待、缓存回源和异步队列之间的相互影响。

每类电商数据都有生命周期。商品基础信息可能长期保留,库存流水需要追溯,购物车数据可能短期有效,订单主数据需要长期查询,行为日志则更适合进入分析系统。不同生命周期的数据,不应该无差别地放在同一张高频交易表或同一个数据库实例中。
我会要求团队为数据标注四个属性:产生频率、访问频率、保留周期和一致性要求。产生频率高、访问频率高且要求强一致的数据,属于交易核心;产生频率高但访问频率低的数据,适合异步落库或批量处理;访问频率高但允许延迟的数据,适合缓存或预聚合;保留周期长但访问频率低的数据,则应考虑归档和分层。
| 数据层 | 典型数据 | 主要目标 | 产品管理动作 |
|---|---|---|---|
| 交易核心层 | 订单、支付状态、可售库存 | 一致性、可追溯、低延迟 | 减少同步附属动作,明确事务边界 |
| 访问服务层 | 商品详情快照、价格展示、类目导航 | 高吞吐、快速读取 | 允许定义数据新鲜度和降级展示规则 |
| 聚合结果层 | 销量、评价数、热度、店铺统计 | 减少重复计算 | 确认统计口径和允许延迟时间 |
| 分析归档层 | 行为日志、历史订单、运营报表 | 长期保存和复杂分析 | 限制直接访问交易主库 |
读写预算可以理解为每个业务功能在高峰期允许消耗的数据库资源。产品经理不需要精确预测每一条 SQL 的执行计划,但应该为关键场景提供一个可讨论的预算,例如商品详情每次最多触发两次核心数据读取,下单同步事务最多写入三类交易记录,销量展示不允许直接扫描订单明细表。
预算的价值在于,它把性能变成了可谈判的产品约束。当业务方要求增加一个“实时优惠提示”模块时,团队可以明确它会增加多少查询、是否需要单独缓存、是否会影响下单主链路,而不是到了联调阶段才发现页面多了六个接口。
一次需求评审中,我们为商品详情页设定了“核心接口不超过 4 个、主库直读不超过 2 次、非核心模块必须可独立降级”的预算。后续营销团队增加推荐和倒计时功能时,只能通过聚合接口或前端静态配置实现,没有继续向交易库追加实时查询。
平均访问量适合规划普通容量,热点模型才适合规划大促容量。产品经理需要识别三种热点:商品热点、用户热点和时间热点。爆款商品会造成单行或少数几行数据集中访问;大客户或团购账户可能造成单个用户维度的集中写入;整点开售、优惠券发放和直播间抽奖则会形成时间热点。
热点并不一定意味着必须立即分库分表。很多情况下,先拆分展示库存和交易库存、使用库存预占记录、把统计写入异步化、对热点请求做排队,就能解决主要问题。真正需要特殊数据结构的判断标准是:热点是否持续、是否集中到单行、是否包含高频写入、是否能够接受排队,以及业务是否能容忍最终一致。
数据库不会永远处于正常状态。连接池耗尽、缓存失效、消息堆积、从库延迟和锁等待都可能发生。产品经理要做的不是承诺“任何情况下都可用”,而是提前定义资源不足时的用户体验。
例如,推荐模块可以直接隐藏,实时销量可以展示上一次成功快照,评价数量可以暂时不更新,订单列表可以限制查询最近 90 天,优惠券试算可以提示稍后重试。但支付状态和库存扣减不能简单返回旧数据,否则会引发资金或库存风险。
降级不是把功能粗暴关闭,而是把系统资源留给最不能失败的业务动作。每一个降级规则都要配套恢复条件、监控指标和客服话术,否则上线后仍然会被误判为系统故障。

下面这个案例来自我参与过的一次匿名化家居电商项目复盘。该项目平时日均订单约 2.4 万笔,活动日预计订单 18 万笔。原始产品方案要求商品详情实时显示库存、销量、已购人数、优惠后价格和预计送达时间,并且用户每次返回页面都刷新全部数据。
研发初步实现后,商品详情接口平均响应约 110 毫秒,普通压测看起来没有问题。但在混合压测中,前 30 个热门商品的访问占到总详情请求的 61%,库存查询和销量统计直接读取交易库。活动开始后,数据库读请求峰值达到 2.1 万次/秒,订单写入只占约 700 次/秒,却出现了大量连接等待。
这个结果非常典型:业务团队以为订单写入会拖垮数据库,实际先发生故障的是展示型读取。因为商品页每次刷新都查询销量和已购人数,系统在最需要保护交易写入时,被大量低价值的实时统计请求占用了连接和 CPU。
我们没有直接要求产品删掉功能,而是把页面数据按业务价值重新分层。商品名称、图片和基础参数使用版本化快照;价格和促销结果使用短时缓存,但结算时重新校验;展示库存使用 2 秒级快照,下单时读取交易库存;销量和已购人数改成异步聚合,页面允许 30 秒内更新。
同时,产品经理补充了三个规则:第一,展示库存只用于帮助用户判断购买机会,不能作为订单成功承诺;第二,优惠倒计时结束后必须重新请求价格,不允许使用过期优惠结果下单;第三,销量统计不可用时隐藏具体数值,不显示“0”,避免把系统故障误导成商品没有销量。
| 页面数据 | 原始方案 | 调整方案 | 调整原因 |
|---|---|---|---|
| 商品基础信息 | 每次实时查询交易库 | 版本化快照加缓存 | 变更频率低,不应消耗交易库连接 |
| 展示库存 | 每次刷新强制查实时库存 | 短周期库存快照,下单再次校验 | 展示与交易职责不同 |
| 优惠后价格 | 页面价格直接用于下单 | 页面预估,结算重新计算 | 防止价格过期和营销规则变化 |
| 销量与已购人数 | 同步 count 和聚合查询 | 异步事件聚合,30秒内更新 | 展示价值低于交易价值,允许最终一致 |
| 预计送达时间 | 实时调用多个配送接口 | 按地区和仓配规则预计算 | 减少页面打开时的级联调用 |
调整后,商品详情对交易主库的直接读取从每次 6 至 9 次降到 1 至 2 次。活动峰值期间,详情接口请求增长到约 2.4 万次/秒,但交易库读请求保持在 4200 次/秒以内;订单写入峰值约 760 次/秒,订单创建 P95 从 410 毫秒下降到 165 毫秒。
更重要的是,数据库 CPU 并没有简单地“降下来”就结束。我们继续观察缓存命中率、消息堆积、库存校验失败率和订单状态延迟。活动期间销量更新延迟中位数约 8 秒,P99 约 27 秒,符合产品允许的 30 秒展示延迟;库存扣减错误率保持在万分之二以内,异常订单都能通过库存流水追溯。
这些数字是该项目脱敏后的观测值,不应被当作所有电商系统的容量基准。它们真正有价值的地方在于说明一个原则:性能优化的结果,必须同时看交易成功率、关键链路延迟、非核心数据延迟和故障可追溯性。只看数据库 CPU 或接口平均响应,容易得到错误结论。

这次项目中最危险的方案差点出现在库存展示环节。最初有人建议直接把缓存中的库存数字用于创建订单,以减少数据库查询。这个方案在压测中吞吐很好,却混淆了“展示库存”和“交易库存”。当多个用户同时下单,缓存更新延迟会导致库存判断过期,最终产生超卖或大量后置取消。
我们最后保留了两套口径:展示层使用库存快照,交易层使用带版本校验的库存记录,并要求每次扣减写入库存流水。产品文档中明确写出“页面显示有货不代表订单必然成功”,同时把库存不足设计成可识别的业务结果,而不是模糊的系统异常。
这个取舍不一定适合所有业务。高价值、低库存、强承诺商品,可能需要更严格的预占和排队;普通长尾商品,则可以用更轻量的库存校验。关键是不要让缓存方案替产品做业务决策。
我建议每个高频功能都附带一张数据访问卡片,不需要写技术实现,但必须把业务约束写完整。卡片应该跟随需求进入设计、开发、测试和上线复盘,而不是只存在于一次会议纪要中。
这张卡片的价值在于避免“研发自己猜业务规则”。如果结算页要求价格绝对准确,研发会把价格计算放进关键事务;如果商品详情只需要大致展示,研发则可以使用快照。两种方案的成本差异很大,不能等上线前才补充说明。
产品经理不必成为数据库专家,但可以通过反向提问识别风险。面对一张新表,我会问:这张表的写入来源是什么?一天产生多少数据?最常见的查询条件是什么?是否会按时间持续增长?是否会被多个业务同时更新?是否允许归档?如果一个字段改变,哪些页面和流程会受影响?
面对一个新接口,我会问:一次用户操作会触发几次数据库访问?是否会循环查询?是否存在全量列表?排序依据是否稳定?分页翻到很深时会发生什么?接口超时后前端会不会自动重试?这些问题往往比“用哪种数据库”更早发现性能隐患。
有些需求变化看起来只是增加一个字段,实际上会改变数据库压力。例如订单列表增加“按收货手机号模糊搜索”,可能使原有索引失效;商品页面增加“实时已购人数”,可能引入高频聚合;优惠券增加“每个用户限领一次”,则需要唯一约束、幂等处理和并发控制。
我会把这类变化标记为“性能影响变更”,要求重新评估读写预算、索引策略和压测场景。产品迭代速度很重要,但不能把数据库当成无限弹性的底层资源。需求变更的审批内容中,应增加“是否改变访问模式”这一项。
高峰压测应同时观察四组指标。第一组是用户体验,包括关键接口 P95、P99、超时率和页面可用率;第二组是数据库状态,包括 CPU、连接数、锁等待、慢查询和缓存命中率;第三组是业务正确性,包括重复订单、库存差异、支付状态延迟和优惠券重复使用;第四组是恢复能力,包括消息积压、失败重试、数据补偿和人工处理时长。
例如,商品详情接口从 100 毫秒变成 60 毫秒,看起来是优化,但如果库存数据错误率上升,就不能算成功。反过来,销量统计延迟从 1 秒变成 10 秒,只要交易链路稳定且符合产品约定,也可能是正确的取舍。
高峰验收 = 关键交易成功率
+ 核心接口P95/P99
+ 数据一致性结果
+ 降级后的可用性
+ 故障恢复时间
没有发生事故不代表方案优秀。上线后应复盘峰值期间的资源使用、缓存命中、热点对象、慢查询、队列堆积和降级触发情况。特别要关注系统是否依赖了某个临时上限,例如连接池刚好没有耗尽,或者某个热点商品恰好没有被集中抢购。
我会把峰值复盘分成三个问题:哪些压力是预期内的,哪些压力是被遗漏的,哪些功能在高峰时消耗了资源却没有带来相应业务价值。第三个问题最容易被忽略,但它直接决定下一次活动应该删减什么、缓存什么或异步化什么。

如果系统以日常订单为主,没有明显秒杀和爆款集中访问,优先级通常不是立刻分库分表,而是限制无边界查询、规范分页、拆分报表访问、建立慢查询监控,并清理不必要的同步写入。
订单列表默认只查询近 90 天,历史订单通过归档查询;后台报表不直接扫描交易主库;商品详情使用短时缓存;订单和库存保留必要索引,避免为了所有筛选组合建立大量索引。这类方案改造成本低,通常能够解决大部分早期性能问题。
取舍是部分后台功能不再“随时查全量历史数据”,运营人员需要接受时间范围和导出任务的限制。对日常电商来说,这是用少量操作等待换取交易系统稳定,通常值得。
如果系统存在明确活动窗口,产品经理应至少提前四周冻结核心链路,完成热点商品识别、读写预算、缓存预热、限流规则和降级演练。活动前不要继续向商品详情和订单页叠加非核心实时功能。
高峰期可以把商品基础信息、类目、活动规则和展示销量放到高吞吐访问层;库存展示使用快照;库存扣减仍走交易路径;推荐、评论、实时访客和营销画像允许降级。所有重试都要设置上限,因为客户端和网关的无控制重试会把短暂故障放大成数据库洪峰。
取舍是用户可能看到短暂延迟的销量、旧的评价数或隐藏的推荐模块,但关键交易仍然可用。活动系统追求的不是页面功能完整度,而是订单、支付和库存链路的确定性。
秒杀场景不适合让所有用户直接竞争数据库中的同一库存行。产品层应先定义排队、资格校验、令牌、限购和失败反馈。只有通过资格筛选的请求,才应该进入库存扣减链路。
库存可以采用预分配、分段库存或库存令牌等方式降低单点热点,但这些方案会引入库存回收、超时释放、异常补偿和对账复杂度。产品经理必须接受一个事实:秒杀系统的核心体验不是“所有人都能实时看到最终库存”,而是让用户获得明确、可解释且不重复扣款的结果。
取舍是部分用户需要排队,库存显示可能是“即将售罄”而不是精确数字,订单结果也可能延迟几秒返回。相较于数据库被击穿、订单重复和售后失控,这种可控的不确定性更适合极端高峰。
多商户系统的热点不一定来自商品,也可能来自某个大型商户的批量导入、库存同步和订单报表。如果所有商户共用一套查询和连接资源,一个大商户的任务就可能影响其他商户的交易。
产品经理需要定义租户级别的配额和任务优先级。例如批量导入限制并发数,报表生成进入异步队列,单个商户的导出任务不能占满公共连接池,管理员查询只能访问授权租户。数据层面则要明确租户字段、隔离边界、跨租户统计权限和归档规则。
取舍是大型商户的批量任务不会立即完成,复杂报表需要等待异步生成。但这种限制保护了平台整体可用性,也让服务等级能够被清晰管理。
对于贵重商品、预售商品、定制商品和强履约业务,库存、价格、配送承诺和订单状态的错误成本很高。此时不应为了追求极限吞吐而过度使用最终一致方案。
产品设计应优先保证状态机清晰、操作幂等、每次变更可追溯,并为异常订单保留人工介入入口。页面展示可以使用缓存,但结算、支付和库存承诺必须重新校验。必要时可以采用排队和人工审核,换取更低的业务错误率。
取舍是峰值吞吐可能低于普通商品系统,用户也可能经历更长的确认时间。但对高客单价业务,少量延迟通常比一次错误扣款、错发货或库存承诺失败更容易接受。

交易表最重要的是记录业务事实和状态变化。订单主表应能够说明订单当前状态、金额口径、用户和商户关系;订单明细应保存下单时的商品快照,而不是完全依赖商品当前信息;库存流水应记录变动类型、关联单号、变动前后数量和操作来源。
不要为了减少表数量,把订单、支付、优惠、物流和售后强行塞进一张宽表。宽表可能让某些查询看起来方便,却会导致字段频繁更新、行变得很宽、并发冲突增加,也不利于追踪状态变化。
但也不要机械地追求高度拆分。一个低频、强关联且总是一起读取的数据,过度拆表会增加接口拼装和事务协调成本。合理设计取决于访问模式、更新频率和一致性边界,而不是“表越少越好”或“范式越高越好”。
订单、商品和消息列表很容易出现深分页问题。传统的 offset 分页在翻到很深的位置时,数据库需要先扫描并跳过大量记录,再返回目标页。用户可能只看到第 20 页,但数据库已经为此前的 19 页付出了成本。
对于按时间倒序的订单列表,可以使用基于游标的分页:记录上一页最后一条数据的时间和唯一编号,下一页从该位置继续查询。产品上需要接受“不能直接跳到第 100 页”,并提供按时间筛选、搜索和导出等替代能力。
SELECT id, order_no, status, total_amount, created_at FROM orders WHERE user_id = ? AND created_at < ? AND (created_at, id) < (?, ?) ORDER BY created_at DESC, id DESC LIMIT 50;
这段示例表达的是游标分页的业务思想:用稳定排序和上一页边界继续读取,而不是让数据库反复跳过大量历史记录。实际字段、索引和数据库语法需要由研发根据具体环境验证。
高峰期网络抖动会带来重复提交。用户点击一次后没有立即看到结果,可能再次点击;网关超时后可能自动重试;支付平台回调也可能重复发送。如果产品需求只描述“点击后创建订单”,而没有定义重复请求的结果,研发很难确定幂等键和状态返回规则。
产品经理应明确哪些动作必须幂等:创建订单、领取优惠券、支付回调、库存扣减、退款申请和物流状态同步通常都需要。幂等结果也要定义清楚:重复请求是返回原订单、返回已领取提示、返回处理中,还是拒绝再次操作。
软删除适合需要恢复或保留关联关系的数据,但会让所有查询都增加状态条件,长期积累后还会影响索引选择和数据体量。状态字段适合表达业务生命周期,不应该被当作删除标记使用。历史记录则应记录事实变化,不能只靠当前状态推断过去发生了什么。
例如订单从待支付变成已取消,订单主表保存当前状态,订单状态历史表保存每次变化。这样既方便当前查询,也支持售后、审计和异常排查。产品经理要确认哪些历史信息属于用户可见、客服可见和内部审计可见,避免后期为了追查问题临时补日志。

数据库方案至少涉及四种成本:运行成本、开发成本、数据错误成本和运营成本。缓存和异步化可能降低运行压力,却增加一致性处理和排障复杂度;分库分表可以扩大容量,却会让跨库查询、数据迁移和报表统计更困难;强一致事务能够降低业务歧义,却可能牺牲吞吐和可用性。
我会在评审中要求团队把方案放到一个四象限里:业务价值高且错误成本高的动作,优先保证正确性;业务价值高但可排队的动作,优先保证可恢复性;业务价值低且访问量高的动作,优先缓存和降级;业务价值低且访问量低的动作,优先减少开发投入。
| 方案 | 性能收益 | 新增复杂度 | 适合场景 | 主要代价 |
|---|---|---|---|---|
| 短时缓存 | 显著降低重复读取 | 低至中 | 商品详情、类目、展示统计 | 数据可能短暂过期,需要失效策略 |
| 异步聚合 | 降低同步写放大 | 中 | 销量、积分、行为统计 | 需要处理消息延迟和重复消费 |
| 读写分离 | 扩大读取承载能力 | 中 | 查询明显多于写入的系统 | 存在复制延迟,不能承接强一致读取 |
| 分库分表 | 突破单库容量和并发边界 | 高 | 数据量和写入量长期增长的核心系统 | 跨分片查询、扩容和迁移复杂 |
| 排队与令牌 | 控制极端热点写入 | 高 | 秒杀、限量抢购、集中开售 | 用户需要等待,库存回收和补偿复杂 |
最终一致适合那些“信息有价值,但短暂不准确不会造成交易错误”的数据,例如销量、评价数、热度、推荐结果、访客数和部分物流展示。选择最终一致前,要明确最大允许延迟、异常时展示什么,以及数据最终不一致时如何校正。
最终一致不等于“不保证一致”。它仍然需要事件顺序、重复消费处理、失败重试、对账任务和最终校验。产品经理应把“最终会一致”拆成可验收的时间和范围,例如“99% 的销量展示在 30 秒内更新,超过 5 分钟的异常进入补偿队列”。
涉及支付金额、库存扣减、优惠券核销、退款状态和账户余额的数据,通常应优先保证强一致或可证明的业务一致性。这里的强一致不是所有模块都放在一个超大事务里,而是关键状态变化必须具备明确的原子性和幂等性。
如果一个业务动作跨越多个服务,无法用单一事务覆盖,产品经理应与研发共同设计状态机和补偿机制,而不是简单要求“全部成功或全部失败”。例如订单创建成功但优惠券服务超时,可以进入待确认状态并在限定时间内补偿,而不是让用户重复提交多个订单。
当系统主要问题是无边界查询、同步统计、缓存失效或热点设计错误时,分库分表往往不是第一选择。它会增加开发、测试、运维和数据治理成本,如果原有访问模式没有改变,新的分片结构仍然可能被错误查询拖慢。
我建议至少满足三个条件后再认真评估分库分表:单实例容量接近明确上限;读写优化和数据分层已经完成;业务能够接受跨分片查询和数据治理复杂度。否则,先解决需求边界和访问模型,通常投入产出比更高。

把首页、搜索、商品详情、购物车、结算、支付、订单、售后和后台报表列出来。对每个功能记录访问者、峰值时段、请求类型、数据新鲜度和失败后的用户体验。不要一开始讨论数据库类型,先把业务动作和优先级说清楚。
为每个核心页面记录一次用户动作产生的接口数量、主库读取次数、写入类型和同步下游动作。对无法准确统计的部分,先使用压测、日志采样或链路追踪获得基线,再设定目标。
预算不需要一开始就非常精确,但必须可验证。例如,商品详情主库读取不超过两次,订单创建同步写入不超过三类交易数据,报表查询不得访问交易主库,销量允许 30 秒延迟。这样的约束足以阻止大量低价值功能无边界增长。
至少准备日常、活动、热点集中和故障恢复四种场景。每种场景都写清楚用户比例、峰值时间、热点对象、读写比例和系统目标。然后建立降级矩阵,明确每个模块在缓存故障、数据库延迟、消息堆积和下游超时时的表现。
| 故障或压力状态 | 必须保留 | 可以降级 | 应避免的行为 |
|---|---|---|---|
| 缓存大面积失效 | 订单创建、库存校验、支付回调 | 推荐、销量、评价和个性化内容 | 所有请求无控制回源 |
| 交易库连接紧张 | 支付状态、库存扣减、订单状态变更 | 历史订单、运营报表和行为写入 | 后台继续执行全量导出 |
| 消息队列堆积 | 核心订单事件和支付事件 | 营销通知、统计和画像更新 | 无限重试导致堆积扩大 |
| 从库延迟升高 | 强一致交易读取 | 非关键列表和历史统计 | 将延迟数据用于库存或支付判断 |
压测脚本要按用户路径而不是接口清单设计。建议至少包含 70% 浏览型流量、20% 加购或试算流量、8% 下单流量和 2% 支付或状态查询流量,再根据实际业务修正比例。对秒杀业务,还要额外模拟同一商品、同一时间窗口的集中请求。
压测结束后,不要只生成一张吞吐报告。必须核对订单数量、库存流水、优惠券核销、支付状态和消息消费结果是否一致。高峰性能验收的最后一关永远是业务账是否对得上,而不是某个接口是否达到预设 QPS。
这是我最推荐但最容易被忽略的一步。上线前逐个检查高峰期间的页面模块,问三个问题:没有这个模块是否影响交易?它是否必须实时?它是否值得消耗交易数据库资源?如果三个问题的答案都不明确,就应该暂时隐藏、缓存、异步化或延迟上线。
高峰保障不是展示团队技术能力的机会,而是一次资源优先级管理。删掉一个低价值实时查询,有时比增加数据库实例更有效,也比上线后紧急排查锁等待更便宜。

第一,数据库性能问题经常由产品需求中的“实时、全部、立即、无限制”触发。产品经理越早定义边界,研发越容易设计出可承受峰值的访问模型。
第二,高峰性能不等于所有功能都保持正常。真正成熟的系统,会主动牺牲推荐、统计、评价、报表等低优先级能力,把连接、CPU、锁和写入资源留给订单、库存和支付。
第三,数据库一致性必须跟业务错误成本匹配。销量晚几十秒通常不是事故,支付金额错一分钱也可能是事故。把所有数据都设计成同一种实时性和一致性,既昂贵又不必要。
我最终想强调的是:把数据库设计转化为高峰性能保障,关键不是产品经理亲自画出最复杂的架构图,而是持续管理数据的价值、时效、一致性、访问方式和失败边界。当这些内容被写进需求、评审、测试和复盘流程,数据库就不再是上线前才被动检查的技术黑盒,而会成为产品决策的一部分。
下一步,建议先从一个真实高频链路开始,不要同时改造整个系统。选择商品详情到下单,或订单列表到售后查询,完成一次读写预算、数据分层、降级设计和混合压测。只要团队能把一个链路从“页面功能”讲清楚到“数据库压力和业务结果”,就已经迈出了高峰性能治理最重要的一步。
我以前一直以为数据库性能主要是后端和架构师的事情,产品经理只要把业务需求写清楚就够了。但在一次大促项目中,订单、库存和优惠券同时出现峰值,系统慢下来后才发现,很多性能问题其实在产品定义阶段就已经埋下了。
产品经理不能只评审“有没有功能”,还要评审每个核心数据对象在高峰期会被怎样读写。我的做法是把需求文档中的业务动作,转换成“数据动作”:创建订单对应一次写入,查询订单对应索引读取,扣减库存对应带条件更新,优惠券核销对应并发竞争。
在一次日订单约12万、峰值每秒订单请求约1800次的项目中,我们将订单列表、库存扣减和营销规则拆开评估。测试发现,订单列表按用户、状态、创建时间组合查询时,如果只建立用户ID索引,平均响应时间为420毫秒;增加状态和创建时间的联合索引后,平均响应时间降至95毫秒,但索引写入成本上升约8%。
这个取舍必须由产品经理结合查询频率和业务优先级确认,而不是默认“索引越多越好”。
业务场景主要数据操作产品经理应确认的指标常见风险 商品详情高频读取峰值QPS、缓存命中率大字段拖慢查询 库存扣减并发更新成功率、锁等待时间超卖或长事务 订单列表条件分页查询95分位响应时间深分页导致全表扫描 售后查询低频复杂读取可接受延迟与交易库争抢资源 真正有效的数据库设计评审,不是逐字段讨论命名,而是要求每张核心表回答四个问题:谁会访问它、访问频率是多少、是否允许延迟、数据增长到什么规模仍要稳定。
只要这四个问题没有答案,表结构看起来再规范,也不能证明它能扛住高峰。
我曾经参与过一个促销项目,库存字段看起来只有一个数字,业务方也要求“下单时直接减库存”。结果压力测试一上来,数据库锁等待明显增加,少量请求还出现库存扣成负数的问题。我想知道,产品经理在需求阶段究竟应该把哪些约束写死?
库存不是一个普通字段,而是一个并发控制对象。产品经理至少要区分“可售库存、锁定库存、已售库存、退回库存”四种状态,并明确每种状态的变化时机,否则开发人员很容易用一条简单的减法语句覆盖复杂业务。我在测试中对比过两种方案。方案一先查询库存,再执行扣减;
方案二直接执行带条件的原子更新,例如“库存大于0时才扣减”。在800个并发请求争抢100件库存时,方案一出现了超卖,方案二虽然有部分请求失败,但库存始终没有低于零。对电商系统而言,后者通常更符合业务目标,因为失败订单可以重试,错误库存却会引发履约和客诉成本。
产品需求中建议明确以下规则:库存扣减是否必须实时、锁库存多久自动释放、支付失败是否回补、取消订单是否立即回补、同一用户是否有限购、库存不足时前端展示什么状态。尤其要写清楚“订单创建成功”与“库存扣减成功”是不是同一个事务边界,这会直接影响数据库锁的持有时间。
设计方式优点缺点适用判断 先查后扣逻辑直观并发下容易超卖仅适合低并发或非关键库存 条件更新防止负库存失败请求需要重试适合大多数交易库存 锁定库存便于支付流程管理需要超时释放机制适合支付链路较长的场景 独立库存服务隔离交易库压力一致性设计复杂适合高峰流量和多渠道销售 我的判断是,产品经理不必亲自决定使用哪一种数据库锁,但必须把库存状态机、并发结果和失败补偿写成可验收规则。
数据库方案可以变化,业务不变量不能变化。
我接触过一个项目,团队还没有完成索引优化和慢查询治理,就直接讨论分库分表,认为只要把数据拆开,性能自然会提升。后来上线前发现报表、订单搜索和售后关联查询都变得复杂,开发成本反而大幅增加。产品经理应该如何判断什么时候真的需要拆分?
分库分表不是性能优化的起点,而是单体数据库在数据规模、写入压力或故障隔离方面已经触顶后的结构性方案。很多团队把它当成“高峰性能开关”,实际上如果慢查询来自错误分页、无效关联或重复计算,拆分只会把问题扩散到更多节点。我通常先要求团队完成三轮验证:第一轮检查慢查询和执行计划;
第二轮验证索引、缓存、读写分离和历史数据归档;第三轮做接近真实流量的压测。如果单库在目标峰值下CPU约55%、连接池使用率约60%、95分位查询延迟低于150毫秒,就没有必要为了“看起来先进”提前分表。
只有当订单表达到数亿级、单表写入持续成为瓶颈,或者某类交易流量会影响用户查询和后台运营时,才应进入拆分评估。评估时产品经理要特别关注跨分片查询、全局订单号、分页排序、数据导出、售后关联和数据迁移,这些往往比“表能不能拆”更决定项目成败。
阶段优先动作产品经理的判断标准 性能初期问题慢查询、索引、分页优化是否存在明显低效SQL 读压力较高缓存、读写分离、数据归档读写是否可以接受短暂延迟 单表持续膨胀按时间或业务维度拆分查询是否有稳定分片键 多业务互相影响独立数据库或服务是否需要故障隔离 我的经验是,最稳妥的拆分依据不是团队偏好,而是访问模式。
订单详情通常按订单号定位,用户订单通常按用户和时间查询,运营报表则可能按时间聚合。若一个拆分方案无法同时解释这些查询如何执行,就说明它还没有达到上线条件。
过去做需求验收时,我常用“页面能打开、接口能返回”作为判断标准,直到一次活动期间页面虽然没有报错,但用户提交订单要等待6秒,客服系统也因为查询变慢无法处理售后。我现在想把数据库设计真正纳入产品验收,应该制定哪些可量化指标?
高峰性能验收不能只看平均响应时间,因为平均值会掩盖少数极慢请求。产品经理至少要同时关注成功率、95分位和99分位延迟、数据库连接池占用、锁等待、慢查询数量以及高峰后恢复时间。我在项目中使用过一张“业务动作,性能指标”表,而不是只写一条“系统支持每秒多少请求”。
例如商品详情关注缓存命中率和读取延迟,创建订单关注成功率和库存一致性,后台导出关注异步任务完成时间。这样做的好处是,测试结果能直接对应业务决策,而不是让产品、开发和测试各自解释一套数据。
业务动作建议验收指标示例目标不合格信号 商品详情95分位响应时间不高于200毫秒缓存命中率下降且数据库读升高 创建订单成功率与库存一致性成功率不低于99.9%出现超卖或重复订单 订单查询99分位响应时间不高于800毫秒深分页越翻越慢 高峰恢复恢复时间流量下降后10分钟内恢复连接池持续耗尽 压测数据还要覆盖真实业务比例,不能只压一个接口。
一次有效测试至少应包含登录、商品浏览、加购、下单、支付回调、订单查询和售后查询,并模拟促销开始前后的流量突增。我们曾发现,单独压下单接口结果很好,但加入后台报表查询后,数据库锁等待增加了近3倍,原因是报表扫描影响了交易表。
最终验收时,建议把“性能阈值、测试流量、数据规模、失败处理、降级策略、责任人”全部写入需求或项目验收单。只有指标可复现、场景可重跑,数据库设计才真正从技术文档变成了高峰性能保障。


读者评论
文章把“高峰性能”从服务器配置问题转回需求管理,这个角度比较实用。尤其是把峰值QPS、写入TPS和热点集中度拆开,能避免只看活动总人数。实际项目中,前20个商品占大部分流量时,平均性能确实很容易掩盖局部热点。
库存状态机和库存口径的部分值得重点关注。详情页展示库存、下单校验库存和实际扣减库存如果没有统一定义,即使数据库没有故障,也会出现“显示有货但无法购买”。产品经理提前明确状态变化、幂等和释放规则,能减少很多返工。
文章对读放大、写放大的解释比较贴近电商场景。不过文中的压测数据来自匿名项目和情景模拟,不能直接当作行业标准,落地时仍需结合自身接口链路、数据规模和P95/P99指标验证。把非核心日志、积分等操作异步化,也要同步评估一致性和补偿机制。