自增主键默认就有索引,不用额外建
MySQL 中定义为
PRIMARY KEY的自增字段(如
id INT AUTO_INCREMENT PRIMARY KEY),InnoDB 存储引擎会自动为其创建聚簇索引(clustered index)。这个索引就是表的物理组织方式,查询、排序、范围扫描都直接走它——你再手动给该字段加一个
INDEX,只会多出一份冗余的二级索引,浪费空间和写入性能。
常见错误现象:
Duplicate entry '123' for key 'id_index'这类报错往往不是因为没索引,而是重复插入或事务未提交导致的自增值冲突;而慢查询如果落在
WHERE id = ?上却仍慢,大概率是用了字符串类型传参(如
'123')触发隐式类型转换,让索引失效,不是索引不存在。
非主键的自增字段必须显式建索引才有效
自增属性本身不带来索引,只有主键或显式
INDEX才有。如果你有类似
log_id BIGINT AUTO_INCREMENT但没设主键,只用它做查询条件(比如
WHERE log_id > 1000),那这字段就是全表扫描——哪怕它自增、有序、看起来“应该能快”。 必须执行
ALTER TABLE t ADD INDEX idx_log_id (log_id)才能加速等值或范围查询 如果该字段还经常用于
ORDER BY log_id DESC或分页(
LIMIT),索引依然有效,无需额外操作 注意:若同时存在复合查询如
WHERE status = ? AND log_id > ?,单列索引效果有限,应考虑联合索引顺序
自增字段做联合索引时,位置很关键
当自增字段参与联合索引(例如
(user_id, create_time, id)),它的价值取决于是否满足最左前缀匹配。由于自增字段值高度离散且无筛选性(几乎每个值只出现一次),把它放在联合索引末尾通常是合理的;但如果放最左边(
(id, user_id)),那
WHERE user_id = ?就完全用不上这个索引。
典型反例:
CREATE INDEX idx_bad ON orders (order_id, user_id); -- order_id 是自增主键 SELECT * FROM orders WHERE user_id = 123; -- 索引失效
正确做法是把高选择性字段(如
user_id)放前面,自增字段仅用于排序或去重补位:
CREATE INDEX idx_good ON orders (user_id, order_id); -- 支持 WHERE + ORDER BY order_id
INSERT 性能受自增索引影响,但通常可忽略
InnoDB 的自增锁(auto-inc lock)在并发插入时可能成为瓶颈,尤其使用
INSERT ... SELECT或批量插入未指定值时。不过日常业务中,只要不是每秒数万级插入,这个开销远小于网络、事务、磁盘 I/O 带来的延迟。 避免在事务里长时间持有自增字段插入(比如先
INSERT再休眠几秒再
COMMIT),会阻塞其他插入 如果真遇到自增争抢,可考虑
innodb_autoinc_lock_mode = 2(须确保 binlog_format = ROW) 不要为了“提升 INSERT 速度”而删掉主键索引——没有主键的 InnoDB 表会自建隐藏聚簇索引,更难维护且无法被外键引用
实际设计中最容易被忽略的,是把自增当成“天然索引”的直觉。它只是个生成规则,索引与否、怎么索引,全看你怎么定义约束和显式索引。
