大表设计需分区、索引与字段协同优化:分区键应选高频查询字段(如tenant_id),避免自增id或uuid;索引要覆盖高qps慢查,遵循最左前缀原则;字段须最小够用、禁用null逻辑、大文本拆表。

大表设计不是堆硬件或盲目加索引,核心是让查询落在尽可能少的数据上。分区、索引和字段设计三者必须协同考虑,单独优化某一项效果有限。
分区要按“查什么”来分,不是按“有多大”来分
分区本质是数据裁剪手段,只有查询条件能精准命中分区键时,才能跳过无关分区。常见误区是按时间字段做 RANGE 分区,但业务查询却很少带时间范围(比如只查用户ID),结果全分区扫描照旧。
- 优先选高频过滤字段作分区键,如 tenant_id(多租户)、status(状态枚举)、region_code(地域)
- 避免用自增 ID 或 UUID 分区——几乎无法用于 WHERE 条件裁剪
- 单表分区数建议控制在 16~64 个之间;太多管理成本高,太少裁剪效果弱
- MySQL 8.0+ 支持 LIST COLUMNS,可对字符串、多列组合分区,比老版本更灵活
索引不是越多越好,要覆盖“最重的那几类查询”
大表上每个二级索引都带来写放大和存储开销。应先分析慢查日志或执行计划,聚焦 QPS 高、响应慢、扫描行数多的 SQL,再针对性建索引。
- 联合索引遵循“最左前缀”,把等值条件字段放前面,范围/排序字段放后面(如 WHERE user_id = ? AND create_time > ? ORDER BY score DESC → 建索引 (user_id, create_time, score))
- 避免冗余索引:(a,b) 存在时,(a) 通常不必单独建;但 (a,b,c) 和 (a,c) 不冗余,因后者可覆盖仅查 a/c 的场景
- 对频繁更新的字段慎建索引,尤其是 TEXT、JSON 类型;可考虑生成列 + 索引(如 MySQL 的 STORED GENERATED COLUMN)
- 定期用 sys.schema_unused_indexes 或 pt-index-usage 检查未被使用的索引
字段设计直接影响存储、IO 和查询效率
大表中每字节都在放大:影响 Buffer Pool 占用、网络传输量、排序/聚合内存消耗。字段类型、长度、是否允许 NULL,都要有明确依据。
- 用最小够用类型:tinyint 代替 int 存状态码,date 代替 datetime 存无时分秒日期,varchar(n) 中 n 按实际业务上限设,别一律 255
- 避免使用 NULL 做逻辑标记(如 is_deleted=NULL 表示未删),改用 tinyint(1) 默认 0;NULL 在索引中处理复杂,且 COUNT(col) 会跳过 NULL 行
- 大文本(如详情、日志)务必拆到扩展表,主表只留摘要或 URL;否则一行变几百 KB,严重拖慢全表扫描和缓存命中率
- 枚举类字段优先用 TINYINT + 字典表,而非 ENUM 类型(ALTER 修改麻烦,且跨库同步易出错)
不复杂但容易忽略:上线前用真实数据量做 explain 分析,确认执行计划走的是你预期的分区和索引;定期看 innodb_buffer_pool_read_requests / innodb_buffer_pool_reads 比值,判断缓存是否健康。










