Oracle 共享锁和排它锁、 DML和DDL锁、 for update 锁表的问题

共享锁和排它锁

oracle有两种模式的锁:排他锁(exclusive lock,即X锁)和共享锁(share lock,即S锁)。 
共享锁:如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁,不能加排他锁。获准共享锁的事务只能读数据,不能修改数据。(注:共享锁是表级的,比如Select会对表加共享锁) 
排他锁:如果事务T对数据A加上排他锁后,则其他事务不能再对A加任任何类型的锁。获准排他锁的事务既能读数据,又能修改数据。(注:排他锁一般是行级的,比如DML操作 insert update delete,在执行DML操作时分两步加锁,先加共享锁,后加排他锁。在添加排他锁后,不能添加任何锁直至锁释放 commit或者rollback) 
数据库利用这两种基本的锁类型来对数据库的事务进行并发控制。 
共享锁无法阻止其他用户读取和修改表中的数据,只能阻止其他用户使用ALTER TABLE命令改变指定表的结构或用DROP TABLE命令删除指定表。多个用户可以同时获得相同数据上的共享锁。 
执行DML语句时可以获得排他锁,并且正在被修改的所有记录都会获得排他锁。在你启动的事务内,排他锁阻止其他用户获得正在处理的数据上的排他锁,直至执行COMMIT或ROLLBACK语句。这样,两个用户就无法同时更新相同的数据。某个用户试图更新被另一个用户锁定的数据时,第一个用户必须等待至去除排他锁后才能进行操作。 
通过DML语句对一张表的某一行数据进行修改,一个事务开始,背后的步骤是: 
1.对这张表加一个共享锁。这么做是为了防止别的会话通过DDL语句修改这张表的表结构。DDL语句要修改了这张表,就必须给表加上排他锁。但是现在给表加了共享锁了,也就排斥了DDL去加排他锁; 
2.对修改的那一行加一个排他锁,别的会话不能修改这一行。但是我对整张表加的是共享锁而不是排他锁,所以别的会话还是可以修改其他行(也经历1、2两个步骤)。

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

注意:只有表级的共享锁没有行级共享锁。的确,也是有row share的锁的,只是这个不是代表行的,是代表表锁的种类。指的是在行上加排他锁,在表上加共享锁,表级共享、行级排他。
-------------------------------------------------------------
mysql锁机制分为表级锁和行级锁,本文就和大家分享一下我对mysql中行级锁中的共享锁与排他锁进行分享交流。

共享锁又称为读锁,简称S锁,顾名思义,共享锁就是多个事务对于同一数据可以共享一把锁,都能访问到数据,但是只能读不能修改。

排他锁又称为写锁,简称X锁,顾名思义,排他锁就是不能与其他所并存,如一个事务获取了一个数据行的排他锁,其他事务就不能再获取该行的其他锁,包括共享锁和排他锁,但是获取排他锁的事务是可以对数据就行读取和修改。

对于共享锁大家可能很好理解,就是多个事务只能读数据不能改数据,对于排他锁大家的理解可能就有些差别,我当初就犯了一个错误,以为排他锁锁住一行数据后,其他事务就不能读取和修改该行数据,其实不是这样的。排他锁指的是一个事务在一行数据加上排他锁后,其他事务不能再在其上加其他的锁。mysql InnoDB引擎默认的修改数据语句,update,delete,insert都会自动给涉及到的数据加上排他锁,select语句默认不会加任何锁类型,如果加排他锁可以使用select ...for update语句,加共享锁可以使用select ... lock in share mode语句。所以加过排他锁的数据行在其他事务种是不能修改数据的,也不能通过for update和lock in share mode锁的方式查询数据,但可以直接通过select ...from...查询数据,因为普通查询没有任何锁机制。
----------------------------------------------

在 MySQL 中的行级锁、表级锁和页级锁中,咱们介绍过,行级锁是 MySQL 中锁定粒度最细的一种锁,行级锁能大大减少数据库操作的冲突。行级锁分为共享锁和排他锁两种,本文将详细介绍共享锁和排他锁的概念、使用方式及注意事项。

共享锁(Share Lock)
共享锁又称读锁,是读取操作创建的锁。其他用户可以并发读取数据,但任何事务都不能对数据进行修改(获取数据上的排他锁),直到已释放所有共享锁。

如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁,不能加排他锁。获准共享锁的事务只能读数据,不能修改数据。

用法

SELECT ... LOCK IN SHARE MODE;

在查询语句后面增加LOCK IN SHARE MODE,MySQL 就会对查询结果中的每行都加共享锁,当没有其他线程对查询结果集中的任何一行使用排他锁时,可以成功申请共享锁,否则会被阻塞。其他线程也可以读取使用了共享锁的表,而且这些线程读取的是同一个版本的数据。

排他锁(Exclusive Lock)
排他锁又称写锁、独占锁,如果事务T对数据A加上排他锁后,则其他事务不能再对A加任何类型的封锁。获准排他锁的事务既能读数据,又能修改数据。

用法

SELECT ... FOR UPDATE;

在查询语句后面增加FOR UPDATE,MySQL 就会对查询结果中的每行都加排他锁,当没有其他线程对查询结果集中的任何一行使用排他锁时,可以成功申请排他锁,否则会被阻塞。
------------------------------------------------------------
共享锁(S锁):共享 (S) 用于不更改或不更新数据的操作(只读操作),如 SELECT 语句。

如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁,不能加排他锁。获准共享锁的事务只能读数据,不能修改数据。

排他锁(X锁):用于数据修改操作,例如 INSERT、UPDATE 或 DELETE。确保不会同时同一资源进行多重更新。

如果事务T对数据A加上排他锁后,则其他事务不能再对A加任任何类型的封锁。获准排他锁的事务既能读数据,又能修改数据。

简要说明为什么会发生死锁?解决死锁的主要方法是什么? 
若干事务相互等待释放封锁,就陷入无限期等待状态,系统就进入死锁

解决死锁的方法应从预防和解除的两个方面着手: 
(1)死锁的预防方法: 
a、要求每一个事务必须一次封锁所要使用的全部数据(要么全成功,要么全不成功) 
b、规定封锁数据的顺序,所有事务必须按这个顺序实行封锁。 
(2)允许死锁发生,然后解除它,如果发现死锁,则将其中一个代价较小的事物撤消,回滚这个事务,并释放此事务持有的封锁,使其他事务继续运行。

oracle:

共享锁

LOCK TABLE 表 IN SHARE MODE ;

排他锁:

LOCK TABLE 表 IN EXCLUSIVE MODE ;

加锁后其它人不可操作,直到加锁用户解锁,用commit或rollback解锁

行排他锁不阻止其他Session申请表共享锁和其他行的排他锁,但阻止申请表排他锁和锁定行的任何锁。 
表排他锁阻止其他Session的申请的所有锁。 
表共享锁不阻止其他Session申请行排他锁和表共享锁,但阻止申请表排他锁。

一道相关的试题:

User SCOTT executes the following command on the EMP table but has not issued COMMIT,ROLLBACK, or any data definition language (DDL)ommand: 
SQL> SELECT job FROM emp WHERE job=’CLERK’ FOR UPDATE OF empno; 
SCOTT has opened another session to work with the database. Which three operations would wait when issued in SCOTT’s second session?(Choose three.) 
A. LOCK TABLE emp IN SHARE MODE;(阻止申请行的共享锁) 
B. LOCK TABLE emp IN EXCLUSIVE MODE;(阻止申请表的排他锁) 
C. DELETE FROM emp WHERE job=’MANAGER’; 
D. INSERT INTO emp(empno,ename) VALUES(1289,’Dick’); 
E. SELECT job FROM emp WHERE job=’CLERK’ FOR UPDATE OF empno;(阻止申请行的排他锁)

SELECT …… FROM …… FOR UPDATE 语句申请的是相应行的排他锁以及行所在表的共享锁。

上题答案给的是A、B、E
--------------------------------------------------------
共享锁:(读取)操作创建的锁。其他用户可以并发读取数据,但任何事物都不能获取数据上的排它锁,直到已释放所有共享锁。
共享锁(S锁)又称为读锁,若事务T对数据对象A加上S锁,则事务T只能读A;其他事务只能再对A加S锁,而不能加X锁,直到T释放A上的S锁。这就保证了其他事务可以读A,但在T释放A上的S锁之前不能对A做任何修改。
 排它锁
排它锁:排它锁又称为写锁((eXclusive lock,简记为X锁)),若事物T对数据对象A加上X锁,则只允许T读取和修改A,其它任何事务都不能再对A加任何类型的锁,直到T释放A上的锁。它防止任何其它事务获取资源上的锁,直到在事务的末尾将资源上的原始锁释放为止。
独占锁和共享锁的区别
1.共享锁(S锁):如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁,不能加排它锁。获准共享锁的事务只能读数据,不能修改数据。
排他锁(X锁):如果事务T对数据A加上排他锁后,则其他事务不能再对A加任任何类型的封锁。获准排他锁的事务既能读数据,又能修改数据。
2.共享锁下其它用户可以并发读取,查询数据。但不能修改,增加,删除数据。资源共享.
 
共享锁的使用
在第一个连接中执行以下语句
begin tran
select * from table1 holdlock -holdlock人为加锁
where B='b2'
waitfor delay '00:00:30' --等待30秒
commit tran
在第二个连接中执行以下语句
begin tran
select A,C from table1
where B='b2'
update table1
set A='aa'
where B='b2'
commit tran
若同时执行上述两个语句,则第二个连接中的select查询可以执行
而update必须等待第一个事务释放共享锁转为排它锁后才能执行 即要等待30秒
----------------------------------------------------------------------

DML和DDL锁

DML锁,也叫做数据锁(data lock),用于保证在多用户操作数据时数据的完整。DML锁防止相互冲突的DML和DDL操作同时发生。
DML锁有行锁(Row Locks,TX)和表锁(Table Locks,TM),不同的DML操作会自动请求对应的锁。

行锁(Row Locks,TX)
行锁也叫TX锁,用于锁表的一行数据。当一个事务对一行数据做INSERT、UPDATE、DELETE、MERGE或SELECT ... FOR UPDATE操作时,数据将为行添加行锁,直到事务执行了commit或roll back操作后,行锁才释放。
行锁防止两个事务修改同一行数据,当一个事务修改一行数据时,数据库总是为修改的行加一个排它锁以至于其它事务无法修改该行,只有当事务执行了commit或者roll back操作后,数据库才会释放对应的锁。行锁是小粒度的锁,为应用提供了最大限度的并行修改数据的能力。
当一个事务获取了一个行锁,那么这个事务也需要获取这行数据所在表的表锁,表锁阻止有冲突的DDL操作,即数据库会自动的为更新的行添加一个排它锁,并为行所在的表添加一个子排它锁。
行锁和并发
下面通过一个例子来理解行锁和并发的关系。
首先创建下面的表格,并初始化数据:
create table employees(employee_id number(10),salary number(10));
insert into employees(employee_id,salary) values(100,512);
insert into employees(employee_id,salary) values(101,600);
......
步骤一:三个Session同时查询ID为100和101的雇员,查询结果一致
Session 1:
SELECT employee_id, salary FROM employees WHERE employee_id IN (100, 101);
EMPLOYEE_ID		SALARY
-------------------------
100				512
101				600

Session 2:
SELECT employee_id, salary FROM employees WHERE employee_id IN (100, 101);
EMPLOYEE_ID		SALARY
-------------------------
100				512
101				600

Session 3:
SELECT employee_id, salary FROM employees WHERE employee_id IN (100, 101);
EMPLOYEE_ID		SALARY
-------------------------
100				512
101				600
步骤二:Session 1执行更新操作,更新id为100的雇员,在这个更新中,写者将请求一个行锁,阻止其它写者更新这行数据,如果其它写者更新该行数据将被阻塞,直到Session 1提交或者回滚数据
Session 1:
update employees set salary = 612 where employee_id = 100
步骤三:再次执行步骤一的操作
Session 1:
SELECT employee_id, salary FROM employees WHERE employee_id IN (100, 101);
EMPLOYEE_ID		SALARY
-------------------------
100				612
101				600

Session 2:
SELECT employee_id, salary FROM employees WHERE employee_id IN (100, 101);
EMPLOYEE_ID		SALARY
-------------------------
100				512
101				600

Session 3:
SELECT employee_id, salary FROM employees WHERE employee_id IN (100, 101);
EMPLOYEE_ID		SALARY
-------------------------
100				512
101				600
Session 1的结果是它更新后的数据,而其他两个session任然是旧数据。
步骤四:Session 2更新101雇员的薪水,并且不提交数据,这样Session 2获取了对雇员101的行锁
UPDATE hr.employees SET salary = salary + 100 WHERE employee_id = 101;
步骤五:再次执行步骤1的查询
Session 1:
SELECT employee_id, salary FROM employees WHERE employee_id IN (100, 101);
EMPLOYEE_ID		SALARY
-------------------------
100				612
101				600

Session 2:
SELECT employee_id, salary FROM employees WHERE employee_id IN (100, 101);
EMPLOYEE_ID		SALARY
-------------------------
100				512
101				700

Session 3:
SELECT employee_id, salary FROM employees WHERE employee_id IN (100, 101);
EMPLOYEE_ID		SALARY
-------------------------
100				512
101				600
行锁的存储
Oracle将锁信息存储在data block中。数据库用一个队列机制处理行锁请求,如果一个事务请求一个未锁定的行,那么事务将放一个锁到data block,被事务修改的每一行都指向存储在block header中的事务ID的一个拷贝。
当一个事务结束时,事务ID保留在block header中,如果另一个事务想修改一行数据,那么它用这个事务ID判定这个锁是否是激活的。如果锁是激活的,那么当锁被释放时,该事务的session将被通知,否则,事务获取这个锁。
表锁(Table Locks,TM)
表锁,也叫TM锁,当对表执行以下操作时将被请求:INSERT、UPDATE、DELETE、MERGE、SELECT ... FOR UPDATE和LOCK TABLE。请求表锁的DML操作将阻止其它冲突的DDL操作。
表锁有以下的模式:
Row Share(RS)
该锁也叫subshare table lock(SS),表示事务持有表上的锁已锁定表中的行,并打算对其进行更新。Row share锁是最小限制的表锁,为表的行数据的高并发修改提供了支持。
Row Exclusive Table Lock(RX)
该所也叫subexclusive table lock(SX),通常表示持有锁的事务更新了表的行或者执行了SELECT ... FOR UPDATE。SX锁允许其它事务查询、插入、更新、删除或者在同一个表上锁定多行数据,因此,SX锁允许多个事务在同一个表上同时获取SX和RS锁。
Share Table Lock(S)
一个事务持有了表的S锁,任然允许其他事务查询该表格(除了用SELECT ... FOR UPDATE),但只有持有了S锁的事务被允许更新表格。由于多个事务可以同时持有S锁,获取S锁并不能保证事务能够修改表格。
Share Row Exclusive Table Lock(SRX)
该锁也叫share-subexclusive table lock(SSX),比S锁限制更强。在一个表上一个时间点只能有一个事务能获取SSX锁。SSX锁允许其它事务查询表(除了用SELECT ... FOR UPDATE),但是不能更新表。
Exclusive Table Lock(X)
这个锁限制最强,禁止其它事务执行任何类型的DML操作或对表防止任何锁。
--------------------- 
DML锁
DML锁用于确保一次只有一个人能修改一行,而且这时别人不能删除这个表。
1.TX锁
A row lock, also called a TX lock, is alock on a single row of table. A transaction acquires a row lock for each rowmodified by an INSERT, UPDATE, DELETE,MERGE, or SELECT ... FOR UPDATEstatement. The row lock exists until the transaction commits or rolls back.

一个行锁,也被称为TX lock,是对于表中一行的一个锁。当对行进行INSERT, UPDATE, DELETE,MERGE, or SELECT ... FOR UPDATE等操作时事务需要获取一个TX锁。这个锁会持续到事务提交或者rollback。

If a transaction obtains a lock for a row,then the transaction also acquires a lock for the table containing the row. Thetable lock prevents conflicting DDL operations that would override data changesin a current transaction.

可以防止DDL操作覆盖当前事务的数据。

2.TM锁
TM锁用于确保在修改表的内容时,表的结构不会被改变。

A table lock, also called a TM lock, isacquired by a transaction when a table is modified by an INSERT, UPDATE,DELETE, MERGE, SELECT with the FOR UPDATE clause, or LOCK TABLE statement. DMLoperations require table locks to reserve DML access to the table on behalf ofa transaction and to prevent DDL operations that would conflict with thetransaction.

一个表锁,也被称为TM锁,被用于一个事务中当表被通过INSERT, UPDATE, DELETE, MERGE, SELECT with theFOR UPDATE clause修改或者进行了LOCK TABLE操作。被用来防止DDL操作导致事务冲突影响了自身的事务。

DDL锁
在DDL操作中会自动为对象加DDL锁,从而保护这些对象不会被其他会话所修改。在DDL语句执行期间会一直持有DDL锁,一旦操作执行完毕就会立即释放。

排他DDL锁(exclusive DDL lock):这会防止其他会话得到他们自己的DDL锁或TM锁。这说明,在DDL操作期间可以查询一个表,但是无法以任何方式修改这个表。
共享DDL锁(share DDL lock):这些锁会保护所引用的对象的结构,使之不会被其他会话修改,但是允许修改数据。
可中断解析锁(breakable parse lock):这些锁允许一个对象向其他对象注册其依赖性。???

--参考来源《Oracle编程艺术深入理解数据库体系结构(第三版)》
-------------------------------------------------------------

 for update 锁表的问题

数据库是一个多用户使用的共享资源。当多个用户并发地存取数据时,在数据库中就会产生多个事务同时存取同一数据的情况。若对并发操作不加控制就可能会读取和存储不正确的数据,破坏数据库的一致性。 
加锁是实现数据库并发控制的一个非常重要的技术。当事务在对某个数据对象进行操作前,先向系统发出请求,对其加锁。加锁后事务就对该数据对象有了一定的控制,在该事务释放锁之前,其他的事务不能对此数据对象进行更新操作。 
在数据库中有两种基本的锁类型:排它锁(Exclusive Locks,即X锁)和共享锁(Share Locks,即S锁)。当数据对象被加上排它锁时,其他的事务不能对它读取和修改。加了共享锁的数据对象可以被其他事务读取,但不能修改。数据库利用这两种基本的锁类型来对数据库的事务进行并发控制。 

Oracle数据库的锁类型 
根据保护的对象不同,Oracle数据库锁可以分为以下几大类:DML锁(data locks,数据锁),用于保护数据的完整性;DDL锁(dictionary locks,字典锁),用于保护数据库对象的结构,如表、索引等的结构定义;内部锁和闩(internal locks and latches),保护 数据库的内部结构。 
DML锁的目的在于保证并发情况下的数据完整性,。在Oracle数据库中,DML锁主要包括TM锁和TX锁,其中TM锁称为表级锁,TX锁称为事务锁或行级锁。

当Oracle执行DML语句时,系统自动在所要操作的表上申请TM类型的锁。当TM锁获得后,系统再自动申请TX类型的锁,并将实际锁定的数据行的锁标志位进行置位。这样在事务加锁前检查TX锁相容性时就不用再逐行检查锁标志,而只需检查TM锁模式的相容性即可,大大提高了系统的效率。TM锁包括了SS、SX、S、X 等多种模式,在数据库中用0-6来表示。不同的SQL操作产生不同类型的TM锁。

在数据行上只有X锁(排他锁)。在Oracle数据库中,当一个事务首次发起一个DML语句时就获得一个TX锁,该锁保持到事务被提交或回滚。当两个或多个会话在表的同一条记录上执行 DML语句时,第一个会话在该条记录上加锁,其他的会话处于等待状态。当第一个会话提交后,TX锁被释放,其他会话才可以加锁。

当Oracle数据库发生TX锁等待时,如果不及时处理常常会引起Oracle数据库挂起,或导致死锁的发生,产生ORA-60的错误。这些现象都会对实际应用产生极大的危害,如长时间未响应,大量事务失败等。 

悲观封锁和乐观封锁 
一、悲观封锁 
锁在用户修改之前就发挥作用: 
Select ..for update(nowait) 
Select * from tab1 for update 
用户发出这条命令之后,oracle将会对返回集中的数据建立行级封锁,以防止其他用户的修改。 
如果此时其他用户对上面返回结果集的数据进行dml或ddl操作都会返回一个错误信息或发生阻塞。 
1:对返回结果集进行update或delete操作会发生阻塞。 
2:对该表进行ddl操作将会报:Ora-00054:resource busy and acquire with nowait specified. 
原因分析 
此时Oracle已经对返回的结果集上加了排它的行级锁,所有其他对这些数据进行的修改或删除操作都必须等待这个锁的释放,产生的外在现象就是其他的操作将发生阻塞,这个这个操作commit或rollback. 
同样这个查询的事务将会对该表加表级锁,不允许对该表的任何ddl操作,否则将会报出ora-00054错误::resource busy and acquire with nowait specified. 

二、乐观封锁 
乐观的认为数据在select出来到update进取并提交的这段时间数据不会被更改。这里面有一种潜在的危险就是由于被选出的结果集并没有被锁定,是存在一种可能被其他用户更改的可能。因此Oracle仍然建议是用悲观封锁,因为这样会更安全。 

阻塞 
定义: 
当一个会话保持另一个会话正在请求的资源上的锁定时,就会发生阻塞。被阻塞的会话将一直挂起,直到持有锁的会话放弃锁定的资源为止。4个常见的dml语句会产生阻塞 
INSERT 
UPDATE 
DELETE 
SELECT…FOR UPDATE 

INSERT 
Insert发生阻塞的唯一情况就是用户拥有一个建有主键约束的表。当2个的会话同时试图向表中插入相同的数据时,其中的一个会话将被阻塞,直到另外一个会话提交或会滚。一个会话提交时,另一个会话将收到主键重复的错误。回滚时,被阻塞的会话将继续执行。 
UPDATE 和DELETE当执行Update和delete操作的数据行已经被另外的会话锁定时,将会发生阻塞,直到另一个会话提交或会滚。 
Select …for update 
当一个用户发出select..for update的错作准备对返回的结果集进行修改时,如果结果集已经被另一个会话锁定,就是发生阻塞。需要等另一个会话结束之后才可继续执行。可以通过发出 select… for update nowait的语句来避免发生阻塞,如果资源已经被另一个会话锁定,则会返回以下错误:Ora-00054:resource busy and acquire with nowait specified. 
死锁-deadlock 
定义:当两个用户希望持有对方的资源时就会发生死锁. 
即两个用户互相等待对方释放资源时,oracle认定为产生了死锁,在这种情况下,将以牺牲一个用户作为代价,另一个用户继续执行,牺牲的用户的事务将回滚. 
例子: 
1:用户1对A表进行Update,没有提交。 
2:用户2对B表进行Update,没有提交。 
此时双反不存在资源共享的问题。 
3:如果用户2此时对A表作update,则会发生阻塞,需要等到用户一的事物结束。 
4:如果此时用户1又对B表作update,则产生死锁。此时Oracle会选择其中一个用户进行会滚,使另一个用户继续执行操作。 
起因: 
Oracle的死锁问题实际上很少见,如果发生,基本上都是不正确的程序设计造成的,经过调整后,基本上都会避免死锁的发生。 

DML锁分类表 
表1Oracle的TM锁类型 
锁模式 锁描述 解释 SQL操作 
0 none 
1 NULL 空 Select 
2 SS(Row-S) 行级共享锁,其他对象 
只能查询这些数据行 Select for update、Lock for 
update、Lock row share 
3 SX(Row-X) 行级排它锁, 
在提交前不允许做DML操作 Insert、Update、 
Delete、Lock row share 
4 S(Share) 共享锁 Create index、Lock share 
5 SSX(S/Row-X) 共享行级排它锁 Lock share row exclusive 
6 X(Exclusive) 排它锁 Alter table、Drop able、Drop index、Truncate table 、Lock exclusive 
oracle 锁问题的解决 

可以用Spotlight软件对数据库的运行状态进行监控。 
当出现session锁时,我们要及时进行处理. 
1. 查看哪些session锁: 
SQL语句:select 'alter system kill session '''||sid||','||serial#||''';' from v$session where sid in (select sid from v$lock where block = 1); 
SQL> select 'alter system kill session '''||sid||','||serial#||''';' from v$session where sid in (select sid from v$lock where block = 1); 
'ALTERSYSTEMKILLSESSION'''||SID||','||SERIAL#||''';' 
-------------------------------------------------------------------------------- 
alter system kill session '132,731'; 
alter system kill session '275,15205'; 
alter system kill session '308,206'; 
alter system kill session '407,3510'; 
2. 查看session锁. 
sql语句:select s.sid, q.sql_text from v$sqltext q, v$session s 
where q.address = s.sql_address 
and s.sid = &sid 
order by piece; 
SQL> select s.sid,q.sql_text from v$sqltext q, v$session s where q.address = s.sql_address and s.sid in (select sid from v$lock where block = 1) order by piece; 
SID SQL_TEXT 
---------- ---------------------------------------------------------------- 
77 UPDATE PROFILE_USER SET ID=1,COMPANY_ID=2,CUSTOMER_ID=3,NAMED 
77 _INSURED_ID=4,LOGIN=5,ROLE_ID=6,PASSWORD=7,EMAIL=8,TIME_ZON 
77 E=9 WHERE PROFILE_USER.ID=:34 
3 rows selected. 

3. kill锁的进程. 
SQL语句:alter system kill session '77,22198'; 
SQL> alter system kill session '391,48398'; 
System altered. 










对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。这是统一的解决方案方案问题,需要前期有所准备

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

 

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

 

下面是采自Oracle官方文档《SQLLanguage 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     79021980-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             36Transaction               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     79021980-12-17    800.00              20

 7499 ALLEN     SALESMAN  76981981-2-20    1600.00   300.00    30

 7521 WARD      SALESMAN  76981981-2-22    1250.00   500.00    30

 7566 JONES     MANAGER   78391981-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     79021980-12-17    800.00              20

 7499 ALLEN     SALESMAN  76981981-2-20    1600.00   300.00    30

 7521 WARD      SALESMAN  76981981-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     77821982-1-23    1300.00              10

 

11 rows selected

 

 

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


键字: oracle 的for update行锁 
  SELECT...FOR UPDATE 语句的语法如下: 
  SELECT ... FOR UPDATE [OF column_list][WAIT n|NOWAIT][SKIP LOCKED]; 
其中: 
  OF 子句用于指定即将更新的列,即锁定行上的特定列。 
  WAIT 子句指定等待其他用户释放锁的秒数,防止无限期的等待。 
  “使用FOR UPDATE WAIT”子句的优点如下: 
  1防止无限期地等待被锁定的行; 
  2允许应用程序中对锁的等待时间进行更多的控制。 
  3对于交互式应用程序非常有用,因为这些用户不能等待不确定 
  4 若使用了skip locked,则可以越过锁定的行,不会报告由wait n 引发的‘资源忙’异常报告


示例: 
create table t(a varchar2(20),b varchar2(20)); 
insert into t values('1','1'); 
insert into t values('2','2'); 
insert into t values('3','3'); 
insert into t values('4','4'); 
现在执行如下操作: 
在plsql develope中打开两个sql窗口, 
在1窗口中运行sql 
select * from t where a='1' for update; 
在2窗口中运行sql1 
1. select * from t where a='1'; 这一点问题也没有,因为行级锁不会影响纯粹的select语句 
再运行sql2 
2. select * from t where a='1' for update; 则这一句sql在执行时,永远处于等待状态,除非窗口1中sql被提交或回滚。 
如何才能让sql2不等待或等待指定的时间呢? 我们再运行sql3 
3. select * from t where a='1' for update nowait; 则在执行此sql时,直接报资源忙的异常。 
若执行 select * from t where a='1' for update wait 6; 则在等待6秒后,报 资源忙的异常。 
如果我们执行sql4 
4. select * from t where a='1' for update nowait skip Locked; 则执行sql时,即不等待,也不报资源忙异常。 
现在我们看看执行如下操作将会发生什么呢? 
在窗口1中执行: 
select * from t where rownum<=3 nowait skip Locked; 
在窗口2中执行: 
select * from t where rownum<=6 nowait skip Locked; 
select for update 也就如此了吧,insert、update、delete操作默认加行级锁,其原理和操作与select for update并无两样。 
select for update of,这个of子句在牵连到多个表时,具有较大作用,如不使用of指定锁定的表的列,则所有表的相关行均被锁定,若在of中指定了需修改的列,则只有与这些列相关的表的行才会被锁定。




---------------------------------  MySQL  -----------------------------------------------------


MySQL中select * for update锁表的问题


页级:引擎 BDB。
表级:引擎 MyISAM , 理解为锁住整个表,可以同时读,写不行
行级:引擎 INNODB , 单独的一行记录加锁
表级,直接锁定整张表,在你锁定期间,其它进程无法对该表进行写操作。如果你是写锁,则其它进程则读也不允许
行级,,仅对指定的记录进行加锁,这样其它进程还是可以对同一个表中的其它记录进行操作。
页级,表级锁速度快,但冲突多,行级冲突少,但速度慢。所以取了折衷的页级,一次锁定相邻的一组记录。
MySQL 5.1支持对MyISAM和MEMORY表进行表级锁定,对BDB表进行页级锁定,对InnoDB表进行行级锁定。
对WRITE,MySQL使用的表锁定方法原理如下:
如果在表上没有锁,在它上面放一个写锁。
否则,把锁定请求放在写锁定队列中。
对READ,MySQL使用的锁定方法原理如下:
如果在表上没有写锁定,把一个读锁定放在它上面   
否则,把锁请求放在读锁定队列中。
InnoDB使用行锁定,BDB使用页锁定。对于这两种存储引擎,都可能存在死锁。这是因为,在SQL语句处理期间,InnoDB自动获得行锁定和BDB获得页锁定,而不是在事务启动时获得。 

MySQL中select * for update锁表的问题
由于InnoDB预设是Row-Level Lock,所以只有「明确」的指定主键,MySQL才会执行Row lock (只锁住被选取的资料例) ,否则MySQL将会执行Table Lock (将整个资料表单给锁住)。
举个例子:
假设有个表单products ,里面有id跟name二个栏位,id是主键。
例1: (明确指定主键,并且有此笔资料,row lock)
SELECT * FROM products WHERE id='3' FOR UPDATE;
SELECT * FROM products WHERE id='3' and type=1 FOR UPDATE;


例2: (明确指定主键,若查无此笔资料,无lock)
SELECT * FROM products WHERE id='-1' FOR UPDATE;


例2: (无主键,table lock)
SELECT * FROM products WHERE name='Mouse' FOR UPDATE;


例3: (主键不明确,table lock)
SELECT * FROM products WHERE id<>'3' FOR UPDATE;


例4: (主键不明确,table lock)
SELECT * FROM products WHERE id LIKE '3' FOR UPDATE;


注1: FOR UPDATE仅适用于InnoDB,且必须在交易区块(BEGIN/COMMIT)中才能生效。
注2: 要测试锁定的状况,可以利用MySQL的Command Mode ,开二个视窗来做测试。


在MySql 5.0中测试确实是这样的
另外:MyAsim 只支持表级锁,InnerDB支持行级锁
添加了(行级锁/表级锁)锁的数据不能被其它事务再锁定,也不被其它事务修改(修改、删除)
是表级锁时,不管是否查询到记录,都会锁定表
---------------------------------------------------------

猜你喜欢

转载自blog.csdn.net/Leon_Jinhai_Sun/article/details/86308390