单表数据量达到数亿后,按时间范围查询和按业务 ID 查询都开始变慢,你会如何判断应该优先做索引治理、分区还是分库分表?
免费版我不会只根据“单表达到数亿”就直接决定分区或分库分表,通常会先确认慢在哪里、慢的原因是什么,再按成本从低到高处理。
我会先看慢查询日志、QPS 和延迟分布,结合执行计划确认是索引没有命中、扫描行数过多、回表和排序开销大,还是已经受到磁盘 IO、CPU、锁竞争或连接数的限制。同时要检查统计信息是否准确、是否存在隐式类型转换、对索引列做函数计算,以及查询条件和排序条件是否匹配索引。
如果主要问题是查询没有合理使用索引,我会优先做索引治理。例如按业务 ID 查询通常需要保证业务 ID 具备高选择性,并建立合适的单列索引;如果经常同时按业务 ID 和时间过滤,可以评估 (business id, event time) 这样的联合索引。如果主要是按时间范围查询,还要根据时间范围的宽窄、排序方式和返回字段,评估时间索引或覆盖索引是否合适。联合索引的列顺序要根据实际查询条件和选择性确定,不能简单认为索引越多越好,因为索引会增加写入、存储和维护成本。时间范围很大时,索引扫描本身也可能不如顺序扫描,这时不能只靠加索引解决。
如果数据天然按时间增长,并且业务存在明显的冷热数据、历史归档或按时间删除需求,而单个数据库实例的存储和并发能力还可以,我会优先考虑按时间分区。时间范围查询可以通过分区裁剪减少扫描的数据量,历史数据也可以单独归档或清理。不过分区不会自动解决所有问题:分区键必须和查询条件匹配,分区数量不能无限增加,还要关注分区内索引、跨分区排序、分区维护以及不同数据库对本地索引或全局索引的限制。按业务 ID 的点查通常仍然需要在分区内走索引,如果查询不带分区键,可能仍然要访问多个分区。
只有在确认单实例已经达到容量或并发瓶颈,例如存储、IO、CPU、索引大小、写入吞吐或高峰期连接数无法通过索引和分区解决时,我才会考虑分库分表。分片键要优先匹配最核心、访问量最大的查询。如果业务 ID 查询占主要流量,可以按业务 ID 做哈希分片,保证点查能够路由到单个分片;如果时间范围查询是核心场景,也可以考虑按时间分片,或者采用按业务 ID 分库分表、分片内再按时间分区的组合方案。需要提前评估跨分片范围查询、分页排序、聚合统计、分布式事务、全局唯一 ID、扩容和数据迁移成本。分库分表虽然能横向扩展,但会明显增加系统复杂度,不能只把它当成解决慢查询的手段。
我的判断顺序一般是:先用执行计划和监控定位问题;如果是 SQL、索引或统计信息问题,优先索引治理;如果数据有明显时间生命周期和范围查询特征,且单实例容量还够,优先按时间分区;如果单实例的资源和吞吐已经成为瓶颈,或者未来数据规模和并发还会持续增长,再做分库分表。实施时会先建立基线,评估索引和分区方案,进行压测和小流量验证,再通过在线建索引、数据回填、双写或增量同步、校验和可回滚切换完成迁移,避免一次性改动影响线上业务。
