insert into t values(1);
insert into t values(2);
insert into t values(3);
commit;
现在连续两次执行同一个SQL语句,如下:
SQL>select * from t where rownum <= 1 for update skip locked;
ID
----------
1
1 row selected.
SQL>select * from t where rownum <= 1 for update skip locked;
请问: 这里应该显示什么结果?
A. | B. | C. | D. |
no rows selected | ID ---------- 1 1 row selected. | ID ---------- 2 1 row selected. | ID ---------- 3 1 row selected. |
现在再开启另一个 SESSION窗口执行:
SQL>select * from t where rownum <= 1 for update skip locked;
1. 我们看看第一个结果, 应该是 B.
这个小知识点地球人都知道, 就是: 同一个SESSION的锁定可以重复获取.
2. 第二题的结果是 A.
我们分析一下为什么出现这个结果,初看感觉是前面的SESSION锁定了全部的记录.
但我们从第二个SESSION 窗口中执行以下语句:
SQL> select * from t for update skip locked;
ID
----------
2
3
2 rows selected.
可以发现后面两条记录并没有被第一个SESSION锁定.
那为何 select * from t where rownum <= 1 for update skip locked; 这个就 no rows 呢?
其实是因为 for update 执行情况我们误解了.
对于存在 for update 的SQL语句, Oracle 首先在没有 For Update 的情况下选取出满足条件的记录.
然后对选取出的记录进行 Lock. 如果后 Skip Locked, 那么在锁定记录的时候对于满足条件的记录中已经被锁定的将会再次被筛选掉.
这就是For update skip locked 的执行过程.
我们第一个SESSION 中锁定了第一条记录(Rownum <=1 ), 第二个SESSION 中依然按照 (Rownum<=1) 取到第一条ID=1的记录,
在后继执行 For Update Skip Locked 的时候发现记录被锁定, 则被筛选掉, 那么最终结果就是 no rows 了.
总结: Skip Locked 是在SQL查询结果集的基础上进行二次筛选.
增加一个由 kmpx 发现的一个知识点: 使用了 For Update 查询的表上会放置一个 MODE=3 的 TM 锁定, 而不管该查询是否查询到数据.