不能只对 user_id 单独建索引,因为高频查询需同时过滤用户、时间及金额,单列索引无法覆盖范围条件与排序,导致回表多、性能差;组合索引应按 user_id、amount、created_at 顺序建立以满足最左前缀原则和覆盖查询需求。

为什么不能只对 user_id 单独建索引?
账单流水表通常按时间范围 + 用户维度高频查询,比如「查某用户最近30天的所有流水」。如果只在 user_id 上建索引,MySQL 能快速定位到该用户所有记录,但后续还得在内存或磁盘中逐行过滤时间(比如 created_at >= '2024-05-01'),尤其是当该用户流水量极大时,回表成本高、排序/分页慢。
更关键的是:金额字段(如 amount)常参与条件(如「金额大于100」)或排序(如「按金额降序取前10笔」)。单列索引无法覆盖这些场景,优化器大概率放弃索引或退化为全索引扫描。
user_id 和 amount 组合索引的字段顺序怎么定?
顺序不是随便写的,得看查询模式。绝大多数账单查询是以用户为前提,再叠加金额筛选或排序,例如:
WHERE user_id = ? AND amount > ?WHERE user_id = ? ORDER BY amount DESC LIMIT 10WHERE user_id = ? AND created_at BETWEEN ? AND ? ORDER BY amount DESC
所以 user_id 必须放最左——它是查询的强过滤条件;amount 放第二位,才能被用于范围查询或排序。反过来建 (amount, user_id),一旦 WHERE 中没出现 amount,整个索引就失效了(最左前缀原则)。
注意:如果查询里还带 created_at,且经常按时间范围+用户查,建议优先考虑 (user_id, created_at),把 amount 放第三位——因为时间范围过滤通常比金额更严格,也更常用。
要不要包含 created_at 或其他字段进这个组合索引?
可以,但得看是否满足「覆盖索引」需求。比如常见查询是:
SELECT id, amount, created_at FROM bill WHERE user_id = 123 AND amount > 50 ORDER BY created_at DESC
如果建索引为 (user_id, amount, created_at, id),就能避免回表——所有 SELECT 字段和 WHERE / ORDER BY 字段都被索引覆盖。但要注意:
- 索引越宽,写入开销越大,尤其流水表 INSERT 频繁
-
created_at是范围条件时,它后面的字段(如id)无法用于排序或过滤,除非用IN或等值匹配 - 如果
ORDER BY created_at出现在WHERE user_id = ? AND amount > ?之后,那created_at必须紧跟在amount后面,否则排序仍需 filesort
实际建索引语句与容易踩的坑
典型建法(兼顾查询与写入平衡):
ALTER TABLE bill ADD INDEX idx_user_amount_created (user_id, amount, created_at);
常见错误:
- 建了
(user_id, amount),但查询里用了ORDER BY created_at→ 触发Using filesort - 把
amount设为DECIMAL(10,2),但索引定义时没注意精度影响排序行为(其实不影响,但有人误以为要转成整数存) - 忽略
NULL值:如果amount允许为NULL,而查询写amount > 0,MySQL 仍能走索引,但IS NULL条件无法利用该索引的amount部分 - 没清理旧索引:删掉冗余的单列
user_id索引,否则优化器可能选错索引,还拖慢 INSERT
最易被忽略的一点:金额字段参与范围查询时,组合索引中它后面的所有字段都失效——所以别指望靠 (user_id, amount, id) 来加速 ORDER BY id,除非 amount 是等值条件。











