last_insert_id() 是 mysql 中唯一可靠获取刚插入自增主键的方式,它是会话级、语句级状态值,必须在同一连接中紧接 insert 后调用;max(id) 或 order by id desc limit 1 在并发或手动指定 id 时不可靠,且 last_insert_id(expr) 为设值操作而非查询。

直接说结论: LAST_INSERT_ID() 是 MySQL 中唯一可靠获取刚插入自增主键的方式,但它不是“查表函数”,而是会话级、语句级的“状态值”——用错时机或跨连接调用,立刻拿不到正确结果。
为什么不能 SELECT MAX(id) 或 SELECT id FROM table ORDER BY id DESC LIMIT 1
看似简单,但这是线上事故高发操作:MAX(id) 和 ORDER BY id DESC 都不保证拿到你刚插的那条。并发插入时,别人可能比你快一步,或者事务未提交导致不可见。更糟的是,如果有人手动 INSERT ... VALUES (1000, ...) 指定了 ID,MAX(id) 就彻底失真。它查的是数据,而 LAST_INSERT_ID() 记的是“你这次 INSERT 干了什么”。
怎么安全调用 LAST_INSERT_ID() —— 必须在同一连接、紧接 INSERT 之后
它的值只在当前客户端连接中有效,且只在执行了带自增列的 INSERT(或 REPLACE)后更新。一旦中间穿插了其他语句(哪怕只是 SELECT 1),值仍保留;但若执行了另一条影响自增列的 INSERT,它就被覆盖了。
实操建议:
- 应用层务必复用同一个数据库连接(connection),不要 insert 后 close,再开新连接去查
LAST_INSERT_ID() - 不要依赖 ORM 的“自动回填 ID”机制而不验证——有些 ORM 在批量插入或使用
INSERT ... ON DUPLICATE KEY UPDATE时,LAST_INSERT_ID()行为和直觉不符 - 如果用了
INSERT ... SELECT,只有当目标表有自增列且实际插入了新行时,LAST_INSERT_ID()才更新;空插入不触发
LAST_INSERT_ID(expr) 的陷阱:别误用参数形式
LAST_INSERT_ID() 无参时是取值;但带参数如 LAST_INSERT_ID(123) 是“设置”当前会话的该值(返回设置前的旧值)。这个写法常被误当成“我要取 123 对应的 ID”,其实它根本不管表结构,纯粹是会话变量赋值。生产环境除非明确要做 ID 伪装(极少见),否则永远只用无参形式。
常见错误现象:
- 代码里写了
SELECT LAST_INSERT_ID(my_id),以为能反查某条记录——不行,这只会把my_id当成新值设进去 - 存储过程中调用
LAST_INSERT_ID(0)重置,结果后续真实插入没拿到 ID——因为被你主动清零了
最易被忽略的一点:它不跨事务生效,但也不受事务回滚影响。也就是说,你 INSERT 成功后 LAST_INSERT_ID() 已写入,即使后面 ROLLBACK,这个值也不会还原。所以别指望靠回滚来“撤销”对 LAST_INSERT_ID() 的影响——它只认 INSERT 是否执行成功,不认 COMMIT 还是 ROLLBACK。











