我可以确定 SQL 服务器中的身份列永远不会获得介于现有值之间的值吗?

Can I be sure that Identity column in SQL Server will never get a value between existing values?

对于上下文:考虑简化的“事件存储”table 用于我们先前存在的 CRUD 数据库。

事件 table 将有一个 BIGINT IDENTITY 主键,它也将作为事件在事件日志中的“位置”。

首先,因为我能找到的所有与相关关键字相关的问题都讨论了差距:我不关心差距,如果身份缓存导致从 12 跳到 1001,那不是实际问题。关于差距,唯一重要的是它们永远不应该被填补。

问题是:我是否可以 运行 进入这样一种场景,其中有多个插入来自并发源的 table,并且从读取端出现的一行的标识值低于 already现有标识值?

例如,如果我创建一个要处理插入到 table 中的所有行的服务,我是否可以在本地保存已处理行的最大标识值,并确定,如果下次我要求标识值大于保存值的行,我永远不会错过由于并发而插入到现有标识值之间的行?

根据我的理解,这是一个理智和防弹的假设,因为 IDENTITY 值应该在执行插入时填写,并且没有重用跳过的值的机制。但是我对 SQL 服务器没有足够的经验,所以不会质疑是否存在一些挑剔的边缘案例/特定的实现细节

  1. 完全打破了这个假设?
  2. 需要一些额外的配置来确保这一点?
  3. 如果这在默认情况下有效,是否有一些配置/T-SQL 命令可能会破坏它并且应该避免?

除非有人通过 DBCC CHECKIDENT (or truncates the table), or overrides the default identity assignment using SET IDENTITY_INSERT ON 重新播种,否则永远不会“填写”较低的值。

但是,如果您有两个具有事务控制的会话,这完全取决于您所说的“第一”是什么意思 - 从理论上讲,一个事务可以在获取和提交其价值之前花费更多时间,而另一个交易可以在之后花费更多时间,因此“首先”开始或完成的交易(再次取决于您如何衡量)实际上获得了后来的价值。假设你有这个 table:

DROP TABLE IF EXISTS dbo.what;
GO
CREATE TABLE dbo.what
(
  id      int       IDENTITY(1,1), 
  spid    int       NOT NULL DEFAULT @@SPID,
  txStart datetime2 NOT NULL DEFAULT sysdatetime()
);
GO

然后打开两个新查询windows。在 window 1 中,开始这个:

-- start this one first, but make it wait
SELECT status = 'Starting time', ts = sysdatetime();

BEGIN TRANSACTION;
WAITFOR DELAY '00:00:30';
INSERT dbo.what DEFAULT VALUES;
COMMIT TRANSACTION;

SELECT status = 'Committed time', ts = sysdatetime();

SELECT id, spid, txStart, 
    [mine?] = CASE spid WHEN @@SPID THEN 'yes' ELSE 'no' END
  FROM dbo.what;

然后,在 window #2 中,开始这个:

-- start this one second
SELECT status = 'Starting time', ts = sysdatetime();

BEGIN TRANSACTION;
INSERT dbo.what DEFAULT VALUES;
WAITFOR DELAY '00:00:45';
COMMIT TRANSACTION;

SELECT status = 'Committed time', ts = sysdatetime();

SELECT id, spid, txStart, 
    [mine?] = CASE spid WHEN @@SPID THEN 'yes' ELSE 'no' END
  FROM dbo.what;

您的结果应该是这样的,表明最先提交的事务实际上获得了更高的标识值:

没有事务或其他延迟可能性,则不,先到先得,最早的插入将始终具有最低的标识值。也就是说,如果您需要确保应用程序 1 的事件(您知道首先发生)在应用程序 2 的事件(您知道它发生在一微秒后)之前正确定位,那么依赖应用程序时间戳会更安全。按您知道的那个值排序,而不是依赖于关于任意代理标识符分配顺序的假设。恕我直言。