关于SqlServer Schema 锁的问题

今天发现一个死锁问题,通过SqlServer Profiler检测,发现是Sch-M阻塞了Sch-S. 大家可以通过下面来模拟产生:

1. 新开一个窗口,取名为session1,执行如下SQL:

begin tran
truncate table fdp_edf9_stg.cdsi_edf;

2. 再开一个窗口执行,取名为session2

begin tran
truncate table fdp_edf9_stg.cds_hist;

 

然后分别在2个窗口执行:

select * from information_schema.constraint_column_usage

很快,你就发现2个窗口中的select 都执行不了了,然后就是死锁。 细心的同学可以发现其实两个表都不一样,为什么会锁住?  那是因为truncate产生了Sch-M锁,  这个锁会阻塞元数据表constraint_column_usage的读取,因为读取元数据需要获取Sch-S锁。 大家还可以通过下面的query查看所有锁的状态:

SELECT resource_type, request_mode, request_status,resource_description,request_session_id, DB_NAME(resource_database_id)as resource_database
FROM sys.dm_tran_locks
WHERE resource_type <> ‘DATABASE’

通过如下query查看当前SQL执行被谁给阻塞了

SELECT
db.name DBName,
tl.request_session_id,
wt.blocking_session_id,
–OBJECT_NAME(p.OBJECT_ID) BlockedObjectName,
tl.resource_type,
h1.TEXT AS RequestingText,
h2.TEXT AS BlockingTest,
tl.request_mode
FROM sys.dm_tran_locks AS tl
INNER JOIN sys.databases db ON db.database_id = tl.resource_database_id
INNER JOIN sys.dm_os_waiting_tasks AS wt ON tl.lock_owner_address = wt.resource_address
–INNER JOIN sys.partitions AS p ON p.hobt_id = tl.resource_associated_entity_id —
INNER JOIN sys.dm_exec_connections ec1 ON ec1.session_id = tl.request_session_id
INNER JOIN sys.dm_exec_connections ec2 ON ec2.session_id = wt.blocking_session_id
CROSS APPLY sys.dm_exec_sql_text(ec1.most_recent_sql_handle) AS h1
CROSS APPLY sys.dm_exec_sql_text(ec2.most_recent_sql_handle) AS h2

其中sys.partitions表注释掉是因为这个好像也会被Sch-M锁阻塞

上一个死锁图吧:

Image 6

 

数据库会自动检测死锁,一旦发现,会根据优先级杀掉一个进程,蓝色叉叉就是SQLServer自动干掉的进程。

 

Schema锁的介绍:

The Database Engine uses schema modification (Sch-M) locks during a table data definition language (DDL) operation, such as adding a column or dropping a table. During the time that it is held, the Sch-M lock prevents concurrent access to the table. This means the Sch-M lock blocks all outside operations until the lock is released.

Some data manipulation language (DML) operations, such as table truncation, use Sch-M locks to prevent access to affected tables by concurrent operations.

The Database Engine uses schema stability (Sch-S) locks when compiling and executing queries. Sch-S locks do not block any transactional locks, including exclusive (X) locks. Therefore, other transactions, including those with X locks on a table, continue to run while a query is being compiled. However, concurrent DDL operations, and concurrent DML operations that acquire Sch-M locks, cannot be performed on the table.

 

本文只讲了Schema锁,关于其他数据库锁,我们可以参看:https://technet.microsoft.com/en-us/library/ms175519(v=sql.105).aspx

这个博客对锁的研究很深,推荐看:http://aboutsqlserver.com/2012/04/05/locking-in-microsoft-sql-server-part-13-schema-locks/

大家有没有发现 truncate是放在一个事务里面,我们平常了解的不是truncate不能回滚吗?但是我通过如下例子,发现SqlServer的truncate是可以回滚的,Oracle是不行的:

begin tran
truncate table fdp_edf9_stg.org_market_data;
ROLLBACK TRAN

select count(*) from fdp_edf9_stg.org_market_data;

 

对于平常update和select数据库就是用到了X和S , 即排他锁和共享锁,具体关系是:

1.共享锁只用于表级,排他锁用于行级。
2.加了共享锁的对象,可以继续加共享锁,不能再加排他锁。加了排他锁后,不能再加任何锁。
3.比如一个DML操作,就要对受影响的行加排他锁,这样就不允许再加别的锁,也就是说别的会话不能修改这些行。同时为了避免在做这个DML操作的时候,有别的会话执行DDL,修改表的定义,所以要在表上加共享锁,这样就阻止了DDL的操作。
4.当执行DDL操作时,就需要在全表上加排他锁。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four 
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>