Oracle SKIP LOCKED 是否防止不可重复读取?
Does Oracle SKIP LOCKED prevent non-repeatable reads?
一个事务以 READ COMMITTED 隔离执行这 2 个语句
SELECT * FROM CATS WHERE ID=1 FOR UPDATE SKIP LOCKED
SELECT * FROM CATS WHERE ID=1
第一个查询 return 是 ID 1 行。我想知道第二个查询是否总是 return 值等于第一个查询的结果?
我的顾虑如下
我已阅读以下问题和相关文章:
Force Oracle to return TOP N rows with SKIP LOCKED.
如果我理解正确,Oracle 首先计算结果集,它打开游标,然后对于每一行,如果该行已被锁定,则跳过该行。如果没有 SKIP LOCKED
,打开游标时结果集将被锁定。
这是正确的吗?
如果是,给定 READ COMMITTED 隔离:
一个事务T1
执行这条语句
SELECT * FROM CATS WHERE ID=1 FOR UPDATE SKIP LOCKED
一个并发事务T2
更新同一个结果集
UPDATE CATS SET CATS.AGE = 10 WHERE CATS.ID = 1
我想知道在下面的情况下,T2 是否可以在行被 T1 锁定之前更新行:
- T1:Oracle 计算结果集
- T2:Oracle更新相同的结果集和COMMIT
- T1:Oracle 打开游标
- T1:Oracle 对每一行,如果该行已经被锁定则跳过该行
可能吗?
如果 skip locked
成功 returns ID = 1
行后续查询将始终 return 值等于第一个查询的结果。
在 select for update skip locked
的情况下,Oracle 不会首先计算结果集,而是在获取时检查块和行。我将尝试通过编写伪代码来解释它
start select for update skip locked
open cursor, skip_locked_SCN := next SCN;
start fetching
for block in table_data_blocks loop
if block.SCN < skip_locked_SCN then -- unchanged block
for row in block.rows(where id = :id) loop -- filter rows
if row is locked then
-- skip that row
else
add_to_resultset(row);
end if;
end loop;
else -- block has been changed
-- go to undo segment and get previous version
undo_block := get_from_UNDO(block); -- (ORA-01555: snapshot too old may be raised)
for undo_row in undo_block.rows(where id = :id) loop
actual_row = block.rows(where rowid = undo_row.rowid); -- get actual version of appropriate row by rowid
if actual_row is locked then
-- skip that row
else
-- check if data in the row remains unchanged
if actual_row.data = undo_row.data then
add_to_resultset(actual_row);
else
-- data changed, skip that row
end if;
end if;
end loop;
end if;
end loop;
一个事务以 READ COMMITTED 隔离执行这 2 个语句
SELECT * FROM CATS WHERE ID=1 FOR UPDATE SKIP LOCKED
SELECT * FROM CATS WHERE ID=1
第一个查询 return 是 ID 1 行。我想知道第二个查询是否总是 return 值等于第一个查询的结果?
我的顾虑如下
我已阅读以下问题和相关文章: Force Oracle to return TOP N rows with SKIP LOCKED.
如果我理解正确,Oracle 首先计算结果集,它打开游标,然后对于每一行,如果该行已被锁定,则跳过该行。如果没有 SKIP LOCKED
,打开游标时结果集将被锁定。
这是正确的吗?
如果是,给定 READ COMMITTED 隔离:
一个事务
T1
执行这条语句SELECT * FROM CATS WHERE ID=1 FOR UPDATE SKIP LOCKED
一个并发事务
T2
更新同一个结果集UPDATE CATS SET CATS.AGE = 10 WHERE CATS.ID = 1
我想知道在下面的情况下,T2 是否可以在行被 T1 锁定之前更新行:
- T1:Oracle 计算结果集
- T2:Oracle更新相同的结果集和COMMIT
- T1:Oracle 打开游标
- T1:Oracle 对每一行,如果该行已经被锁定则跳过该行
可能吗?
如果 skip locked
成功 returns ID = 1
行后续查询将始终 return 值等于第一个查询的结果。
在 select for update skip locked
的情况下,Oracle 不会首先计算结果集,而是在获取时检查块和行。我将尝试通过编写伪代码来解释它
start select for update skip locked
open cursor, skip_locked_SCN := next SCN;
start fetching
for block in table_data_blocks loop
if block.SCN < skip_locked_SCN then -- unchanged block
for row in block.rows(where id = :id) loop -- filter rows
if row is locked then
-- skip that row
else
add_to_resultset(row);
end if;
end loop;
else -- block has been changed
-- go to undo segment and get previous version
undo_block := get_from_UNDO(block); -- (ORA-01555: snapshot too old may be raised)
for undo_row in undo_block.rows(where id = :id) loop
actual_row = block.rows(where rowid = undo_row.rowid); -- get actual version of appropriate row by rowid
if actual_row is locked then
-- skip that row
else
-- check if data in the row remains unchanged
if actual_row.data = undo_row.data then
add_to_resultset(actual_row);
else
-- data changed, skip that row
end if;
end if;
end loop;
end if;
end loop;