Skip to content

暂时先不考虑分库分表,如果单表的数据量很大,如何优化? ​

  • 标签:后端、场景题

🏛 业务架构师 ​

单表数据量大但暂不考虑分库分表——这个约束我很认同:分库分表是最后手段(带来分布式事务、跨片查询、扩容再平衡的长期成本),在这之前有一整套更便宜的优化手段,按投入产出排序。

第一优先:确认"大"是不是问题 先看慢的原因是不是数据量本身:很多"表大所以慢"实际是慢 SQL 和索引问题。用 SHOW TABLE STATUS 看行数和平均行长,抓慢查询日志定位真实瓶颈。

第二:索引与 SQL 治理(成本最低、收益最大)

  • 覆盖索引消除回表、联合索引匹配最左前缀
  • 深分页改游标(WHERE id > lastId LIMIT n)
  • SELECT * 改具体列,大字段(TEXT/BLOB)垂直拆到附表

第三:冷热分离(业务归档,被低估的手段)

  • 订单类数据天然有生命周期:活跃单 3 个月、半年归档、两年冷存
  • 按时间分区(PARTITION BY RANGE)或归档表 + 归档库,主表保持千万级,查询性能长期健康——这本质是业务数据治理,比技术分片更治本,而且审计/查询冷数据走归档库不影响在线

第四:读写分离 报表、列表、导出类读流量迁到从库,主库只承担交易写。

第五:引入搜索引擎/宽表 复杂多条件查询(订单后台按商家+状态+时间筛选)转 ES,Binlog 同步维护——把"数据库不适合的查询"挪到适合的引擎,而不是逼 MySQL 做全文检索。

收尾观点:这套组合拳能让千万到亿级单表长期健康运行。真到单表写入持续数万 QPS、以上手段全失效时,再按业务键(用户 ID/订单 ID)哈希分片,并选好中间件(ShardingSphere)——分库分表解决的是写容量问题,不是为了"表大了就拆"。先问业务增长预期,再决定要不要为三年后的量级今天就付成本。

🏗 系统架构师 ​

不分库分表的前提下,单表大数据量的优化我按"查询链路逐层榨干"来讲,以 2000w~1 亿行 MySQL 为例。

1. 索引优化(收益最大)

  • 为高频查询建联合索引,遵循最左前缀;索引列顺序按"等值在前、范围在后"
  • 覆盖索引消除回表:SELECT id, status FROM t WHERE ... 把所需列全放进索引,EXPLAIN 出 Extra=Using index
  • 长字符串用前缀索引或反转/hash 索引;低区分度字段做联合索引的一部分而非单独索引

2. SQL 与分页优化

  • 深分页 LIMIT 1000000, 20 改为游标/延迟关联:WHERE id > last_id LIMIT 20,或先查 id 再回表 join,从扫百万行降到几十行
  • 尽量走索引排序避免 filesort;大事务、SELECT *、隐式类型转换(字符串列传数字)都是杀手

3. 冷热分离与归档 单表最大的问题是冷数据拖累:按时间把 3 个月前的数据归档到历史表/归档库(或 ClickHouse 做 OLAP),主表瘦身回千万级,这是不拆分前提下的"分表效果"。

4. 架构层分担读压力

  • 读写分离:一主多从,报表与列表查询走从库
  • 缓存:热点查询前置 Redis,DB 只承担 miss
  • MySQL 8.0 的并行查询、合理的 innodb_buffer_pool_size(物理内存 60%~70%)让热数据全在内存

5. 参数与硬件 buffer pool 命中率 > 99.9% 是健康线;IO 队列深度、redo log 大小影响写入吞吐。

判断题眼:以上手段把单表撑到亿级可行,但写入 QPS 超过单机几千、且持续增长,分库分表就躲不掉了——主动给出这个边界,体现容量规划意识。

💻 全栈开发专家 ​

不分库分表的前提下,我按"索引 → 归档 → 分区 → 读写分离 → 异构查询"五板斧优化。

1. 索引与 SQL 治理(性价比最高)

  • 覆盖索引消回表:查询列全放索引里,idx_status_create(status, create_time, user_id)。
  • 深分页改游标:WHERE id > ? LIMIT 100 替代 LIMIT 1000000, 100。

2. 冷热分离 / 归档

大表的量 80% 是历史数据。按时间把冷数据搬到归档表(orders_archive)或归档库,主表保持千万级,所有查询立即变快。归档用低峰批量迁移 + 双写过渡,这是我优先考虑的手段。

3. 分区表(MySQL 原生)

sql
ALTER TABLE orders PARTITION BY RANGE (TO_DAYS(create_time)) (
  PARTITION p202601 VALUES LESS THAN (TO_DAYS('2026-02-01')),
  PARTITION p202602 VALUES LESS THAN (TO_DAYS('2026-03-01')),
  PARTITION pmax    VALUES LESS THAN MAXVALUE
);

查询带分区键时分区裁剪只扫对应分区,删历史数据直接 DROP PARTITION 秒级完成。注意:分区键必须包含在主键/唯一键里,这是最常见的限制。

4. 读写分离:读多写少的报表、列表查询走从库,注意复制延迟监控。

5. 异构查询:复杂多维查询(模糊搜索、聚合)移交 ES / ClickHouse,MySQL 只承担交易。

补一句:优化前先用 sys 表或慢日志确认瓶颈真的是表大,很多时候是索引缺失或全表扫描在裸奔。