订单表5000万行查询越来越慢,你怎么办?
先说结论
解决大表问题的思路是先优化、再拆分、最后才分库分表。不要上来就分表,这是最复杂的方案,应该是最后的手段。
先把这段讲顺;面试官追问时,再展开下面的原理和细节。
详细讲解
单表数据量过大是MySQL最常见的性能问题。很多人上来就说“分库分表”,但面试官想听的是你的思考过程——为什么选这个方案,有没有更简单的办法。今天把所有方案按“从简单到复杂”的顺序讲清楚。
方案一:索引优化(成本最低)
先检查现有查询的索引是否合理。很多时候“表大”不是根本原因,索引不合理才是。我见过一张5000万的表,索引正确的情况下查询只要5ms。
方案二:历史数据归档(最实用)
大多数场景下,订单表的查询都集中在近30天。把30天前的数据迁移到历史表,主表只保留热数据。
-- 建历史表(结构一样)
CREATE TABLE orders_archive LIKE orders;
-- 定期迁移
INSERT INTO orders_archive
SELECT * FROM orders WHERE created_at < DATE_SUB(NOW(), INTERVAL 30 DAY);
DELETE FROM orders WHERE created_at < DATE_SUB(NOW(), INTERVAL 30 DAY);
-- 注意:要分批删除,不要一次删太多,避免长事务和主从延迟
这个方案最简单有效。我在元宇宙社交App做过,主表从5000万降到500万,查询性能提升30%。
方案三:分区表(MySQL原生支持)
如果查询总是带时间条件,可以用MySQL的分区表按月分区:
ALTER TABLE orders PARTITION BY RANGE (TO_DAYS(created_at)) (
PARTITION p202501 VALUES LESS THAN (TO_DAYS('2025-02-01')),
PARTITION p202502 VALUES LESS THAN (TO_DAYS('2025-03-01')),
PARTITION p202503 VALUES LESS THAN (TO_DAYS('2025-04-01')),
PARTITION pmax VALUES LESS THAN MAXVALUE
);
查询带时间条件时,MySQL只扫描相关分区,不扫描全表。但分区表有限制:唯一索引必须包含分区键,某些查询可能无法利用分区裁剪。
方案四:分库分表(最后的手段)
当前面的方案都不够时,才考虑分库分表。常用方案:
水平分表: 按user_id或order_id的Hash值分到多张表。比如orders_0、orders_1、...orders_15,共分刲16张表。
常用中间件: ShardingSphere、Vitess、阿里云DRDS。
分表后最大的问题是跨表查询。比如“查询某个用户的所有订单”很简单(按user_id分表就能定位到一张表),但“查询今天所有用户的订单总金额”就需要查所有表再汇总,很复杂。
实战经验:我的选择路径
我在元宇宙社交App的做法是:
1. 先优化索引→ 解决了80%的问题
2. 历史数据归档→ 主表从5000万降到500万
3. 特别大的表(聊天记录——每天新增几百万条)才做了分表,按user_id取模刈64张表
所以记住顺序:索引优化 → 归档 → 分区表 → 分库分表。从简单到复杂,能不分表就不分表。
总结
| 方案 | 复杂度 | 适用场景 |
|---|---|---|
| 索引优化 | 低 | 先检查,大多数问题根源在此 |
| 历史归档 | 低 | 查询集中在近期数据 |
| 分区表 | 中 | 查询总带时间/分类条件 |
| 分库分表 | 高 | 单表超过千万级且前面方案不够 |
面试时按“从简单到复杂”的顺序讲,说明你有完整的思考过程,而不是无脑“上来就分表”。
下一篇聊一个很实战的场景:两个事务同时改同一条数据,一个超时失败了——你怎么排查死锁?
你们生产环境单表最大有多少行?评论区聊聊~
关注「Wood聊大数据」| 9年大数据老兵,聊技术、聊职场、聊成长