阻塞
如果一个会话持有某个资源的锁,而另一个会话在请求这个资源,就会出现阻塞(blocking )。这样一来,请求的会话会被阻塞,它会“挂起”,直至持有锁的会话放弃锁定的资源。
数据库中有5 条常见的DML 语句可能会阻塞,具体是:INSERT 、UPDATE 、DELETE 、MERGE 和SELECT FOR UPDATE 。对于一个阻塞的SELECT FOR UPDATE ,解决方案很简单:只需增加NOWAIT 子句,它就不会阻塞了。
例子
时间点 | Session1 | Session2 |
T1 | 开始事务 |
|
T2 |
| 开始事务 |
T3 | Update A set col1=2 where id=1 获得id 为1 的行的排它锁(X ) |
|
T4 |
| Update A set col1=5 where id=1 等待session1 释放id 为1 的行的排它锁(X ) |
T5 | commit |
|
T6 |
| commit |
在上面的例子中
Session1 在T3 时刻获得id 为1 的行的排它锁(X), 在T4 时刻,session2 也要更新该行,也要在行上加排它锁(X ),由于session1 的事务还没有提交,因此session2 这个时候就被阻塞了,只到session1 的事务提交或回滚后session2 也获得此行的排它锁(X )使事务得以进行。
死锁
如果你有两个会话,每个会话都持有另一个会话想要的资源,此时就会出现死锁(deadlock )。
例子
时间点 | Session1 | Session2 |
T1 | 开始事务 |
|
T2 |
| 开始事务 |
T3 | Update A set col1=2 where id=1 获得id 为1 的行的排它锁(X ) |
|
T4 |
| Update B set col1=5 where id=4 获得id 为4 的行的排它锁(X ) |
T5 | Update B set col1=6 where id=4 等待session2 释放id 为4 的行的排它锁(X ) |
|
T6 |
| Update A set col1=8 where id=1 等待session1 释放id 为1 的行的排它锁(X ) |
T7 | commit |
|
T8 |
| commit |
在上面的例子中
Session1 在T3 时刻获得表A 的id 为1 的行的排它锁(X), 在T4 时刻,session2 获得表B 的id 为4 的行的排它锁(X),T5 时刻,session1 要更新表B 的id 为4 的行,要在行上加排它锁(X ),由于session2 的事务还没有提交,因此session1 这个时候就被阻塞了,T6 时刻session2 要更新表A 的id 为1 的行,要在行上加排它锁(X ),由于session1 的事务还没有提交,因此session2 这个时候就被阻塞了,这时两个会话都被对方阻塞了于是死锁就发生了。
在oracle 中,oracle 会自己检测死锁,一旦检测到就会回滚该语句,在这个例子中oracle 会在T6 时刻检测到死锁并回滚该语句,但session2 在T4 上仍然保持着锁,因此session1 还是不能继续进行。
即使对同一个表操作也会出现死锁的情况
时间点 | Session1 | Session2 |
T1 | 开始事务 |
|
T2 |
| 开始事务 |
T3 | Update A set col1=2 where id=1 获得id 为1 的行的排它锁(X ) |
|
T4 |
| Update A set col1=5 where id=4 获得id 为4 的行的排它锁(X ) |
T5 | Update A set col1=6 where id=4 等待session2 释放id 为4 的行的排它锁(X ) |
|
T6 |
| Update A set col1=8 where id=1 等待session1 释放id 为1 的行的排它锁(X ) |
T7 | commit |
|
T8 |
| commit |