当业务数据量达到千万甚至亿级时,原本运行正常的SQL查询可能会出现耗时几秒甚至几十秒的情况,严重影响业务体验。大表性能优化需要从表结构设计、索引使用、查询逻辑、架构调整多个维度综合处理,不同场景的优化侧重点也有差异。

场景一:单表千万级数据按条件查询慢
最常见的慢查询场景是单表数据量过大,按照非索引字段查询时触发全表扫描。比如用户订单表order_info有2000万条数据,需要查询某个用户最近30天的订单列表,原始查询语句如下:
-- 未优化前的查询,user_id无索引时会全表扫描 SELECT * FROM order_info WHERE user_id = 12345 AND create_time >= '2024-01-01' AND create_time < '2024-02-01' ORDER BY create_time DESC;
优化思路是为查询条件创建联合索引,遵循最左前缀匹配原则,把区分度高的字段放在前面。这里user_id区分度高,create_time是范围查询字段,适合放在联合索引末尾:
-- 创建联合索引,提升查询效率 CREATE INDEX idx_user_time ON order_info(user_id, create_time);
索引创建后,查询会直接通过索引定位到对应数据,避免全表扫描,耗时可以从几秒降低到几十毫秒。
场景二:大表分页查询越往后越慢
分页查询是另一个高频痛点,比如查询订单列表的第1000页,每页10条数据,原始SQL如下:
-- 普通分页查询,offset越大扫描数据越多 SELECT * FROM order_info ORDER BY id DESC LIMIT 10 OFFSET 9990;
这种写法会先扫描前9990条数据再取后面的10条,数据量越大offset越大,扫描效率越低。优化方案是使用覆盖索引+子查询,先通过索引拿到分页的id,再回表查询完整数据:
-- 优化后的分页查询,减少扫描数据量 SELECT * FROM order_info WHERE id <= ( SELECT id FROM order_info ORDER BY id DESC LIMIT 1 OFFSET 9990 ) ORDER BY id DESC LIMIT 10;
如果表的主键是自增的,还可以用id范围查询替代offset,效率更高:
-- 基于自增主键的分页优化 SELECT * FROM order_info WHERE id < 上次查询的最小id ORDER BY id DESC LIMIT 10;
场景三:大表关联查询性能差
多表关联查询时如果关联字段没有索引,会产生笛卡尔积或者大量嵌套循环,性能急剧下降。比如查询用户信息和对应的订单信息,原始语句如下:
-- 关联查询无索引,性能差 SELECT u.user_name, o.order_no, o.amount FROM user u JOIN order_info o ON u.id = o.user_id WHERE u.id = 12345;
优化时需要给关联字段order_info.user_id创建索引,同时尽量只查询需要的字段,避免SELECT *带来的额外IO开销:
-- 给关联字段创建索引 CREATE INDEX idx_order_user ON order_info(user_id); -- 优化后的关联查询,只查需要的字段 SELECT u.user_name, o.order_no, o.amount FROM user u JOIN order_info o ON u.id = o.user_id WHERE u.id = 12345;
大表优化的其他实用技巧
- 定期清理无用数据,把历史冷数据迁移到归档表,减少主表数据量
- 对于超亿级的大表,可以考虑水平分表,按照时间或者用户id哈希拆分到多个子表
- 避免使用函数、表达式对索引字段进行处理,比如
WHERE DATE(create_time) = '2024-01-01'会导致索引失效 - 开启慢查询日志,定期分析慢查询语句,针对性优化高频慢SQL
大表性能优化没有通用的最优解,需要结合具体的业务场景、数据分布、查询频率来做调整,建议每次优化后通过EXPLAIN命令查看执行计划,验证优化效果是否符合预期。