哪些字段适合加索引,哪些不适合
数据库索引适合与不适合的字段概述
- 定义:
- 数据库索引是一种数据结构(如 B+ 树、哈希),用于加速表中数据的查询(如
SELECT
、WHERE
、JOIN
),通过减少扫描的数据量提高性能。 - 索引的选择需权衡查询性能提升与维护开销(插入、更新、删除的性能下降)。
- 核心点:
- 适合加索引的字段:高选择性、频繁查询、常用于条件或排序的字段。
- 不适合加索引的字段:低选择性、频繁修改、查询频率低或数据量小的字段。
1. 适合加索引的字段
以下字段因查询性能提升显著,适合创建索引:
(1) 高选择性字段
- 定义:
- 选择性(Selectivity)指字段值的唯一性比例,选择性越高(值越分散),索引效果越好。
- 计算:
选择性 = 唯一值数 / 总行数
(接近 1 最佳)。 - 示例:
- 主键(Primary Key):如
id
(唯一且不为空),天然适合索引。 - 唯一键(Unique Key):如
email
、user_id
(值唯一)。 - 外键(Foreign Key):如
order.user_id
(常用于JOIN
)。 - 索引字段:如
phone_number
(值分散,查询频繁)。 - 原因:
- 高选择性字段(如
email
)能快速定位少量行,减少扫描。
(2) 频繁查询的字段
- 定义:
- 常出现在
WHERE
、JOIN
、GROUP BY
、ORDER BY
的字段。 - 示例:
WHERE
条件:SELECT * FROM users WHERE status = 'active'
(status
适合索引)。JOIN
关联:SELECT * FROM orders JOIN users ON orders.user_id = users.id
(user_id
、id
)。ORDER BY
:SELECT * FROM posts ORDER BY create_time DESC
(create_time
)。GROUP BY
:SELECT department, COUNT(*) FROM employees GROUP BY department
(department
)。- 原因:
- 索引加速条件过滤、排序和分组,降低全表扫描。
(3) 范围查询字段
- 定义:
- 常用于范围查询(如
>
,<
,BETWEEN
)的字段。 - 示例:
- 时间字段:
created_at
(SELECT * FROM logs WHERE created_at BETWEEN '2023-01-01' AND '2023-12-31'
)。 - 数值字段:
price
(SELECT * FROM products WHERE price > 100
)。 - 原因:
- B+ 树索引适合范围查找,快速定位数据区间。
(4) 排序或分组字段
- 定义:
- 常用于
ORDER BY
或GROUP BY
的字段。 - 示例:
publish_date
:SELECT * FROM articles ORDER BY publish_date DESC
。category_id
:SELECT 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
)。 - 枚举字段:
gender
(male
、female
)。 - 状态字段:
order_status
(pending
、completed
、cancelled
)。 - 原因:
- 低选择性字段(如
gender
)索引后仍需扫描大量行,效果差。 - 示例:表有 100 万行,
gender='male'
占 50 万,索引无法有效减少扫描。
(2) 频繁更新的字段
- 定义:
- 常被
UPDATE
、INSERT
、DELETE
修改的字段。 - 示例:
- 计数器:
view_count
(文章浏览量)。 - 日志字段:
last_updated
(频繁更新)。 - 原因:
- 索引需同步更新,增加写操作开销,降低性能。
- 示例:更新
view_count
需调整 B+ 树,消耗 I/O。
(3) 很少查询的字段
- 定义:
- 不出现在
WHERE
、JOIN
、ORDER BY
的字段。 - 示例:
- 备注字段:
description
(仅偶尔查询)。 - 元数据:
metadata
(存储 JSON,极少过滤)。 - 原因:
- 索引未被使用,浪费存储和维护开销。
(4) 大文本或二进制字段
- 定义:
- 存储长文本或大数据的字段(如
TEXT
、BLOB
)。 - 示例:
- 文章内容:
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. 索引选择的原则
- 选择性优先:
- 优先为高选择性字段(如
id
、email
)建索引。 - 检查选择性:
sql SELECT COUNT(DISTINCT column) / COUNT(*) AS selectivity FROM table;
- 查询频率:
- 分析慢查询日志(MySQL:
slow_query_log
),为常见WHERE
、JOIN
字段建索引。 - 覆盖索引:
- 设计复合索引包含查询字段,减少回表。
- 示例:
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_level
(INFO
、ERROR
,低选择性)、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. 注意事项
- 索引过多:
- 增加存储(索引占用磁盘)。
- 降低写性能(
INSERT
、UPDATE
、DELETE
需更新索引)。 - 解决:定期分析索引使用率,删除冗余索引。
- 复合索引顺序:
- 按查询频率和选择性排序(如
(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. 面试角度
- 问“哪些字段适合加索引”:
- 提高选择性(主键、唯一键)、频繁查询(
WHERE
、JOIN
)、范围/排序字段,举例(user_id
、created_at
)。 - 问“不适合加索引的字段”:
- 提低选择性(布尔、枚举)、频繁更新(计数器)、少查询(备注)、大文本(
TEXT
)。 - 问“如何判断是否加索引”:
- 提选择性(
DISTINCT
比例)、查询频率(慢查询日志)、覆盖索引、写开销。 - 问“复合索引作用”:
- 提多字段查询、覆盖索引、最左前缀,举例
INDEX(user_id, created_at)
。 - 问“索引的缺点”:
- 提存储开销、写性能下降、维护成本,需权衡查询与写。
- 问“优化索引”:
- 提删除冗余、用前缀索引、分析
EXPLAIN
、定期优化。
7. 总结
- 适合加索引:
- 高选择性:主键、唯一键、外键(如
id
、email
)。 - 频繁查询:
WHERE
、JOIN
、ORDER BY
、GROUP BY
(如status
、created_at
)。 - 范围/排序:时间、数值字段。
- 覆盖索引:查询字段全在索引中。
- 不适合加索引:
- 低选择性:布尔、枚举(如
gender
)。 - 频繁更新:计数器、日志字段(如
view_count
)。 - 少查询:备注、元数据。
- 大文本:
TEXT
、BLOB
(用前缀或全文索引)。 - 小表:行数少全表扫描已够快。
- 原则:
- 优先高选择性、查询频繁字段,权衡写开销,避免冗余。
- 面试建议:
- 提适合/不适合字段、选择性计算、复合索引规则、慢查询分析、优缺点,举 SQL 示例(
CREATE INDEX
、EXPLAIN
),清晰展示理解。