在项目中,有一竞价模块,多个用户在竞价结束时,系统自动录入结果,系统操作为,先查询对应的结果,如果没有就向数据库插入数据。
出现的问题,第一次请求到时,无结果,插入;第二次请求到时,查询无结果(未查到插入的结果),再次插入了结果;第三次请求查到了结果。
解决方案:用行级排他锁。
具体实现:
//给数据加锁,防止数据重复插入
String sql = "select 1 from a s where s.b = ? for update ";
Db.query(sql, new Object[]{id});
数据库锁机制:
两种锁机制
共享锁(Share Lock):即S锁,是通过对数据存取的高并行性来实现的。加了共享锁的数据库对象可以被其它事务读取,但是不能被其它事务修改。
独占锁(Exclusive Lock):即X锁,又称排它锁,是用来防止同时共享相同资源的锁。加了独占锁的数据库对象不能被其它事务读取和修改。
默认情况下的select ... for update 语句与DML语句相似,效果相当于启动了一个会话级别的事务,在对应的数据表( select 所涉及的所有数据表)上加入一个数据表级共享锁( TM , lmode=3 )。同时,在对应的数据行中加入独占锁( TX , lmode=6 )。
以下来自也网络
SQL > select * from t1;
ID
1
2
3
4
SQL > alter table t1 add primary key(id);
表已更改。
SQL > select * from v$ lock where type in ( 'TX', 'TM');
未选定行
SQL > insert into t1 values( 5);
已创建 1 行。
SQL > select sid, type,id1,id2,lmode,request,block from v$ lock where type in ( 'TX', 'TM');
SID TY ID1 ID2 LMODE REQUEST BLOCK
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
141 TM 82975 0 3 0 0
141 TX 131084 1787 6 0 0
会话二:
SQL > insert into t1 values( 5);
__
(阻塞)
会话一:
SQL > select sid, type,id1,id2,lmode,request,block from v$ lock where type in ( 'TX', 'TM');
SID TY ID1 ID2 LMODE REQUEST BLOCK
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
19 TX 131084 1787 0 4 0
(此处出现申请 4号锁, insert本身没有阻塞,而是在等待判断主键冲突否)
19 TM 82975 0 3 0 0
141 TM 82975 0 3 0 0
19 TX 327697 1662 6 0 0
141 TX 131084 1787 6 0 1
SQL > update t1 set id = 5 where id = 4;
已更新 1 行。
SQL > select sid, type,id1,id2,lmode,request,block from v$ lock where type in ( 'TX', 'TM');
SID TY ID1 ID2 LMODE REQUEST BLOCK
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
141 TM 82975 0 3 0 0
141 TX 327706 1663 6 0 0
SQL > select sid from v$mystat where rownum = 1;
SID
141
SQL > select sid,event from v$session_wait where sid in( 141, 19);
SID EVENT
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
19 SQL *Net message from client
141 SQL *Net message from client
会话二:
SQL > select sid from v$mystat where rownum = 1;
SID
19
SQL > update t1 set id = 5 where id = 4;
__(阻塞)
会话一:
SQL > select sid,event from v$session_wait where sid in( 141, 19);
SID EVENT
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
19 enq: TX - row lock contention(等待事件出现tx锁,原因是行锁争用)
141 SQL *Net message from client
SQL > select sid, type,id1,id2,lmode,request,block from v$ lock where type in ( 'TX', 'TM');
SID TY ID1 ID2 LMODE REQUEST BLOCK
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
19 TX 327706 1663 0 6 0
(可以看到会话二在申请 6号锁,而 141的TX锁block为 1,表示阻塞了别的会话)
19 TM 82975 0 3 0 0
141 TM 82975 0 3 0 0
141 TX 327706 1663 6 0 1
SQL > delete t1 where id = 4;
已删除 1 行。
SQL > select sid, type,id1,id2,lmode,request,block from v$ lock where type in ( 'TX', 'TM');
SID TY ID1 ID2 LMODE REQUEST BLOCK
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
141 TM 82975 0 3 0 0
141 TX 327706 1663 6 0 0
SQL > select sid from v$mystat where rownum = 1;
SID
141
SQL > select sid,event from v$session_wait where sid in( 141, 19);
SID EVENT
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
19 SQL *Net message from client
141 SQL *Net message from client
会话二:
SQL > select sid from v$mystat where rownum = 1;
SID
19
SQL > delete t1 where id = 4;
__(阻塞)
会话一:
SQL > select sid,event from v$session_wait where sid in( 141, 19);
SID EVENT
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
19 enq: TX - row lock contention(等待事件出现tx锁,原因是行锁争用)
141 SQL *Net message from client
SQL > select sid, type,id1,id2,lmode,request,block from v$ lock where type in ( 'TX', 'TM');
SID TY ID1 ID2 LMODE REQUEST BLOCK
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
19 TX 327706 1663 0 6 0
(可以看到会话二在申请 6号锁,而 141的TX锁block为 1,表示阻塞了别的会话)
19 TM 82975 0 3 0 0
141 TM 82975 0 3 0 0
141 TX 327706 1663 6 0 1
KADDR
SID:锁所在的会话id
TYPE:锁的类型,类型有很多种,常用的有TX(事务锁)、TM锁(表级锁)
ID1:TM锁为对象的object_id,TX锁为v$ transaction 中 XIDUSN * 2^ 16 + XIDSLOT
ID2:TM锁为 0,TX锁为v$ transaction 中 XIDSQN
LMODE:锁的模式,有 2~ 6,越大锁的限制越高,
REQUEST:为 0表示没有申请锁,其他 2~ 6表示在申请该模式的锁
CTIME:表示锁从出现到现在的时间
BLOCK:为 1表示阻塞了其他会话申请锁,为 0表示没有阻塞别人。
会话一:
SQL > lock table t1 in share mode;
表已锁定。
会话二:
SQL > update t1 set id = 11 where id = 1;
__(等待)
直到会话一 commit或roll,会话二执行。
| 锁模式 | 锁描述 | 含义 | 锁定表的SQL |
| 0 | None | ||
| 1 | Null | 空,本模式是oracle预留模式 | |
| 2 | Row Share(RS) 又叫(SS) | 行级共享锁,是限制最少的TM锁,可以提供最高程度的并发性。其他会话可以对锁定的表进行任何类型的DML操作,还可以与其他会话锁并存 | Lock table t1 in row share mode; |
| 3 | Row Exclusive Table Lock(RX) 又叫(SX) | 行级排他锁,通常已经有事务在修改行或者select…for update 修改结果集。允许其他事务对锁定的表进行select insert update delete 或 lock table 同时锁定一张表 | Lock table t1 in row exclusive mode; |
| 4 | Share Table Lock(S) | 共享锁,其他事务可以查询锁定的表但不能修改,只允许当前事务修改,但可以多个事务持有它 | Lock table t1 in share mode; |
| 5 | Share Row Exclusive Table Lock(SRX) 又叫SSX | 共享行级排他锁,同一时间只允许一个事务持有和修改锁定的表,其他事务可以查询但不能修改 | Lock table t in share row exclusive mode; |
| 6 | Exclusive Table Lock (X) | 排他锁,是限制最高的TM锁,禁止其他事务执行任何类型的DML语句或者锁表 一个表一般只能有一个6号锁 | Lock table t1 in exclusive mode; |
| 锁模式 | 锁名称 | 允许级别 | 互斥级别 |
| 2 | 行级共享锁 | 2 3 4 5 6 | 6 |
| 3 | 行级排他锁 | 2 3 | 4 5 6 |
| 4 | 共享锁 | 2 4 | 3 5 6 |
| 5 | 共享行级排他锁 | 2 | 3 4 5 6 |
| 6 | 排他锁 | 2 3 4 5 6 |
1、 NULL,可以某些情况下,如分布式数据库的查询会产生此锁。
2、SS,表结构共享锁
3、SX,表结构共享锁 +被操作的记录的排它锁
4、S, 表结构共享锁 +所有记录共享锁
5、SRX 表结构共享锁 +所有记录排它锁
6、X 表结构排它锁 +所有记录排它锁
2 SS, 表结构共享 +加上部分数据共享.虽然有人说,ORACLE里,数据没有S状态,但我还是愿意这 样理解:第 1个S代表表结构共享,第 2个代表表里的数据共享. 你可以想象一下,当往子表里增 加纪录时,主表的相关主键是不是得处于共享模式.
3 SX, 用于DML操作,第 1个S代表表结构共享,第 2个代表表里被操作的数据独占.
4 S, 代表表结构+表里的数据都是处于共享模式.当对表创建索引时,在创建期间,表处于这种模式.
5 SRX =S( 4) +SX( 3),
6 X, 删除表是会用上.
表已锁定。
141 > select sid, type,id1,id2,lmode,request,block from v$ lock where type in ( 'TX', 'TM');
SID TY ID1 ID2 LMODE REQUEST BLOCK
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
141 TM 82975 0 2 0 0
19 > select * from t1 for update; / /另开会话给t1上 3和 6号锁
ID
- - - - - - - - - -
1
2
3
4
141 > select sid, type,id1,id2,lmode,request,block from v$ lock where type in ( 'TX', 'TM');
SID TY ID1 ID2 LMODE REQUEST BLOCK
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
19 TM 82975 0 3 0 0
141 TM 82975 0 2 0 0
19 TX 393221 1754 6 0 0
可以看到 236可以共存,没有互斥
19 > lock table t1 in share mode; / /给表再上 4号锁
表已锁定。
141 > select sid, type,id1,id2,lmode,request,block from v$ lock where type in ( 'TX', 'TM');
SID TY ID1 ID2 LMODE REQUEST BLOCK
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
19 TM 82975 0 5 0 0
141 TM 82975 0 2 0 0
19 TX 393221 1754 6 0 0
因为 3和 4不能共存,因此 3 + 4升级为 5号锁
19 > lock table t1 in exclusive mode; / /给表上 6号锁,发生等待
__
141 > select sid, type,id1,id2,lmode,request,block from v$ lock where type in ( 'TX', 'TM');
SID TY ID1 ID2 LMODE REQUEST BLOCK
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
19 TM 82975 0 5 6 1
141 TM 82975 0 2 0 1
19 TX 393221 1754 6 0 0
因为同是 19号会话,上 6号TM只需将原有 5升级,但是已经存在 2号TM,产生互斥,所以等待
表已锁定。
SQL > select sid, type,id1,id2,lmode,request,block from v$ lock where type in ( 'TX', 'TM');
SID TY ID1 ID2 LMODE REQUEST BLOCK
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
141 TM 82975 0 6 0 0
SQL > update t1 set id = 5 where id = 4;
已更新 1 行。
SQL > select sid, type,id1,id2,lmode,request,block from v$ lock where type in ( 'TX', 'TM');
SID TY ID1 ID2 LMODE REQUEST BLOCK
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
141 TM 82975 0 6 0 0
141 TX 262155 1324 6 0 0
表已锁定。
141 > select sid, type,id1,id2,lmode,request,block from v$ lock where type in ( 'TX', 'TM') order by 1;
SID TY ID1 ID2 LMODE REQUEST BLOCK
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
141 TM 82975 0 2 0 0
19 > update t1 set id = 6 where id = 4;
已更新 1 行。
141 > select sid, type,id1,id2,lmode,request,block from v$ lock where type in ( 'TX', 'TM');
SID TY ID1 ID2 LMODE REQUEST BLOCK
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
19 TM 82975 0 3 0 0
141 TM 82975 0 2 0 0
19 TX 458778 1387 6 0 0
会话一:
SQL > update t1 set id = 11 where id = 1;
已更新 1 行。
会话二:
SQL > update t2 set id = 11 where id = 1;
已更新 1 行。
会话一:
SQL > update t2 set id = 11 where id = 1;
__(等待)
会话二:
SQL > update t1 set id = 11 where id = 1;
__(等待)
会话一:
SQL > update t2 set id = 11 where id = 1;
update t2 set id = 11 where id = 1
*
第 1 行出现错误:
ORA - 00060: 等待资源时检测到死锁

1万+

被折叠的 条评论
为什么被折叠?



