Select … for update语句是我们经常使用手工加锁语句。通常情况下,select语句是不会对数据加锁,妨碍影响其他的DML和DDL操作。同时,在多版本一致读机制的支持下,select语句也不会被其他类型语句所阻碍。

借助for update子句,我们可以在应用程序的层面手工实现数据加锁保护操作。本篇我们就来介绍一下这个子句的用法和功能。

下面是采自Oracle官方文档《SQL Language Reference》中关于for update子句的说明:(请双击点开图片查看)

从for update子句的语法状态图中,我们可以看出该子句分为两个部分:加锁范围子句和加锁行为子句。下面我们分别针对两个方面的进行介绍。

加锁范围子句

在select…for update之后,可以使用of子句选择对select的特定数据表进行加锁操作。默认情况下,不使用of子句表示在select所有的数据表中加锁。

//采用默认格式for update

SQL> select * from emp where rownum<2 for update;

EMPNO ENAME      JOB         MGR HIREDATE          SAL      COMM DEPTNO

----- ---------- --------- ----- ----------- --------- --------- ------

7369 SMITH      CLERK      7902 1980-12-17     800.00               20

此时,我们观察v$lock和v$locked_object视图,可以看到锁信息。

//事务信息视图

SQL> select addr,xidusn,xidslot,xidsqn from v$transaction;

ADDR         XIDUSN    XIDSLOT     XIDSQN

-------- ---------- ---------- ----------

377DB5D0          7         19        808

//锁对象信息

SQL> select xidusn,xidslot,xidsqn,object_id,session_id, oracle_username from v$locked_object;

XIDUSN    XIDSLOT     XIDSQN  OBJECT_ID SESSION_ID ORACLE_USERNAME

---------- ---------- ---------- ---------- ---------- ------------------------------

7         19        808      73181         36 SCOTT

//

SQL> select owner,object_name from dba_objects where object_id=73181;

OWNER                          OBJECT_NAME

------------------------------ ------------------------------------------------------------

SCOTT                          EMP

//

SQL> select addr, sid, type, id1,id2,lmode, request, block from v$lock where sid=36;

ADDR      SID TYPE        ID1        ID2      LMODE    REQUEST  BLOCK

-------- ---------- ---- ---------- ---------- ---------- ---------- ----------

37E808F0    36 AE          100          0          4          0    0

B7DE8A44   36 TM        73181          0          3          0   0

377DB5D0   36 TX       458771        808          6          0    0

从上面的情况看,默认情况下的for update语句,效果相当于启动了一个会话级别的事务,在对应的数据表(select所涉及的所有数据表)上加入一个数据表级共享锁(TM,lmode=3)。同时,在对应的数据行中加入独占锁(TX,lmode=6)。

根据我们以前的知识,如果此时有另一个会话视图获取对应数据行的独占权限(无论是用update/delete还是另一个for update),都会以block而告终。

SQL> select sid from v$mystat where rownum<2;

SID

----------

37

SQL> select * from emp where empno=7369 for update;

//系统blocking

此时系统中状态,切换到另一个用户下进行观察:

SQL> select addr, sid, type, id1,id2,lmode, request, block from v$lock where sid in (36,37);

ADDR   SID TYPE        ID1        ID2      LMODE    REQUEST      BLOCK

-------- ---------- ---- ---------- ---------- ---------- ---------- ----------

37E808F0         36 AE          100          0          4          0   0

37E80ED4         37 AE          100          0          4          0   0

37E80F48         37 TX       458771        808          0          6   0

B7DE8A44         37 TM        73181          0          3          0  0

B7DE8A44         36 TM        73181          0          3          0  0

377DB5D0         36 TX       458771        808          6          0  1

6 rows selected

SQL> select * from dba_waiters;

WAITING_SESSION HOLDING_SESSION LOCK_TYPE                  MODE_HELD                                MODE_REQUESTED                             LOCK_ID1  LOCK_ID2

--------------- --------------- -------------------------- ---------------------------------------- ---------------------------------------- ---------- ----------

37              36 Transaction                Exclusive                                Exclusive                                    458771        808

由此,我们可以获取到结论:for update子句的默认行为就是自动启动一个事务,借助事务的锁机制将数据进行锁定。

Of子句是配合for update语句使用的一个范围说明标记。从官方的语法结构看,后面可以跟一个或者多个数据列列表。这种语法场景常常使用在进行连接查询的select中,对其中一张数据表数据进行锁定。

SQL> select empno,ename,job,mgr,sal from emp,dept where emp.deptno=dept.deptno and empno=7369 for update of emp.empno;

EMPNO ENAME      JOB         MGR       SAL

----- ---------- --------- ----- ---------

7369 SMITH      CLERK      7902    800.00

SQL>  select addr, sid, type, id1,id2,lmode, request, block from v$lock where sid=36;

ADDR       SID TYPE        ID1        ID2      LMODE    REQUEST BLOCK

-------- ---------- ---- ---------- ---------- ---------- ---------- ----------

37E808F0         36 AE          100          0          4          0    0

B7E1C2E8         36 TM        73181          0          3         0    0

377DBC0C         36 TX        65566        747          6       0   0

上面的语句中,我们看到使用for update of指定数据列之后,锁定的范围限制在了所在的数据表。也就是说,当我们使用连接查询配合of子句的时候,可以实现有针对性的锁定。

同样在连接查询的时候,如果没有of子句,同样采用默认的模式,会如何呢?

SQL> select empno,ename,job,mgr,sal from emp,dept where emp.deptno=dept.deptno and empno=7369 for update;

EMPNO ENAME      JOB         MGR       SAL

----- ---------- --------- ----- ---------

7369 SMITH      CLERK      7902    800.00

SQL>  select addr, sid, type, id1,id2,lmode, request, block from v$lock where sid=36;

ADDR     SID TYPE        ID1        ID2      LMODE    REQUEST  BLOCK

-------- ---------- ---- ---------- ---------- ---------- ---------- ----------

37E808F0         36 AE          100          0          4          0     0

B7E1C2E8         36 TM        73179          0          3          0   0

B7E1C2E8         36 TM        73181          0          3          0     0

377DBC0C         36 TX       458777        805          6          0    0

SQL> select owner,object_name from dba_objects where object_id=73179;

OWNER                          OBJECT_NAME

------------------------------ --------------------------------------------------------------------------------

SCOTT                          DEPT

明显可以看到,当我们没有使用of子句的时候,默认就是对所有select的数据表进行lock操作。

加锁行为子句

加锁行为子句相对比较容易理解。这里分别介绍。

Nowait子句

当我们进行for update的操作时,与普通select存在很大不同。一般select是不需要考虑数据是否被锁定,最多根据多版本一致读的特性读取之前的版本。加入for update之后,Oracle就要求启动一个新事务,尝试对数据进行加锁。如果当前已经被加锁,默认的行为必然是block等待。

使用nowait子句的作用就是避免进行等待,当发现请求加锁资源被锁定未释放的时候,直接报错返回。

///session1中

SQL> select * from emp for update;

EMPNO ENAME      JOB         MGR HIREDATE          SAL      COMM DEPTNO

----- ---------- --------- ----- ----------- --------- --------- ------

7369 SMITH      CLERK      7902 1980-12-17     800.00               20

7499 ALLEN      SALESMAN   7698 1981-2-20     1600.00    300.00     30

7521 WARD       SALESMAN   7698 1981-2-22     1250.00    500.00     30

7566 JONES      MANAGER    7839 1981-4-2      2975.00               20

//变换session,进行执行。

SQL> select * from emp for update nowait;

select * from emp for update nowait

ORA-00054: 资源正忙, 但指定以 NOWAIT 方式获取资源, 或者超时失效

对应的还有就是wait子句,也就是默认的for update行为。一旦发现对应资源被锁定,就等待blocking,直到资源被释放或者用户强制终止命令。

对wait子句还存在一个数据参数位,表示当出现blocking等待的时候最多等待多长时间。单位是秒级别。

//接上面的案例

SQL> select * from emp for update wait 3;

select * from emp for update wait 3

ORA-30006: 资源已被占用; 执行操作时出现 WAIT 超时

Skip locked参数

Skip locked参数是最新引入到for update语句中的一个参数。简单的说,就是在对数据行进行加锁操作时,如果发现数据行被锁定,就跳过处理。这样for update就只针对未加锁的数据行进行处理加锁。

//session1中,对一部分数据加锁;

SQL> select * from emp where rownum<4 for update;

EMPNO ENAME      JOB         MGR HIREDATE          SAL      COMM DEPTNO

----- ---------- --------- ----- ----------- --------- --------- ------

7369 SMITH      CLERK      7902 1980-12-17     800.00               20

7499 ALLEN      SALESMAN   7698 1981-2-20     1600.00    300.00     30

7521 WARD       SALESMAN   7698 1981-2-22     1250.00    500.00     30

//在session2中;

SQL> select * from emp for update skip locked;

EMPNO ENAME      JOB         MGR HIREDATE          SAL      COMM DEPTNO

----- ---------- --------- ----- ----------- --------- --------- ------

(篇幅原因,省略)

7934 MILLER     CLERK      7782 1982-1-23     1300.00               10

11 rows selected

总数据一共14行。Session1中,先lock住了3行数据。之后的seesion2中,由于使用的skip locked子句参数,将剩下的11条数据进行读取到并且加锁。

对for update的使用

在日常中,我们对for update的使用还是比较普遍的,特别是在如pl/sql developer中手工修改数据。此时只是觉得方便,而对for update真正的含义缺乏理解。

For update是Oracle提供的手工提高锁级别和范围的特例语句。Oracle的锁机制是目前各类型数据库锁机制中比较优秀的。所以,Oracle认为一般不需要用户和应用直接进行锁的控制和提升。甚至认为死锁这类锁相关问题的出现场景,大都与手工提升锁有关。所以,Oracle并不推荐使用for update作为日常开发使用。而且,在平时开发和运维中,使用了for update却忘记提交,会引起很多锁表故障。

那么,什么时候需要使用for update?就是那些需要业务层面数据独占时,可以考虑使用for update。场景上,比如火车票订票,在屏幕上显示邮票,而真正进行出票时,需要重新确定一下这个数据没有被其他客户端修改。所以,在这个确认过程中,可以使用for update。这是统一的解决方案方案问题,需要前期有所准备。

http://blog.itpub.net/17203031/viewspace-694383/

Select For update语句浅析 (转)的更多相关文章

  1. Select For update语句浅析

    Select -forupdate语句是我们经常使用手工加锁语句.通常情况下,select语句是不会对数据加锁,妨碍影响其他的DML和DDL操作.同时,在多版本一致读机制的支持下,select语句也不 ...

  2. 数据库中Select For update语句的解析

    ----------- Oracle -----------------– Oracle 的for update行锁 键字: oracle 的for update行锁 SELECT-FOR UPDAT ...

  3. sql server中同时执行select和update语句死锁问题

    原始出处 http://oecpby.blog.51cto.com/2203338/457054 最近在项目中使用SqlServer的时候发现在高并发情况下,频繁更新和频繁查询引发死锁.通常我们知道如 ...

  4. mysql SELECT FOR UPDATE语句使用示例

    以MySQL 的InnoDB 为例,预设的Tansaction isolation level 为REPEATABLE READ,在SELECT 的读取锁定主要分为两种方式:SELECT ... LO ...

  5. sql server中高并发情况下 同时执行select和update语句死锁问题 (二)

    SQL Server死锁使我们经常遇到的问题,数据库操作的死锁是不可避免的,本文并不打算讨论死锁如何产生,重点在于解决死锁.希望对您学习SQL Server死锁方面能有所帮助. 死锁对于DBA或是数据 ...

  6. Mysql查询语句使用select.. for update导致的数据库死锁分析

    近期有一个业务需求,多台机器需要同时从Mysql一个表里查询数据并做后续业务逻辑,为了防止多台机器同时拿到一样的数据,每台机器需要在获取时锁住获取数据的数据段,保证多台机器不拿到相同的数据. 我们My ...

  7. select for update行锁

     select for update行锁 2008-05-26 15:15:37 分类: Oracle Select-For Update语句的语法与select语句相同,只是在select语句的后面 ...

  8. Select for update/lock in share mode 对事务并发性影响

    select for update/lock in share mode 对事务并发性影响 事务并发性理解 事务并发性,粗略的理解就是单位时间内能够执行的事务数量,常见的单位是 TPS( transa ...

  9. MySQL 使用SELECT ... FOR UPDATE 做事务写入前的确认(转)

    Select…For Update语句的语法与select语句相同,只是在select语句的后面加FOR UPDATE [NOWAIT]子句. 该语句用来锁定特定的行(如果有where子句,就是满足w ...

随机推荐

  1. java读取xml文件

    public ArrayList getMessage(){ String xmlFileName = null; List list = new ArrayList(); MessageBean m ...

  2. 通过hexo+NexT构建静态博客

    一般的教程网上有很多,主要讲下我遇到的问题以及解决方法: 一.hexo建立的文档无法上传github deploy: type: git repository: https://github.com/ ...

  3. SQL21日自学通笔记(1)

    查找不重复数据 DISTINCT 查询日期 在access中格式是yyyy-mm-dd,Query执行的语句中用‘#’+Formatdata(‘yyyy-mm-dd’,date)+‘#’ SQL运算符 ...

  4. 玩转 H5 下拉上滑动效

    按照上面的技术方案实施,具体过程为: 禁用页面顶部下拉事件  ------>  将页面的主体内容用一个DIV容器包含起来,同时复制需要放大处理的内容节点至主体内容之外  ------> 绑 ...

  5. 百钱买百鸡问题 php版本

    /* * 百钱买百鸡问题 * * 我国古代数学家张丘建在<算经>一书中曾提出过著名的“百钱买百鸡”问题,该问题叙述如下:鸡翁一,值钱五:鸡母一,值钱三:鸡雏三,值钱一:百钱买百鸡,则翁.母 ...

  6. springMVC、httpClient调用别人提供的接口!!!(外加定时调用)

    import com.ibm.db.util.AppConfig; import com.ibm.db.util.JacksonUitl; import org.apache.http.HttpEnt ...

  7. web自动化测试中绕开验证码登陆的方式

    web自动化测试中登陆需验证码是很大的一个困扰.现推荐一种简单的避开验证码登陆的方式,先代码进入登录页,人工输入验证码登录后浏览器自动保存cookie,再在新的标签中登录. 具体代码如下: publi ...

  8. linux建立文件夹软连接

    linux建立文件夹软连接,并强制覆盖 ln -sfn /home/var/log/httpd/logs logs 这将在当前目录下建立logs软连接,指向/home/var/log/httpd/lo ...

  9. undefined reference to `libiconv_open 无法编译PHP

    解决方法:#wget http://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.13.1.tar.gz#tar -zxvf libiconv-1.13.1.tar. ...

  10. 1JavaScript简介

    文档对象模型(DOM,Document Object Model)是针对XML但经过扩展用于HTML的应用程序编程接口(API,Application Programming Interface). ...