今天发现一个死锁问题,通过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锁阻塞
上一个死锁图吧:
数据库会自动检测死锁,一旦发现,会根据优先级杀掉一个进程,蓝色叉叉就是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 TRANselect count(*) from fdp_edf9_stg.org_market_data;
对于平常update和select数据库就是用到了X和S , 即排他锁和共享锁,具体关系是:
1.共享锁只用于表级,排他锁用于行级。
2.加了共享锁的对象,可以继续加共享锁,不能再加排他锁。加了排他锁后,不能再加任何锁。
3.比如一个DML操作,就要对受影响的行加排他锁,这样就不允许再加别的锁,也就是说别的会话不能修改这些行。同时为了避免在做这个DML操作的时候,有别的会话执行DDL,修改表的定义,所以要在表上加共享锁,这样就阻止了DDL的操作。
4.当执行DDL操作时,就需要在全表上加排他锁。