SQL Server Transaction Isolation Levels documentation 将幻像行定义为“与搜索条件匹配但最初没有看到”(强调我的)。因此,发生幻读需要不止一个SELECT 语句。
在执行SELECT 语句执行期间插入的数据可能不会在READ COMMITTED 隔离级别中返回,具体取决于时间,但这不是定义上的幻读。下面的示例显示了这种行为。
--create table with enough data for a long-running SELECT query
CREATE TABLE dbo.PhantomReadExample(
PhantomReadExampleID int NOT NULL
CONSTRAINT PK_PhantomReadExample PRIMARY KEY
, PhantomReadData char(8000) NOT NULL
);
--insert 100K rows
WITH
t10 AS (SELECT n FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) t(n))
,t1k AS (SELECT 0 AS n FROM t10 AS a CROSS JOIN t10 AS b CROSS JOIN t10 AS c)
,t1m AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS num FROM t1k AS a CROSS JOIN t1k AS b)
INSERT INTO dbo.PhantomReadExample WITH(TABLOCKX) (PhantomReadExampleID, PhantomReadData)
SELECT num*2, 'data'
FROM t1m
WHERE num <= 100000;
GO
--run this on connection 1
SELECT *
FROM dbo.PhantomReadExample
ORDER BY PhantomReadExampleID;
GO
--run this on connection 2 while the connection 1 SELECT is running
INSERT INTO dbo.PhantomReadExample(PhantomReadExampleID, PhantomReadData)
VALUES(1, 'data');
GO
在SELECT 查询扫描期间读取行时会在行上获取共享锁,以确保只读取已提交的数据,但一旦读取数据就会立即释放这些锁以提高并发性。这允许其他会话在 SELECT 查询运行时插入、更新和删除行。
在这种情况下不返回插入的行,因为有序聚集索引扫描已经超过了插入点。