我设法通过在一个事务中运行2条select语句(例如https://blobeater.blog/2017/10/26/sql-server-phantom-reads/)来找到幻象读取的所有说明
BEGIN TRAN
SELECT #1
DELAY DURING WHICH AN INSERT TAKES PLACE IN A DIFFERENT TRANSACTION
SELECT #2
END TRAN
是否可以在一个select语句中重现幻像读取的内容?这意味着select语句从事务#1开始。然后,insert在事务#2上运行并提交。最后,事务1的select语句完成,但不返回事务2已插入的行。
答案 0 :(得分:2)
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
查询运行时,其他会话插入,更新和删除行。
在这种情况下,不会返回插入的行,因为有序的聚集索引扫描已经超过了插入点。
答案 1 :(得分:1)
下面是幻像读取的wikipedia definition
幻像读取发生在事务过程中的新行中 被另一个事务添加到正在读取的记录中。
当执行 SELECT ... WHERE操作。幻像读取异常是特殊的 事务1重复一个有范围的操作时不可重复读取的情况 SELECT ... WHERE查询,以及两个操作之间的事务2 在目标表中创建(即插入)新行(满足 该WHERE子句。
在单个读取查询中肯定可以重现(当然,还必须发生其他数据库活动才能产生幻像行)。
设置
CREATE TABLE Test(X INT PRIMARY KEY);
连接1 (保持运行状态)
SET NOCOUNT ON;
WHILE 1 = 1
INSERT INTO Test VALUES (CRYPT_GEN_RANDOM(4))
连接2
如果在已读提交的锁隔离级别(本地产品的默认值并通过下面的表提示实施)下运行,这极有可能返回一些行
WITH CTE AS
(
SELECT *
FROM Test WITH (READCOMMITTEDLOCK)
WHERE X BETWEEN 0 AND 2147483647
)
SELECT *
FROM CTE c1
FULL OUTER HASH JOIN CTE c2 ON c1.X = c2.X
WHERE (c1.X IS NULL OR c2.X IS NULL)
返回的行是在表的第一次读取和第二次读取之间添加的值,用于与WHERE X BETWEEN 0 AND 2147483647
谓词匹配的行。