插入3行后开事务插入5行再ROLLBACK,看AUTO_INCREMENT计数器是不是也跟着回滚——验证发现,下一条插入直接跳到了id=9,中间的编号永久消失。
先插入3行确认基线,再在一个事务里插入5行并观察AUTO_INCREMENT,最后回滚。
操作CREATE TABLE t5 (id INT PRIMARY KEY AUTO_INCREMENT, v INT); INSERT INTO t5 (v) VALUES (1),(2),(3); 查AUTO_INCREMENT → 4(下一次插入该用的id) START TRANSACTION; INSERT INTO t5 (v) VALUES (100),(101),(102),(103),(104); -- 占用id 4~8 查AUTO_INCREMENT(事务内)→ 4(information_schema缓存值,事务内还没刷新) ROLLBACK;
ROLLBACK之后,先确认数据真的回滚了,再看真正的关键问题——下一次插入用几号。
结果ROLLBACK之后 SELECT * FROM t5 → 只剩3行:(1,1) (2,2) (3,3) ← 数据确实回滚了 再插入一行 INSERT INTO t5 (v) VALUES (999) → 结果:(1,1) (2,2) (3,3) (9,999) ← 新行的id是9,不是4!
数据本身确实回滚干净了——事务里那5行(100~104)彻底消失,表里只剩最初的3行。但下一次插入拿到的id是9,中间4、5、6、7、8这5个编号永久消失了,再也不会被用到。
如果自增id要等事务提交才分配,会有什么问题。
如果两个并发事务都在等"提交时才分配id",就必须让它们按提交顺序排队分配,这意味着自增id的分配会变成一个的串行瓶颈——后提交的事务必须等前一个事务提交完,才能知道自己该用哪个id。InnoDB选择的是"插入语句执行时就立刻分配id",这样多个并发事务可以各自独立地拿号、各自插入,互不阻塞;代价就是一旦某个事务回滚,它已经拿到手的那些编号就浪费掉了,不会被回收再利用。这是用"编号可能留空洞"换"并发插入不用排队"的权衡。
每一步都是本文上面执行SQL得到的原始结果。
information_schema.TABLES:查询AUTO_INCREMENT当前值