浅谈Oracle select for update
1、问题是:如果多线程之下同时查询一条数据查不到,则去插入,插入的时候也是多线程插入。
所以想到用select for update来控制不允许多线程插入(其实这种方式并没有解决问题)
2、先来了解几个名词: statement: 一个SQL语句。 session: 一个由ORACLE用户产生的连接,一个用户能产生多个SESSION ,但相互之间是独立的。 transaction:所有的改动都能划分到transaction里,一个transaction包含一个或多个SQL。当一个SESSION建立的时候就是个TRANSACTION开始的时刻,此后transaction的开 始和结束由DCL控制,也就是每个COMMIT/ROLLBACK都标示着一个transaction的结束。
3、用法介绍: select ... for update会LOCK相应的ROW 。 只有一个TRANSACTION可以LOCK相应的行,也就是说如果一个ROW已经LOCKED了,那就不能被其他TRANSACTION所LOCK了。 LOCK由statement产生但却由TRANSACTION结尾(commit,rollback),也就是说一个SQL完成后LOCK还会存在,只有在COMMIT/ROLLBACK后才会师释放。 使用这个行锁的情况一般是对并发的情况要求比较高的时候,需要锁住某行进行一些更新语句之后进行释放,再让其他transaction 去操作。很好的利用了这点解决了并发的问 题。数据库中锁类型:有两种基本的锁类型,排它锁(Exclusive Locks,即X锁)和共享锁(Share Locks,即S锁)。当数据对象被加上排它锁时,其他的事务不能对它读取和 修改。加了共享锁的数据对象可以被其他事务读取,但不能修改。数据库利用这两种基本的锁类型来对数据库的事务进行并发控制。
4、Oracle给Select结果集加锁,Skip Locked(跳过加锁行获得可以加锁的结果集),请参考:点击打开链接
5、for update 后面还可以跟着[OF cols] [NOWAIT] of 的使用主要是针对多表关联的时候,如果不使用of,对两个表涉及到的行都将锁住,使用of可以指定锁定哪个表, 例如:select a.MOBILE,b.NAME from connector a,student b where a.STU_ID=b.ID and a.MOBILE='13937134399' for updata of a.MOBILE 这样的话student表中对应的行是不加锁的,对connector一个表中行加锁,不使用两个表都加锁。 [NOWAIT]的使用是当锁冲突的时候提示的情况: 当有LOCK冲突时会提示错误并结束STATEMENT而不是在那里等待.返回错误是"ORA-00054: resource busy and acquire with NOWAIT specified" ,如果不使用就会一直等待,直到锁释放之后执行。在页面上调试的时候由于异常处理不好,把数据锁住了没有提交,也没有rollback,遇到这样的情况的时候可以通过以下方式解决:
6、效果图演示 开两个窗口: 1)第一个窗口用select for update查询
3)、点击第一个窗口里的提交事务按钮,另一个窗口可以立刻获取查询结果
7、继续回到我们开始提出的问题,为什么用for update不可以避免多线程插入的问题,因为:select for update只能针对已经存在的数据进行加排他锁,如果查询的数据是 null,根本就不存在所谓的锁了。其中之一的解决办法是:加上唯一性约束条件。我们就给OPEN_UID和LOGIN_TYPE一起加唯一性约束unique。所以数据库会保证只有一个 唯一确定的记录,当两个请求同时向数据库插入相同的OPEN_UID和LOGIN_TYPE时,会采用抢占式插入,谁先插入其他方就不能再插入数据。 就会报唯一约束的异常: ### 参考:http://www.blogjava.net/sunchaojin/archive/2007/05/14/117327.html (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |