浅谈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,遇到这样的情况的时候可以通过以下方式解决:

-----查看被锁对象的序列号、sid
SELECT o.owner,o.object_name,o.object_type,s.sid,s.serial# 
FROM v$locked_object l,dba_objects o,v$session s 
WHERE l.object_id=o.object_id 
AND l.session_id=s.sid 
ORDER BY o.object_id,xidusn DESC
/
------利用sid 和序列号删除
alter system kill session '243,10265';
243是sid 10265是序列号


6、效果图演示

开两个窗口:

1)第一个窗口用select for update查询


2)在另一个窗口用同样的语句查询,会显示一直在查询...说明被block了


3)、点击第一个窗口里的提交事务按钮,另一个窗口可以立刻获取查询结果



7、继续回到我们开始提出的问题,为什么用for update不可以避免多线程插入的问题,因为:select for update只能针对已经存在的数据进行加排他锁,如果查询的数据是

null,根本就不存在所谓的锁了。其中之一的解决办法是:加上唯一性约束条件。我们就给OPEN_UID和LOGIN_TYPE一起加唯一性约束unique。所以数据库会保证只有一个

唯一确定的记录,当两个请求同时向数据库插入相同的OPEN_UID和LOGIN_TYPE时,会采用抢占式插入,谁先插入其他方就不能再插入数据。

就会报唯一约束的异常:

### 
### ### Error updating database.  Cause: java.sql.SQLIntegrityConstraintViolationException: ORA-00001: unique constraint (SLPROD.SYS_C0019132) violated
### The error occurred while setting parameters
### SQL: insert into SL$THIRD_LOGIN (OPEN_UID, REQUEST_SOURCE, LOGIN_TYPE, BIND_AID, NICK_NAME, ACCESS_TOKEN, EXPIRES_IN, UNION_ID)     values (      ?,       ?,       ?,       ?,          ?,          ?,          ?,         ?       )
### Cause: java.sql.SQLIntegrityConstraintViolationException: ORA-00001: unique constraint (SLPROD.SYS_C0019132) violated
; SQL []; ORA-00001: unique constraint (SLPROD.SYS_C0019132) violated
; nested exception is java.sql.SQLIntegrityConstraintViolationException: ORA-00001: unique constraint (SLPROD.SYS_C0019132) violated
... 86 more
Caused by: java.sql.SQLIntegrityConstraintViolationException: ORA-00001: unique constraint (SLPROD.SYS_C0019132) violated

参考:http://www.blogjava.net/sunchaojin/archive/2007/05/14/117327.html

### 回答1: Oracle中的SELECT FOR UPDATE语句用于锁定查询结果集中的行,以便在事务中进行更新操作。当一个事务使用SELECT FOR UPDATE语句锁定了某些行时,其他事务将无法对这些行进行更新操作,直到锁定的事务释放了锁。这种机制可以确保数据的一致性和完整性。 ### 回答2: Oracleselect for update 是一种锁定行功能,它可以确保在一个事务中,当一个会话开始更新数据时,另一个会话无法修改这些数据。 使用 select for update 首先需要使用 select 语句,该语句从需要更新的表中选择需要锁定的行。然后使用 for update 子句将这些行锁定。例如,下面是一个使用 select for update 的 SQL 语句: SELECT * FROM myTable WHERE id = 1 FOR UPDATE; 这个语句将检索 myTable 表中 id 为 1 的行,并将其锁定,以便其他会话无法修改此行,直到当前会话完成。 请注意,使用 select for update 会对数据库性能产生负面影响。由于锁定行后其他会话无法访问这些数据,因此可能会出现锁定竞争的情况,导致其他会话阻塞等待。因此,应谨慎使用 select for update,只在必要时使用它。同时还需要在程序中考虑一些优化思路,例如使用更细粒度的锁定,减少锁定时间等手段,以降低锁定导致的性能问题。 总之,select for updateOracle 中的一个很有用的功能,它可以确保数据的一致性和完整性。但是,在使用它时,需要仔细考虑并合理利用,以提高其性能效果。 ### 回答3: 在Oracle数据库中,SELECT ... FOR UPDATE语句用于锁定查询结果集中的行,以防止其他事务修改它们。在使用SELECT ... FOR UPDATE语句时,要在语句中明确地指定哪些列需要被锁定。如果不指定任何列,则默认会锁定所有列。 使用SELECT ... FOR UPDATE语句可以保证多个事务在并发修改同一行数据时不会发生冲突。这种锁定行的方式称为行级锁定。 SELECT ... FOR UPDATE语句可以与基于行级锁定的事务并发使用,并且可以充分利用Oracle数据库并发控制机制,提高系统的性能。 例子: 假设有一个账户表,在多个线程中需要对账户进行加款操作,需要保证加款时不会有其他线程同时进行减款操作。 可以使用以下SQL语句来实现这个功能: ```sql SELECT * FROM account WHERE account_number = '123456' FOR UPDATE; ``` 该语句会锁定account_number为"123456"的行,以防止其他事务修改该行数据。当一行被锁定时,其他事务不能修改该行。如果其他事务尝试修改该行,则会被阻塞,直到锁定被释放。 在加款操作完成后,需要手动提交事务或者回滚事务,以释放锁定。如果事务没有被提交或回滚,则锁定会一直存在,直到事务结束。 ```sql UPDATE account SET balance = balance + 100 WHERE account_number = '123456'; ``` 需要注意的是,使用SELECT ... FOR UPDATE语句时要小心死锁的问题。如果多个事务在查询同一行数据时都使用了FOR UPDATE语句,可能会出现死锁情况,这是需要谨慎处理。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值