SQL 索引在大数据量表中的应用实践

2次阅读

未加索引的where条件会导致全表扫描,使查询慢十倍;需用explain确认索引使用情况,复合索引字段顺序影响生效范围,低区分度字段不宜单独建索引,order by需索引支持,大offset分页应改用游标分页。

SQL 索引在大数据量表中的应用实践

WHERE 条件字段没加索引,查询直接变慢十倍

大数据量表里,select * FROM orders WHERE user_id = 123 这种查询如果 user_id 没索引,mysqlpostgresql 就得全表扫描。几千万行?可能要几秒甚至十几秒——而加了索引通常压到几十毫秒。

实操建议:

  • 先用 EXPLAIN 看执行计划,重点盯 type 字段:要是 ALL,基本就是全扫;refrange 才算走索引
  • 复合索引要注意字段顺序:CREATE INDEX idx_status_user ON orders(status, user_id) 能加速 WHERE status = 'paid' AND user_id = 123,但对 WHERE user_id = 123 单独查几乎无效
  • 别给低区分度字段单独建索引,比如 is_deleted TINYINT(99% 是 0),优化器大概率直接弃用

ORDER BY + LIMIT 在无索引时会触发 filesort

SELECT * FROM logs ORDER BY created_at DESC LIMIT 20 看似简单,但如果 created_at 没索引,数据库就得把几百万行全读出来、内存排序、再取前 20——内存爆掉或磁盘临时文件都会拖垮性能。

实操建议:

  • ORDER BY 的分页查询,必须确保排序字段在索引最左侧,且和查询条件能复用同一个索引
  • 避免 OFFSET 大值分页:OFFSET 100000 会让数据库先跳过 10 万行,即使有索引也慢;改用游标分页(如 WHERE created_at )
  • PostgreSQL 中 ORDER BY ... NULLS LAST 会影响索引使用,建索引时得显式写成 CREATE INDEX ON logs(created_at DESC NULLS LAST)

索引太多反而拖慢写入,尤其高并发 INSERT/UPDATE 场景

每多一个索引,每次 INSERT 就得多写一份 B+ 树结构;UPDATE 如果改的是索引列,还要同步更新多个索引页。单表上百个索引?写入吞吐可能掉一半以上。

实操建议:

  • information_schema.STATISTICS 查哪些索引从没被用过(结合 performance_schema.table_io_waits_summary_by_index_usage,MySQL 8.0+)
  • 唯一性不强的字段组合慎建唯一索引,比如 (region, category, status) 可能重复率高,但加了 UNIQUE 会导致插入冲突或锁等待加剧
  • 考虑部分索引(PostgreSQL)或条件索引(MySQL 8.0.13+):只索引活跃数据,例如 CREATE INDEX idx_active_orders ON orders(user_id) WHERE status IN ('pending', 'processing')

TEXT/BLOB 字段不能直接建普通索引,容易报错或失效

ALTER TABLE articles ADD INDEX idx_content(content) 在 MySQL 里会报错 Error 1170 (42000): BLOB/TEXT column 'content' used in key specification without a key Length;就算强行指定长度(如 content(255)),也可能因截断导致查询不准。

实操建议:

  • 全文检索场景用 FULLTEXT 索引(MySQL)或 tsvector + gin(PostgreSQL),别硬套 B-tree
  • 想按前缀快速过滤?提取特征字段单独存,比如加一列 content_hash char(16) 存 MD5 前 16 位,再对它建索引
  • json 字段别直接索引整个字段,MySQL 支持 GENERATED COLUMN 提取路径值并建索引,例如 ALTER TABLE events ADD COLUMN event_type VARCHAR(32) AS (data->>'$.type'),再建 INDEX idx_event_type ON events(event_type)

索引不是越多越好,也不是建了就一定生效;关键看查询模式是否匹配索引结构,以及写入负载能不能扛住额外开销。最常被忽略的是:上线前没用真实数据量 + 真实流量压测索引效果,结果大促时才发现某个看似合理的索引根本没被优化器选中。

text=ZqhQzanResources