一般来说,我们通常认为,truncate 的伪代码是:
commit; ---------- 1
truncate;
commit; ------------ 2
那我们有个疑问,commit 1和 truncate 是不可分割的吗?具有原子性吗?那我们来看看下面的演示
session 1:
SQL> create table t as select * from all_objects where rownum < 11;
Table created.
SQL> delete from t where rownum = 1;
1 row deleted.
SQL>
session 2:
SQL> lock table t in exclusive mode;
很明显,session 2 在这里被hang住了
session 1:
SQL> create table t as select * from all_objects where rownum < 11;
Table created.
SQL> delete from t where rownum = 1;
1 row deleted.
SQL> commit;
Commit complete.
SQL>
再看 session 2
SQL> lock table t in exclusive mode;
Table(s) Locked.
SQL>
加锁成功
这时我们再在 session 1上执行delete操作
session 1:
SQL> create table t as select * from all_objects where rownum < 11;
Table created.
SQL> delete from t where rownum = 1;
1 row deleted.
SQL> commit;
Commit complete.
SQL> delete from t where rownum = 1;
这时被阻塞了
接下来我们去 session 2中去尝试truncate
session 2:
SQL> lock table t in exclusive mode;
Table(s) Locked.
SQL> truncate table t;
truncate table t
*
ERROR at line 1:
ORA-00054: resource busy and acquire with NOWAIT specified
SQL>
发现truncate 失败,切到 session 1 看看
SQL> create table t as select * from all_objects where rownum < 11;
Table created.
SQL> delete from t where rownum = 1;
1 row deleted.
SQL> commit;
Commit complete.
SQL> delete from t where rownum = 1;
1 row deleted.
SQL>
发现 session 1的删除居然成功了,而session 2的truncate 却失败了。这说明,当 session 2 truncate发出commit 1 之后,还没有执行truncate,在这之前session 1的delete操作由于是被 lock table 阻塞但是时间却在 truncate 之前,所以在这个间歇中 session 抢在了前面,从而导致 truncate 失败。
其实之所以要做这个测试,也不纯粹是无聊,是因为我们有一个程序,需要首先truncate一个表,然后另一个进程去做一些数据的操作,如果truncate失败则后续的操作就有问题,但是truncate失败却不能阻止后面的进程的操作。当时我在想能否在truncate之前使用lock table 去加锁,直到其他进程dml完毕这个进程获得了独占表锁然后truncate,看看这样是否可行。结果很遗憾,我的测试结果发现这个想法是不大现实的。