欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

SQL Server 阻塞、死锁和最大并行度

程序员文章站 2024-03-22 12:12:52
...

1.阻塞

阻塞:是指当一个数据库会话中的事务,正在锁定其他会话事务想要读取或修改的资源,造成这些会话发出的请求进入等待的状态。SQL Server 默认会让被阻塞的请求无限期地一直等待,直到原来的事务释放相关的锁,或直到它超时 (根据 SET LOCK_TIMEOUT参数)、进程被杀死、服务器关闭。一般系统中,偶尔有短时间的阻塞是正常且合理的;但若设计不良的程序,就可能导致长时间的阻塞,这样就不必要地锁定了资源,而且阻塞了其他会话欲读取或更新的需求,系统表现为很卡。

项目中平时遇到的大多数是阻塞问题,比如系统页面报超时错误、SSMS中执行一条原本很快的SQL却卡住执行不了或需要执行很久、甚至整个数据库处于瘫痪卡死状态(严重情况)等。

阻塞的原因有很多,这里简单罗列下:

1.并发执行大量表扫描的SQL,由于缺失索引,造成大量阻塞,这种现象非常常见,例如网站首页中某个模块加载的SQL中缺失索引,导致网站首页访问超时。

2.隔离级别的问题,SQL Server默认隔离级别为READ COMMITTED,级别越高,并发性越差,造成阻塞的几率也越高。

3.长时间运行的SQL或者存储过程,由于执行效率较低或内容较多,事务期间会阻塞住其他会话执行

SQL Server 阻塞、死锁和最大并行度
 

2.死锁

死锁:是指多个事务会话互相等待对方释放持有的锁而造成的环路,如果没有外部干预,死锁将无限期持续下去。Sqlserver 中默认产生死锁时,会根据事务开销选择终止并回滚其中某一个事务,让其他事务获取所需的锁完成事务提交。如果系统出现大量死锁,会导致用户使用过程中出现大量死锁报错页面,事务数据无法正常提交,影响系统使用。

 

SQL Server 阻塞、死锁和最大并行度

 

死锁发生的现象往往是很明显的,页面会出现报错,如下图

 

SQL Server 阻塞、死锁和最大并行度

3.阻塞与死锁区别

阻塞现象:系统访问超时、卡慢 
死锁现象:系统访问报错

死锁其实也是由于阻塞造成的,会话之间先是阻塞,然后又因为产生交叉访问,造成环路,最终死锁。死锁的发生其实和阻塞是类似的,只不过阻塞是一种串行的等待,而死锁是交叉环路。

等待方式:默认情况下,当数据库出现阻塞时,如果没有人工干预,那么该会话会一直阻塞下去,直到阻塞该会话的事务结束,或直到LOCK_TIMEOUT设置的超时等待时间(默认为0即无限); 而当出现死锁时,数据库会立即根据事务的回滚开销,来选择终止(回滚)其中一个事务,进而解除死锁,所以死锁会造成操作无效。

4.排查维护

之前在公司平台发布栏目里发布了SQLServer 性能排查指南,可以下载参阅,下面简单说下操作。

4.1 阻塞处理

当系统突然发生了超时或卡顿等情况,可以先使用下面的脚本查看数据库阻塞情况,正常情况下没有结果或者出现少量短时间阻塞,问题发生时,通常会出现大量长时间阻塞,可以根据显示的BlockingSession_TSQL进行查看是SQL性能问题还是程序问题,必要时可以选择kill掉阻塞的会话来解决。

 SELECT  R.session_id AS WaitingSessionID ,  
            S.session_id AS BlockingSessionID ,  
            Q1.text AS WaitingSession_TSQL ,  
            Q2.text AS BlockingSession_TSQL ,  
            A.wait_duration_ms as WAIT_DURATION_MS ,
            S.original_login_name AS BlockingSession_LoginName ,  
            S.program_name AS BlockingSession_ApplicationName ,  
            S.host_name AS BlockingSession_HostName 
    FROM    sys.dm_exec_requests AS R  
            INNER JOIN sys.dm_exec_sessions AS S ON R.blocking_session_id = S.session_id  
            INNER JOIN sys.dm_exec_connections AS C1 ON R.session_id = C1.most_recent_session_id  
            INNER JOIN sys.dm_exec_connections AS C2 ON S.session_id = C2.most_recent_session_id  
            INNER JOIN sys.dm_os_waiting_tasks AS A ON A.session_id = C1.most_recent_session_id  
            CROSS APPLY sys.dm_exec_sql_text(C1.most_recent_sql_handle) AS Q1  
            CROSS APPLY sys.dm_exec_sql_text(C2.most_recent_sql_handle) AS Q2  

示例:

 

SQL Server 阻塞、死锁和最大并行度

4.2 死锁处理

当系统中出现死锁时,可以使用SQl Profile进行死锁监控(具体方法参考SQLServer 性能排查指南),捕捉到死锁后进行分析,看是否是由于SQL缺失索引导致。

 

SQL Server 阻塞、死锁和最大并行度

5.最大并行度

SQL Server 通过设置最大并行度来限制并行计划执行时所用的处理器数,默认为0,即不限制,由SQL Server自己控制并行。

并行开销阈值:指定SQLServer创建和运行并行查询计划的阈值,仅当运行同一查询的串行计划的估计开销高于在“并行的开销阈值”中设置的值时,SQLServer才创建和运行该查询的并行计划。开销指的是在特定硬件配置中运行串行计划估计需要花费的时间(秒)。

 

SQL Server 阻塞、死锁和最大并行度

我们的业务系统一般都是为OLTP,理想情况下事务很短,这时候其实就没必要通过并行来提高运行速度,所以对于这类系统来说,可以把最大并行度设为1,也就是不适用并行操作,从而减少不必要的资源等待。

如果是OLAP,由于事务普遍较长,所以并行操作往往能提高速度和资源利用率。

个人观点:对于最大并行度,项目上出现过几次该问题,导致系统访问超时,修改为1也就是关闭最大并行度后,问题解决。对于我们公司的系统来说,大部分事务都很短,不需要并行,小部分较长的事务可以改写SQL来使用并行; 另外也可以调整并行阈值,同时设置并行度为0或是固定值;

SELECT * FROM TEST  WHERE OBJECT_ID =1 OPTION (MAXDOP 8) 

没有最好的方法,只有最适合的方法