📖 本篇简介:阿里系(电商/支付/云)面试的 MySQL 题有明显的"高并发 + 规模化"烙印——很少让你背概念,几乎都是在亿级表、大促、秒杀、分库分表的语境里追问。本篇精选 20 道公开高频题型,覆盖高并发下单、全局 ID、路由键、分布式事务、扣库存防超卖、热点行、主从延迟、跨库分页、缓存一致性、扩容双写等。题目均为公开面经常见类型,解析讲通用原理,不涉及任何公司未公开信息。前置知识:第 3、5、15、17、18 章;本系列示例基于 MySQL 8.0。
目录
- 1 本篇考情与知识地图
- 题目 1:高并发下单场景数据库怎么扛
- 题目 2:分库分表后全局唯一ID怎么生成
- 题目 3:分片路由键怎么选才不数据倾斜
- 题目 4:分布式事务有哪些方案怎么选
- 题目 5:数据库中间件和应用层分片区别
- 题目 6:秒杀扣库存怎么防超卖
- 题目 7:热点行热点更新把库打满怎么办
- 题目 8:读写分离后主从延迟读到旧数据怎么办
- 题目 9:分库分表后跨库分页排序怎么做
- 题目 10:缓存与数据库双写一致性怎么保证
- 题目 11:分库分表从8库扩到16库怎么做
- 题目 12:亿级订单表如何设计与归档
- 题目 13:本地消息表实现最终一致
- 题目 14:红包余额这类热点账户并发怎么扣
- 题目 15:数据库连接数被打满怎么处理
- 题目 16:自增主键和分布式ID怎么取舍
- 题目 17:一次大促前数据库容量怎么评估
- 题目 18:MySQL和ES缓存HBase怎么分工
- 题目 19:分库分表后跨库Join怎么办
- 题目 20:高并发下慢SQL治理体系怎么建
- 速查速记
- 常见坑与注意事项
- 复习清单
- 练习题
1 本篇考情与知识地图
阿里系面试 MySQL 的典型画风:不给你"是什么",直接给你"双十一这一秒十万单打进来你怎么办"。所以答题要从"单机原理"立刻切换到"规模化工程"视角。先建立下面这张地图,再逐题拆:
| 维度 | 单机阶段 | 阿里系规模化阶段 |
|---|---|---|
| 主键 | 自增 INT/BIGINT | 分布式 ID(号段/雪花) |
| 库表 | 单库单表 | 分库分表 + 中间件路由 |
| 一致性 | 本地事务 ACID | 最终一致 + 本地消息表/可靠消息 |
| 读 | 主库查 | 缓存 + 只读副本 + 归档 |
| 写 | 直接 update | 限流 + 扣减前置(Redis)+ 异步落库 |
| 扩容 | 纵向升配 | 双写迁移 + 数据校验 + 灰度切流 |
记住一条主线:能不加数据库压力就不加,能异步就不同步,能缓存就不查库。下面 20 题都围绕这条主线展开。
题目 1:高并发下单场景数据库怎么扛
考点
- 读写分离、分库分表的触发时机;
- 把同步链路改成异步链路;
- 最终一致性思维。
参考答案
1. 垂直拆分:按业务域(订单、库存、支付)拆库,避免互相争抢资源;
2. 水平拆分:订单表按用户 ID 或订单 ID 哈希分 8/16/32 库,单库控制在千万级;
3. 读写分离:下单写主库,订单列表/详情走只读副本,热点详情再挡一层 Redis;
4. 异步化:下单主链路只做"创建订单 + 扣库存"等必要写,后续积分、通知、风控异步化(MQ);
5. 削峰:大促用消息队列把瞬时写洪峰拉平,DB 按可承受速率消费;
6. 限流降级:DB 连接数、TPS 到达阈值时提前在网关/应用层限流,别把压力灌进数据库。
-- 下单主链路只保留必要写,本示例基于 MySQL 8.0
INSERT INTO t_order(user_id, order_no, amount, status, created_at)
VALUES (1001, '20261007100001', 99.00, 1, NOW()); -- 状态1=待支付,后续事件走MQ异步
追问
- 问:什么数据必须同步写库?答:扣减库存、创建订单这类资金/状态强相关的必须同步;营销、通知、积分异步。
- 问:异步了会不会丢?答:用本地消息表/事务消息保证"DB 提交与消息发送"的一致性(见题目 13)。
题目 2:分库分表后全局唯一ID怎么生成
考点
- 为什么不能用自增主键;
- 常见方案与优缺点。
参考答案
1. UUID:简单但无序,作主键导致页分裂、索引膨胀,不推荐;
2. 数据库号段模式:一段一段取 ID(如每次取 1000 个内存自增),简单可靠,依赖一个号段库;
3. 雪花算法 Snowflake:时间戳 + 机器 ID + 序列号,趋势递增、本地生成无中心,需解决时钟回拨;
4. 号段 + 雪花组合:业务订单号用雪花保证趋势递增,内部主键可再叠加库表后缀定位分片;
5. 要求:ID 要"全局唯一、趋势递增、高可用、时钟依赖可接受"。
-- Snowflake 64 位结构(示意)
| 1 符号位 | 41 位时间戳(ms) | 10 位机器ID | 12 位序列号 |
-- 约 69 年可用,单机每毫秒 4096 个
追问
- 问:时钟回拨怎么办?答:记录上次时间戳,回拨超过阈值就报错/等待,或用号段兜底。
- 问:订单号里要不要带库表信息?答:业务订单号与分片键解耦;若需路由可内嵌分片键,但会暴露规模。
题目 3:分片路由键怎么选才不数据倾斜
考点
- 分片键选择对查询与扩容的影响;
- 数据倾斜识别。
参考答案
1. 优先选高离散、高频查询过滤列:如 user_id、order_id,让大部分 SQL 能定位到单分片;
2. 避免用时间片做哈希键:否则近期数据全压在少数分片(热点);
3. 避免用低基数列:如 status、性别做键必然倾斜;
4. 兼顾查询模式:订单常按用户查就用 user_id 分片;运营常按商家查就用 merchant_id——二选一,另一个维度用"维度冗余表/ES"兜底;
5. 扩容友好:采用一致哈希或预留分片数,避免二次迁移全量重hash。
追问
- 问:按 user_id 分片,商家维度查订单怎么办?答:建一张 merchant_id → order_id 的路由索引表,或把订单同步一份到 ES 供运营多条件检索。
- 问:怎么发现倾斜?答:监控各分片容量/QPS,单分片远超均值就是倾斜。
题目 4:分布式事务有哪些方案怎么选
考点
- CAP/BASE;
- 2PC/TCC、本地消息表、最大努力通知对比。
参考答案
1. 2PC/XA:强一致但同步阻塞、性能差,互联网少用;
2. TCC(Try-Confirm-Cancel):业务层两阶段,侵入大,适合资金类强一致;
3. 本地消息表(最终一致):本地事务同时写业务表和消息表,后台任务投递 MQ,消费方幂等——最常用;
4. 事务消息(半消息):MQ 提供半消息机制,本地事务成功再投递;
5. 最大努力通知:定时对账 + 补偿,适合弱一致场景;
6. 选型原则:能最终一致就不用强一致;资金类用 TCC,普通业务用本地消息表。
追问
- 问:消费方重复消费怎么办?答:消费端幂等(唯一业务号去重表 / 状态机只允许单向流转)。
- 问:消息投递失败一直重试?答:重试到上限进死信队列,人工/对账兜底。
题目 5:数据库中间件和应用层分片区别
考点
- Proxy 层 vs SDK 层;
- 运维与性能权衡。
参考答案
1. 中间件 Proxy(如 ProxySQL/ShardingSphere-Proxy):对应用透明,统一运维、可灰度,但多一层网络跳转、运维复杂度高;
2. 应用层 SDK(ShardingSphere-JDBC 等):进程内路由,性能好、灵活,但每个应用都要升级 SDK;
3. 选择:应用语言栈多、想统一治理用 Proxy;单语言、追求极致性能用 SDK;
4. 代价:跨库 Join、分布式事务、跨库分页都被中间件弱化或不支持,业务要自己规避。
追问
- 问:中间件本身会不会成单点?答:Proxy 无状态集群部署,前面挂负载均衡;故障时可直连分片兜底。
- 问:中间件带来哪些坑?答:SQL 兼容性(不支持部分跨库语法)、连接膨胀、排查链路变长。
题目 6:秒杀扣库存怎么防超卖
考点
- 行锁粒度;
- 乐观锁/原子扣减;
- 库存前置。
参考答案
1. DB 原子扣减:UPDATE stock SET count = count - 1 WHERE sku_id = ? AND count > 0,靠条件 count>0 防超卖,不用先查后改;
2. 乐观锁版本号:UPDATE ... SET count=count-1, version=version+1 WHERE sku_id=? AND version=?;
3. 库存前置 Redis:秒杀量级直接在 Redis 预扣,异步落库,DB 只做最终核对,扛住洪峰;
4. 限流排队:网关层按商品限流,只放 N 个请求到扣减环节;
5. 兜底对账:异步核对 Redis 与 DB 库存,纠正误差。
-- 原子扣减:一条SQL同时完成判断与扣减,本示例基于 MySQL 8.0
UPDATE t_stock
SET stock_count = stock_count - 1, update_time = NOW()
WHERE sku_id = 10086 AND stock_count > 0; -- 受影响行数=0 即售罄
追问
- 问:为什么不先 SELECT 再 UPDATE?答:两个事务间隙会超卖(写覆盖写),必须在一条 SQL 里原子完成。
- 问:Redis 预扣和 DB 不一致怎么办?答:下单成功才异步扣 DB,失败回补 Redis;定时对账校正。
题目 7:热点行热点更新把库打满怎么办
考点
- 单行更新的行锁串行化;
- 热点拆分、异步合并。
参考答案
1. 热点拆分(分桶):把一行库存拆成 N 个分桶行(stock_1...stock_N),请求随机路由到桶,再汇总,把单行锁打散;
2. 读写分离 + 缓存:读走 Redis,写尽量少回表;
3. 异步合并写:计数类先写 Redis/内存,定时批量刷回 DB;
4. 降低锁持有时间:事务里别夹带 RPC、别在锁内做重逻辑;
5. 临时方案:热点 key 本地缓存 + 最终一致,或把热点数据迁到专用 KV。
-- 库存分桶:把单行拆成 10 行,打散行锁竞争
UPDATE t_stock_bucket
SET stock = stock - 1
WHERE bucket_id = FLOOR(RAND() * 10) AND sku_id = 10086 AND stock > 0;
追问
- 问:分桶后怎么查总库存?答:
SELECT SUM(stock) FROM t_stock_bucket WHERE sku_id=?,或用 Redis 维护总数。 - 问:极端热点(单个爆款)呢?答:库存直接全放 Redis,DB 只做对账快照。
题目 8:读写分离后主从延迟读到旧数据怎么办
考点
- 主从复制异步性;
- 一致性读策略。
参考答案
1. 写后读走主库:写操作后的短时间内(如创建订单后查详情)强制路由主库;
2. 等待主从同步:半同步复制,或读从库前 MASTER_POS_WAIT 等待位点追上;
3. 关键读主:支付状态、余额这类强一致读一律走主库;
4. 缓存兜底:写后失效/更新缓存,读走缓存绕开从库延迟;
5. 监控延迟:Seconds_Behind_Master 超阈值把读流量切回主库。
追问
- 问:怎么判断该走主还是走从?答:按业务容忍度——能容忍秒级旧数据走从,不能容忍走主或走缓存。
- 问:半同步有什么代价?答:主库提交要等至少一个从库 ACK,写入延迟略增,换取一致性。
题目 9:分库分表后跨库分页排序怎么做
考点
- 跨库
LIMIT 0,10的正确性; - 归并排序思想。
参考答案
1. 禁止跨库深分页:LIMIT 100000,10 要每个分片取前 100010 条再归并,代价极大;
2. 业务上避免跨库翻页:尽量按分片键查(用户自己的订单只落一个分片);
3. 必须跨库时:用"每分片取前 N → 内存归并排序",且 N 不能大;
4. 运营检索走 ES:复杂多条件分页/排序全部丢给 Elasticsearch,MySQL 只存明细;
5. 游标/游标键分页:用 WHERE create_time < ? ORDER BY create_time LIMIT 10 替代 OFFSET。
追问
- 问:归并为什么不能深度翻页?答:翻得越深每个分片取的越多,网络和内存开销线性爆炸。
- 问:ES 和 MySQL 怎么同步?答:binlog 订阅(Canal)异步同步到 ES,容忍短暂延迟。
题目 10:缓存与数据库双写一致性怎么保证
考点
- 先更新库还是先删缓存;
- 延迟双删、binlog 订阅。
参考答案
1. 通用策略:先更新数据库,再删除缓存(Cache-Aside);
2. 删缓存失败要兜底:失败重试队列 / 订阅 binlog 异步删;
3. 延迟双删:更新库后删一次缓存,隔几百毫秒再删一次,覆盖"读旧库回填旧缓存"的窗口;
4. 设置过期时间:作为最终兜底,不一致最多持续到过期;
5. 强一致场景:加分布式锁或直接读库,别指望缓存。
-- Cache-Aside 写路径
写:更新DB -> 删除缓存(失败进重试队列)
读:读缓存miss -> 查DB -> 回填缓存(带TTL)
追问
- 问:为什么是删缓存而不是更新缓存?答:更新缓存可能并发覆盖,且很多缓存值是计算得来的,删除更简单安全。
- 问:先删缓存再更库行不行?答:有窗口导致旧值回填,一般不推荐。
题目 11:分库分表从8库扩到16库怎么做
考点
- 不停机扩容;
- 双写 + 数据迁移 + 校验。
参考答案
1. 改分片规则:从 8 库 hash 变 16 库,多数数据需要重新分布;
2. 双写迁移:新写同时写新旧两套,老库历史数据用离线/增量工具搬运;
3. 追平增量:迁移期间老库 binlog 订阅回放新库;
4. 数据校验:迁移后抽样/全量 count 与 checksum 比对;
5. 灰度切读:先把读流量按比例切到新库,观察无误再切写;
6. 回滚预案:出问题把切流开关打回老库。
追问
- 问:怎么避免二次全量迁移?答:一开始就按 2 的幂预留分片数,或用一致性哈希减少迁移量。
- 问:双写期间一边写失败怎么办?答:以一边为准,异步补偿对账,不能让两分支不一致。
题目 12:亿级订单表如何设计与归档
考点
- 冷热分离;
- 归档策略。
参考答案
1. 按时间 + 用户双维度:近期热数据留在线库,历史冷数据归档到归档库/列存;
2. 分区表:按月份 RANGE 分区,查询只扫相关分区,归档直接 DROP 分区;
3. 只留必要字段:在线库瘦身,大字段/明细转存对象存储;
4. 定时归档任务:夜间把 N 个月前数据迁到归档库,在线库删除;
5. 归档表可查:归档库提供只读查询入口,别让业务"找不到老订单"。
-- 按月份分区,本示例基于 MySQL 8.0
CREATE TABLE t_order (
id BIGINT, user_id BIGINT, amount DECIMAL(10,2), status TINYINT,
created_at DATETIME, KEY idx_user(user_id)
) PARTITION BY RANGE (TO_DAYS(created_at)) (
PARTITION p202609 VALUES LESS THAN (TO_DAYS('2026-10-01')),
PARTITION p202610 VALUES LESS THAN (TO_DAYS('2026-11-01'))
);
追问
- 问:归档删数据会不会误删?答:先迁移到归档库校验再删,删前备份;按分区 DROP 比 DELETE 快且可恢复。
- 问:跨分区查询慢吗?答:不带时间条件会扫全分区,查询必须带时间范围。
题目 13:本地消息表实现最终一致
考点
- 本地事务 + 消息投递;
- 幂等消费。
参考答案
1. 同事务写业务表 + 消息表:保证业务和消息要么都成功要么都失败;
2. 后台任务扫消息表:把未投递消息发到 MQ,成功后标记已发;
3. 消费方幂等:按消息唯一 ID 去重,重复消费不产生副作用;
4. 失败重试 + 死信:重试 N 次进死信,人工介入;
5. 好处:不依赖 MQ 事务特性,普通 MQ 即可实现最终一致。
-- 本地消息表:与业务同事务写入,本示例基于 MySQL 8.0
CREATE TABLE t_msg (
msg_id VARCHAR(64) PRIMARY KEY, -- 全局唯一消息号,消费端幂等键
topic VARCHAR(128), payload TEXT,
status TINYINT DEFAULT 0, -- 0待投递 1已投递 2失败
retry_cnt INT DEFAULT 0,
next_retry_time DATETIME,
KEY idx_status_time(status, next_retry_time)
);
追问
- 问:消息表会不会膨胀?答:已成功消息定期清理;按状态时间索引快速捞待发。
- 问:和事务消息比?答:事务消息对 MQ 有要求,本地消息表通用、可控、易排查。
题目 14:红包余额这类热点账户并发怎么扣
考点
- 行锁竞争;
- 乐观锁 / 分段余额。
参考答案
1. 乐观锁扣减:UPDATE account SET balance=balance-?, version=version+1 WHERE uid=? AND version=? AND balance>=?;
2. 悲观锁:SELECT ... FOR UPDATE 适合强一致但并发低;
3. 余额分桶:把一个余额拆多个子账户,扣款随机命中,汇总对账;
4. 异步记账:先异步流水,最终汇总到余额表,削峰;
5. 对账兜底:流水汇总 = 余额,每日校验。
追问
- 问:余额不能扣成负数怎么保证?答:SQL 里带
balance >= ?条件,受影响行=0 即余额不足。 - 问:乐观锁冲突高怎么办?答:冲突重试有限次,或改分桶降低冲突。
题目 15:数据库连接数被打满怎么处理
考点
- max_connections;连接池;
- 慢 SQL 占连接不释放。
参考答案
1. 先止血:kill 长时间空闲/慢查询连接,临时调大 max_connections;
2. 定位根因:多半是慢 SQL 或事务太长占着连接不放,不是真的并发高;
3. 连接池治理:应用侧 HikariCP 池大小合理(不是越大越好),控制等待超时;
4. 短事务:避免事务里夹 RPC,缩短连接占用;
5. 中间件复用连接:Proxy 层复用后端连接,避免每个应用连都打满;
6. 监控:Threads_connected / Threads_running 告警。
追问
- 问:连接池设多大?答:参考"CPU 核数*2 + 有效磁盘数",过大反而上下文切换更慢。
- 问:为什么不是越大越好?答:每个连接都占内存,且并发线程越多 CPU 调度和锁竞争越重。
题目 16:自增主键和分布式ID怎么取舍
考点
- 自增的局限;
- 趋势递增的意义。
参考答案
1. 单库自增:简单、有序、紧凑,但分库后无法全局唯一;
2. 分库后:必须用分布式 ID,且要趋势递增(B+ 树顺序插入减少页分裂);
3. 纯随机 UUID:无序导致页分裂、二级索引臃肿,不做主键;
4. 号段/雪花:趋势递增 + 全局唯一,是分库场景首选;
5. 业务键:订单号可独立设计(含时间/渠道),与内部主键解耦。
追问
- 问:自增主键在高并发下有什么问题?答:自增锁竞争(8.0 已优化为轻量 mutex)、主从切换需注意自增重复。
- 问:UUID 当业务单号行不行?答:可以展示,但别当聚簇索引主键。
题目 17:一次大促前数据库容量怎么评估
考点
- QPS/TPS 估算;容量冗余。
参考答案
1. 压测基线:压测得出单库极限 TPS、QPS、CPU/IO 水位;
2. 预估峰值:按历史峰值 × 预估增长 × 安全系数(3~5 倍);
3. 容量冗余:CPU 日常水位压到 50% 以下,给峰值留缓冲;
4. 提前预热:热点数据加载进 Buffer Pool、缓存预热;
5. 预案演练:限流、降级、扩容、切换演练一遍;
6. 大促值守:关键指标大盘 + 值班 + 应急预案。
追问
- 问:怎么压测才真实?答:按真实流量回放,混合读写比例,别只压纯查询。
- 问:单库扛不住又来不及拆?答:临时加只读副本、上缓存、限流,纵向升配过渡。
题目 18:MySQL和ES缓存HBase怎么分工
考点
- 各存储定位;
- 避免大材小用。
参考答案
1. MySQL:核心交易、强一致、事务、结构化短查询;
2. Redis:热点读、计数器、会话、排行榜,抗读洪峰;
3. ES:多条件模糊检索、全文搜索、运营复杂筛选;
4. HBase/列存:海量历史明细、宽表、写多查少的归档;
5. 原则:MySQL 只做它擅长的,别把全文搜索、海量日志硬塞 MySQL。
追问
- 问:ES 里的数据从哪来?答:binlog 订阅 MySQL 异步同步,ES 不承担写主。
- 问:缓存穿透/击穿?答:空值缓存、布隆过滤器、热点永不过期 + 互斥重建。
题目 19:分库分表后跨库Join怎么办
考点
- 避免分布式 Join;
- 冗余/组装思路。
参考答案
1. 业务上避免跨库 Join:分片时把常一起查的表放同库(如订单+订单明细);
2. 冗余字段:把需要展示的字段冗余进主表,省一次 Join;
3. 应用层组装:分别查各库,在内存里按 ID 拼接;
4. 宽表化:适度反范式,把 Join 结果落成一张宽表;
5. 中间件绑定表:相关表用相同分片键,让路由到同一库再 Join。
追问
- 问:不得不 Join 怎么办?答:小表广播 + 大表分片(绑定表),或退到离线数仓做关联。
- 问:反范式冗余怎么保证一致?答:冗余字段随主表更新同步,接受短暂最终一致。
题目 20:高并发下慢SQL治理体系怎么建
考点
- 全流程治理;
- 事前拦截。
参考答案
1. 事前:SQL 审核上线,禁止无索引、深分页、大事务上线;
2. 事中:慢日志采集 + 实时告警,自动 kill 超过阈值的 SQL;
3. 事后:慢 SQL 复盘 → 改 SQL/加索引 → 沉淀案例;
4. 工具链:慢日志自动分析、pt-query-digest、监控大盘;
5. 规范:禁止 SELECT *、必须分页、大表变更走流程;
6. 目标:把慢 SQL 拦截在上线前,而不是线上救火。
追问
- 问:SQL 审核查什么?答:有没有索引、扫描行数、返回行数比、是否大事务。
- 问:线上发现慢 SQL 又不能马上改?答:先 kill 止血,再用索引提示/临时加索引缓解,随后优化。
速查速记
| 主题 | 一句话结论 | 首选方案 |
|---|---|---|
| 全局 ID | 分库后不能自增 | 号段 / 雪花 |
| 防超卖 | 一条 SQL 原子扣减 | WHERE stock>0 |
| 热点行 | 单行锁要打散 | 分桶 + 异步合并 |
| 主从延迟 | 写后读走主 | 关键读主 + 半同步 |
| 跨库分页 | 禁止深 OFFSET | ES + 游标 |
| 缓存一致 | 先更库再删缓存 | 延迟双删 + TTL |
| 扩容 | 不停机双写迁移 | 双写 + 校验 + 灰度 |
| 分布式事务 | 能最终一致不强一致 | 本地消息表 |
常见坑与注意事项
- 坑 1:先查后扣库存——两个并发事务都查到有货再各自扣,必然超卖,必须一条 SQL 原子完成;
- 坑 2:跨库深分页当单机用——
LIMIT 100000,10在分库后是每分片都取 10 万+10,直接压垮; - 坑 3:UUID 当主键——无序聚簇索引导致频繁页分裂,写入性能和空间都吃亏;
- 坑 4:分片键选错——用低基数列或时间片做键,上线即数据倾斜;
- 坑 5:以为加了中间件就万事大吉——跨库 Join、分布式事务、跨库分页能力都被削弱,业务要提前规避;
- 注意:本篇所有题目均为公开面经高频题型与通用原理,不代表任何公司内部实际方案。
复习清单
| 题目 | 核心结论 | 易错点 |
|---|---|---|
| 高并发下单 | 拆分+异步+削峰+限流 | 别只说加机器 |
| 全局 ID | 趋势递增全局唯一 | 别用随机 UUID 主键 |
| 路由键 | 高离散高频列 | 别用低基数列 |
| 分布式事务 | 最终一致优先 | 别上来就 2PC |
| 中间件 | Proxy vs SDK 取舍 | 别忽略跨库限制 |
| 防超卖 | 原子扣减 | 别先 SELECT 再 UPDATE |
| 热点行 | 分桶打散锁 | 别在锁内做重逻辑 |
| 主从延迟 | 写后读走主 | 别忽略半同步代价 |
| 跨库分页 | 走 ES+游标 | 别做深度归并 |
| 缓存一致 | 先更库后删缓存 | 删除失败要兜底 |
练习题
练 1:写一条原子扣减库存的 SQL,要求库存不足时返回 0 行受影响,并说明它如何防超卖。
<details>
<summary>参考答案</summary>
UPDATE t_stock
SET stock_count = stock_count - 1, update_time = NOW()
WHERE sku_id = 10086 AND stock_count > 0;
stock_count > 0 作为更新条件在数据库内原子判断,并发事务不会读到"已经被别人扣走"的库存;受影响行数为 0 即售罄,杜绝超卖。
</details>
练 2:分库分表后运营要"按商家 + 时间段 + 状态"做复杂分页查询,MySQL 直连分片不可行,给出整体方案。
<details>
<summary>参考答案</summary>
订单按 user_id 分片保证用户侧快查;运营侧复杂检索把订单通过 binlog 同步到 ES,多条件过滤、分页、排序都在 ES 完成,MySQL 只存明细;跨库禁止深度 OFFSET,ES 侧也用游标分页。
</details>
下一篇预告:第 30 章 腾讯系MySQL真题精讲
文章回复
0 条公开回复