Oracle SKIP LOCKED会阻止不可重复的读取吗?

时间:2017-03-20 09:08:25

标签: sql oracle transactions isolation-level

事务使用READ COMMITTED隔离

执行这两个语句
SELECT * FROM CATS WHERE ID=1 FOR UPDATE SKIP LOCKED
SELECT * FROM CATS WHERE ID=1 

第一个查询返回ID 1行。我想知道第二个查询是否总是返回值等于第一个查询的结果?

我的疑虑如下

我已阅读以下问题及相关文章: 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锁定之前可以更新一行,则给出以下情况:

  1. T1:Oracle计算结果集
  2. T2:Oracle更新相同的结果集和COMMIT
  3. T1:Oracle打开光标
  4. T1:每行的Oracle,如果该行已被锁定,则会跳过该行
  5. 有可能吗?

1 个答案:

答案 0 :(得分:1)

如果skip locked成功返回ID = 1行,则后续查询将始终返回等于第一个查询结果的值。 在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;