我们有时候要对读出的数据做变更,如果使用常规的select读出来,再update,那么可能在update之前,其他的事务对所读的数据做了修改,这时我们之前读到的数据就变成了脏数据。针对这种需求,oracle提供了两者类型的读锁:
- 共享读锁
SELECT ... LOCK IN SHARE MODE
使用共享锁读数据,其他的事务也能对这些数据执行读操作。但只能等到共享锁读事务提交后才能执行修改操作。
如果在共享锁读之前,其他的事务正在对这些数据做更新,则当前的读操作会等其他事务结束后,再读最新的数据来做修改。
事务提交后,共享锁才会被释放。
共享锁一般在不互斥读的场景下使用,即对读的要求不严格。比如child表,主键用一个counter的变量通过递增的方式生成,如果使用共享锁,则两个会话读到的数据可能会一致,这会导致提交的修改也会一致,结果会导致主键冲突。
2.互斥读锁
SELECT ... FOR UPDATE
互斥读锁会锁住符合条件的行,以及与其相关的索引列表,跟我们使用update语句对这些行做操作一样。
其他事务在这些行上的更新操作将会被阻塞,如共享读更新,或者某些隔离级别的读更新事务。
互斥读锁只有在使用事务( START TRANSACTION ),或者关闭自动提交(将autocommit设为0)的情况下才起作用。否则如果允许自动提交,使用互斥读锁时符合条件的行不会被锁。
针对上面那种情况,我试了下不在方法上开启(annotation方式)spring的声明式事务,使用互斥读锁也能锁到符合条件的行,使用hibernate的upgrade_wait lock_mode,但方法结束后锁并未被释放,如果SELECT ... FOR UPDATE的后面紧接着使用 UPDATE ... SET ...更新这些数据,则会造成死锁。为什么会出现这种情况,目前还未找到原因。
参考资料:
官方文档 SELECT ... FOR UPDATE and SELECT ... LOCK IN SHARE MODE Locking Reads