不能只写auto_increment,因为它必须搭配主键或唯一索引使用,且仅支持整数类型;否则建表报错error 1075,因innodb需通过索引定位并加锁最大值以保证并发安全。

为什么不能只写 AUTO_INCREMENT 就完事?
很多人建表时只加 AUTO_INCREMENT,却忽略它必须配合主键或唯一索引才能生效。InnoDB 要求自增列必须是索引的一部分,否则会报错 ERROR 1075: Incorrect table definition; there can be only one auto column and it must be defined as a key。
常见错误包括:
- 漏写
PRIMARY KEY(id)或UNIQUE KEY(id) - 把
AUTO_INCREMENT加在非整数类型字段上(如VARCHAR) - 用
INT却没加UNSIGNED,导致实际可用范围只剩一半(21亿 vs 42亿)
正确写法必须显式声明主键,并确保数据类型匹配:
CREATE TABLE orders ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL, PRIMARY KEY (id) ) ENGINE=InnoDB;
innodb_autoinc_lock_mode 设为 2 真的更高效吗?
默认值 innodb_autoinc_lock_mode = 1 在大多数业务中够用,但高并发插入(比如秒杀订单、日志流水)下容易卡在锁分配上。设为 2 可显著减少锁等待,前提是接受 ID 不连续和主从复制需配合 sync_binlog = 1。
三种模式关键差异:
-
0:表级锁,完全串行化,已基本淘汰 -
1:预分配 + mutex,平衡连续性与并发,适合事务型业务 -
2:无锁分配,ID 可能跳变(如事务回滚后不回收),仅推荐用于日志类、分析类表
检查当前设置:
SELECT @@innodb_autoinc_lock_mode;
修改需重启或动态生效(5.6+ 支持):
SET GLOBAL innodb_autoinc_lock_mode = 2;
选 INT 还是 BIGINT?别等溢出才后悔
INT UNSIGNED 最大值是 4294967295(约 42 亿),对中低频写入表足够;但一旦日均写入超 10 万条,10 年就可能触顶。而 BIGINT UNSIGNED 上限是 18446744073709551615(约 1800 万亿),几乎无现实溢出风险。
选型建议:
- 用户、订单、支付等核心业务表 → 无条件用
BIGINT UNSIGNED - 配置类、字典类、权限类小表 →
INT UNSIGNED足够,节省空间 - 绝对不要用带符号的
INT或BIGINT,负值毫无业务意义且浪费一半容量
注意:BIGINT 比 INT 多占 4 字节,但对索引深度和缓存效率影响极小,远小于因溢出导致停服重置的成本。
自增值被“跳号”不是 bug,是设计使然
插入失败、事务回滚、批量 INSERT ... SELECT、甚至 REPLACE INTO 都会导致自增 ID 出现空洞。这不是异常,而是 InnoDB 为保证并发安全和性能做的取舍。
典型场景:
- 执行
INSERT INTO t VALUES (100, 'a');后又回滚 → 下一个 ID 是 101,不是 100 -
INSERT INTO t SELECT ... FROM s;预分配了 1000 个 ID,但只成功插入 800 行 → 剩余 200 个 ID 永久丢失 - 手动指定
id = 5000插入后,后续自增从 5001 开始,哪怕中间有空缺
业务代码里绝不能依赖 ID 的连续性做分页、排序或断点续传。需要严格连续序号的场景,应另建逻辑序列字段,而非强求 AUTO_INCREMENT。











