为什么我的SQL存储过程在事务回滚后依然导致自增ID跳号?

夏辰同学_6594

夏辰同学_6594

2026-06-09

238人浏览

原创

sql server的identity值在insert执行瞬间分配且不可回收,即使事务回滚也会跳号;根本原因是identity_cache预取机制(int默认1000)及设计上id分配不参与事务控制。

为什么我的sql存储过程在事务回滚后依然导致自增id跳号?

事务回滚时IDENTITY值已分配,无法回收

SQL Server 的 IDENTITY 列在 INSERT 语句执行的**瞬间**就完成值分配,不等事务提交。哪怕你马上 ROLLBACK,那个 ID 也永久作废了——它不会归还、不会重用、也不会触发任何警告。

常见错误现象:事务里插了 3 行,第 3 行因约束失败报错,整个事务回滚;但下一次成功插入时,ID 直接跳到 +3 后的位置,比如从 100 → 103。

  • 根本原因不是“回滚没生效”,而是 SQL Server 设计上把 ID 分配视为不可逆的轻量操作
  • 即使只写 INSERT INTO t (col) VALUES ('x'); 没指定 ID,只要表有 IDENTITY 列,就触发分配
  • SET IDENTITY_INSERT ON 手动插入不会影响缓存计数,但它本身也不参与跳号逻辑

IDENTITY_CACHE 是默认开启的幕后推手

SQL Server 2012+ 默认启用 IDENTITY_CACHE,它让每次分配不是加 1,而是预取一段(INT 类型默认 1000 个),存在内存里批量用。一旦进程崩溃、实例重启或 AlwaysOn 故障转移,这部分未落地的 ID 就彻底丢失。

你看到的“跳号差值 ≈ 1000”不是偶然——那是缓存块大小,且这个值不可配置,只由数据类型绑定(BIGINT 是 10000)。

  • 查当前状态:SELECT name, value FROM sys.database_scoped_configurations WHERE name = 'IDENTITY_CACHE';,返回 value = 1 即开启
  • 老版本(2016 及以前)无此视图,得靠 DBCC TRACESTATUS(272) 查跟踪标志
  • 禁用它(设为 0)能消除缓存导致的大跳号,但高并发插入性能会明显下降,闩锁争用上升

存储过程中无法绕过这个机制

你在存储过程里加 BEGIN TRAN / COMMIT / ROLLBACK,对 IDENTITY 分配行为毫无影响。它不看事务边界,只看 INSERT 是否被执行。

BrandCrowd
BrandCrowd

BrandCrowd是一款面向品牌创建的在线 Logo 设计和营销素材生成工具。

下载

典型误判场景:

  • 用 TRY...CATCH 捕获错误后 ROLLBACK,以为能“挽回”ID —— 实际不能
  • 在循环中逐条 INSERT 并检查 @@ERROR,每失败一次就丢一个 ID
  • 用 IF NOT EXISTS 做存在性校验再插入,结果校验和插入之间被并发写入抢占,导致唯一冲突 + 回滚 + ID 浪费

真正可控的替代方案只有业务层介入

如果你的业务真要求“显示序号连续”(比如订单号、单据流水号),别碰 IDENTITY。它天生不是干这个的。

务实选择:

  • 用 SEQUENCE 对象(SQL Server 2012+):NEXT VALUE FOR seq_order_no 显式取号,再插入到普通字段;比 IDENTITY 更可控,但仍有小概率间隙(如取号后应用崩溃)
  • 号段表 + 行锁:UPDATE number_pool SET current_no = current_no + 1 OUTPUT INSERTED.current_no WHERE type = 'order';,确保原子性
  • 纯展示用序号?直接 ROW_NUMBER() OVER (ORDER BY create_time),查的时候算,不存、不依赖、不跳

跳号本身不是 bug,是性能与一致性的权衡结果。关键在于分清:哪部分 ID 是给数据库当主键用的,哪部分是给人看的——混在一起,早晚踩坑。

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
数据分析工具有哪些
数据分析工具有哪些

数据分析工具有Excel、SQL、Python、R、Tableau、Power BI、SAS、SPSS和MATLAB等。详细介绍:1、Excel,具有强大的计算和数据处理功能;2、SQL,可以进行数据查询、过滤、排序、聚合等操作;3、Python,拥有丰富的数据分析库;4、R,拥有丰富的统计分析库和图形库;5、Tableau,提供了直观易用的用户界面等等。

2023.10.12

4103

8

SQL中distinct的用法
SQL中distinct的用法

SQL中distinct的语法是“SELECT DISTINCT column1, column2,...,FROM table_name;”。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2023.10.27

871

4

SQL中months_between使用方法
SQL中months_between使用方法

在SQL中,MONTHS_BETWEEN 是一个常见的函数,用于计算两个日期之间的月份差。想了解更多SQL的相关内容,可以阅读本专题下面的文章。

2024.02.23

1069

5

SQL出现5120错误解决方法
SQL出现5120错误解决方法

SQL Server错误5120是由于没有足够的权限来访问或操作指定的数据库或文件引起的。想了解更多sql错误的相关内容,可以阅读本专题下面的文章。

2024.03.06

5981

10

sql procedure语法错误解决方法
sql procedure语法错误解决方法

sql procedure语法错误解决办法:1、仔细检查错误消息;2、检查语法规则;3、检查括号和引号;4、检查变量和参数;5、检查关键字和函数;6、逐步调试;7、参考文档和示例。想了解更多语法错误的相关内容,可以阅读本专题下面的文章。

2024.03.06

2883

4

oracle数据库运行sql方法
oracle数据库运行sql方法

运行sql步骤包括:打开sql plus工具并连接到数据库。在提示符下输入sql语句。按enter键运行该语句。查看结果,错误消息或退出sql plus。想了解更多oracle数据库的相关内容,可以阅读本专题下面的文章。

2024.04.07

5960

11

sql中where的含义
sql中where的含义

sql中where子句用于从表中过滤数据,它基于指定条件选择特定的行。想了解更多where的相关内容,可以阅读本专题下面的文章。

2024.04.29

7961

6

sql中删除表的语句是什么
sql中删除表的语句是什么

sql中用于删除表的语句是drop table。语法为drop table table_name;该语句将永久删除指定表的表和数据。想了解更多sql的相关内容,可以阅读本专题下面的文章。

2024.04.29

1090

5

sql中删除一列的命令是什么
sql中删除一列的命令是什么

在sql中,使用alter table语句可以删除一列,语法为:alter table table_name drop column column_name。想了解更多sql的相关内容,可以阅读本专题下面的文章。

2024.04.29

952

5

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
phpStudy极速入门视频教程
phpStudy极速入门视频教程

共6课时 | 54.6万人学习

独孤九贱(4)_PHP视频教程
独孤九贱(4)_PHP视频教程

共89课时 | 133.4万人学习