Skip to content

哪些字段适合加索引,哪些不适合

数据库索引适合与不适合的字段概述

  • 定义
  • 数据库索引是一种数据结构(如 B+ 树、哈希),用于加速表中数据的查询(如 SELECTWHEREJOIN),通过减少扫描的数据量提高性能。
  • 索引的选择需权衡查询性能提升与维护开销(插入、更新、删除的性能下降)。
  • 核心点
  • 适合加索引的字段:高选择性、频繁查询、常用于条件或排序的字段。
  • 不适合加索引的字段:低选择性、频繁修改、查询频率低或数据量小的字段。

1. 适合加索引的字段

以下字段因查询性能提升显著,适合创建索引:

(1) 高选择性字段

  • 定义
  • 选择性(Selectivity)指字段值的唯一性比例,选择性越高(值越分散),索引效果越好。
  • 计算:选择性 = 唯一值数 / 总行数(接近 1 最佳)。
  • 示例
  • 主键(Primary Key):如 id(唯一且不为空),天然适合索引。
  • 唯一键(Unique Key):如 emailuser_id(值唯一)。
  • 外键(Foreign Key):如 order.user_id(常用于 JOIN)。
  • 索引字段:如 phone_number(值分散,查询频繁)。
  • 原因
  • 高选择性字段(如 email)能快速定位少量行,减少扫描。

(2) 频繁查询的字段

  • 定义
  • 常出现在 WHEREJOINGROUP BYORDER BY 的字段。
  • 示例
  • WHERE 条件:SELECT * FROM users WHERE status = 'active'status 适合索引)。
  • JOIN 关联:SELECT * FROM orders JOIN users ON orders.user_id = users.iduser_idid)。
  • ORDER BYSELECT * FROM posts ORDER BY create_time DESCcreate_time)。
  • GROUP BYSELECT department, COUNT(*) FROM employees GROUP BY departmentdepartment)。
  • 原因
  • 索引加速条件过滤、排序和分组,降低全表扫描。

(3) 范围查询字段

  • 定义
  • 常用于范围查询(如 >, <, BETWEEN)的字段。
  • 示例
  • 时间字段:created_atSELECT * FROM logs WHERE created_at BETWEEN '2023-01-01' AND '2023-12-31')。
  • 数值字段:priceSELECT * FROM products WHERE price > 100)。
  • 原因
  • B+ 树索引适合范围查找,快速定位数据区间。

(4) 排序或分组字段

  • 定义
  • 常用于 ORDER BYGROUP BY 的字段。
  • 示例
  • publish_dateSELECT * FROM articles ORDER BY publish_date DESC
  • category_idSELECT category_id, COUNT(*) FROM products GROUP BY category_id
  • 原因
  • 索引可避免临时排序或分组,提高性能。

(5) 覆盖索引的字段

  • 定义
  • 查询仅需索引中的字段,无需回表(Covering Index)。
  • 示例
  • 查询:SELECT user_id, name FROM users WHERE user_id = 123
  • 创建复合索引:INDEX(user_id, name),查询直接从索引获取数据。
  • 原因
  • 覆盖索引避免访问表数据,减少 I/O。

(6) 外键字段

  • 定义
  • 关联表的主键或唯一键字段,常用于 JOIN
  • 示例
  • orders.customer_id 关联 customers.id
  • 原因
  • 加速 JOIN 操作,减少扫描。

2. 不适合加索引的字段

以下字段因索引维护开销高或查询性能提升有限,不适合创建索引:

(1) 低选择性字段

  • 定义
  • 字段值重复率高,唯一值少,选择性低(如 0.01)。
  • 示例
  • 布尔字段is_active(只有 true/false)。
  • 枚举字段gendermalefemale)。
  • 状态字段order_statuspendingcompletedcancelled)。
  • 原因
  • 低选择性字段(如 gender)索引后仍需扫描大量行,效果差。
  • 示例:表有 100 万行,gender='male' 占 50 万,索引无法有效减少扫描。

(2) 频繁更新的字段

  • 定义
  • 常被 UPDATEINSERTDELETE 修改的字段。
  • 示例
  • 计数器:view_count(文章浏览量)。
  • 日志字段:last_updated(频繁更新)。
  • 原因
  • 索引需同步更新,增加写操作开销,降低性能。
  • 示例:更新 view_count 需调整 B+ 树,消耗 I/O。

(3) 很少查询的字段

  • 定义
  • 不出现在 WHEREJOINORDER BY 的字段。
  • 示例
  • 备注字段:description(仅偶尔查询)。
  • 元数据:metadata(存储 JSON,极少过滤)。
  • 原因
  • 索引未被使用,浪费存储和维护开销。

(4) 大文本或二进制字段

  • 定义
  • 存储长文本或大数据的字段(如 TEXTBLOB)。
  • 示例
  • 文章内容:content(长文本)。
  • 文件数据:file_data(二进制)。
  • 原因
  • 索引占用大量存储,维护成本高。
  • 长文本查询通常用全文索引(如 MySQL 的 FULLTEXT 或 Elasticsearch)。
  • 替代
  • TEXT 字段,使用前缀索引: sql CREATE INDEX idx_content ON articles(content(50)); -- 前 50 字符

(5) 数据量极小的表

  • 定义
  • 表行数少(如几百行)。
  • 示例
  • 配置表:settings(10 行)。
  • 小字典表:countries(200 行)。
  • 原因
  • 小表全表扫描已足够快,索引无明显提升。
  • 索引维护反而增加开销。

(6) 频繁变动的复合字段

  • 定义
  • 复合索引中包含频繁更新的字段。
  • 示例
  • 索引 (status, update_time)update_time 频繁变化。
  • 原因
  • 更新导致复合索引频繁重组,性能下降。

3. 索引选择的原则

  • 选择性优先
  • 优先为高选择性字段(如 idemail)建索引。
  • 检查选择性: sql SELECT COUNT(DISTINCT column) / COUNT(*) AS selectivity FROM table;
  • 查询频率
  • 分析慢查询日志(MySQL:slow_query_log),为常见 WHEREJOIN 字段建索引。
  • 覆盖索引
  • 设计复合索引包含查询字段,减少回表。
  • 示例: sql CREATE INDEX idx_user ON users(user_id, name); SELECT user_id, name FROM users WHERE user_id = 123; -- 覆盖索引
  • 最小化索引
  • 避免冗余索引(如 (a)(a,b) 同时存在)。
  • 删除未使用索引: sql SELECT * FROM information_schema.STATISTICS WHERE TABLE_NAME = 'table';
  • 维护成本
  • 评估写操作频率,频繁 UPDATE 的表谨慎加索引。
  • 前缀索引
  • 长字符串字段用前缀索引: sql CREATE INDEX idx_name ON users(name(20));

4. 具体场景示例

适合加索引

  • 用户表
  • 字段:user_id(主键)、email(唯一)、created_at(范围查询)。
  • SQL:SELECT * FROM users WHERE email = 'a@b.com' AND created_at > '2023-01-01'
  • 索引:INDEX(email), INDEX(created_at) 或复合索引 INDEX(email, created_at)
  • 订单表
  • 字段:order_id(主键)、user_id(外键)、status(查询)。
  • SQL:SELECT * FROM orders WHERE user_id = 123 AND status = 'pending'
  • 索引:INDEX(user_id, status)

不适合加索引

  • 日志表
  • 字段:log_levelINFOERROR,低选择性)、message(长文本)。
  • SQL:SELECT * FROM logs WHERE log_level = 'INFO'
  • 原因:log_level 选择性低,message 不适合普通索引(用全文索引)。
  • 计数表
  • 字段:counter(频繁更新)。
  • SQL:UPDATE counters SET counter = counter + 1 WHERE id = 1
  • 原因:counter 频繁修改,索引维护开销高。

5. 注意事项

  • 索引过多
  • 增加存储(索引占用磁盘)。
  • 降低写性能(INSERTUPDATEDELETE 需更新索引)。
  • 解决:定期分析索引使用率,删除冗余索引。
  • 复合索引顺序
  • 按查询频率和选择性排序(如 (user_id, created_at) 而非反序)。
  • 遵循“最左前缀”原则: sql INDEX(user_id, created_at) -- 支持 WHERE user_id = 123 AND created_at > '2023-01-01'
  • 数据库差异
  • MySQL:B+ 树为主,支持前缀、全文索引。
  • PostgreSQL:支持 GiST、GIN(数组、JSON)。
  • MongoDB:支持 TTL、地理索引。
  • 慢查询分析
  • MySQL:启用 slow_query_log,用 EXPLAIN 检查查询计划。 sql EXPLAIN SELECT * FROM users WHERE email = 'a@b.com';
  • 索引维护
  • 定期优化(OPTIMIZE TABLE)或重建索引(ALTER TABLE ... REBUILD INDEX)。
  • 分布式系统
  • 分库分表后,索引需按分片键设计(如 user_id 分片)。

6. 面试角度

  • 问“哪些字段适合加索引”
  • 提高选择性(主键、唯一键)、频繁查询(WHEREJOIN)、范围/排序字段,举例(user_idcreated_at)。
  • 问“不适合加索引的字段”
  • 提低选择性(布尔、枚举)、频繁更新(计数器)、少查询(备注)、大文本(TEXT)。
  • 问“如何判断是否加索引”
  • 提选择性(DISTINCT 比例)、查询频率(慢查询日志)、覆盖索引、写开销。
  • 问“复合索引作用”
  • 提多字段查询、覆盖索引、最左前缀,举例 INDEX(user_id, created_at)
  • 问“索引的缺点”
  • 提存储开销、写性能下降、维护成本,需权衡查询与写。
  • 问“优化索引”
  • 提删除冗余、用前缀索引、分析 EXPLAIN、定期优化。

7. 总结

  • 适合加索引
  • 高选择性:主键、唯一键、外键(如 idemail)。
  • 频繁查询:WHEREJOINORDER BYGROUP BY(如 statuscreated_at)。
  • 范围/排序:时间、数值字段。
  • 覆盖索引:查询字段全在索引中。
  • 不适合加索引
  • 低选择性:布尔、枚举(如 gender)。
  • 频繁更新:计数器、日志字段(如 view_count)。
  • 少查询:备注、元数据。
  • 大文本:TEXTBLOB(用前缀或全文索引)。
  • 小表:行数少全表扫描已够快。
  • 原则
  • 优先高选择性、查询频繁字段,权衡写开销,避免冗余。
  • 面试建议
  • 提适合/不适合字段、选择性计算、复合索引规则、慢查询分析、优缺点,举 SQL 示例(CREATE INDEXEXPLAIN),清晰展示理解。