select查询造成的数据库死锁

本文描述了在SQL Server中遇到的死锁问题及其解决过程。通过对客户系统的观察和调试,发现死锁主要由特定的存储过程和查询语句引起。通过对SQL语句和存储过程的分析,最终确定了死锁的根本原因是由于对同一表的不同索引同时进行读写操作导致的资源冲突。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >



    最近给一个客户更新了一个模块,查询过程中老是出现查询结果不一致的情况,有时多有时少,通过调试发现sql语句都一样,返回的结果却不一样,跟踪SQL语句发现,在查询结果少的时候,会报 事务被作为牺牲品的死锁错误,正常情况下,如果报错会返回null值,为什么会出现一部分数据,难道是脏读?(关于这个脏读的问题到现在都没明白怎么回事。

   事情过了几天,情况也不是很严重,就一直没管他,后来一个老开发过来,在说到这一块问题的时候,他说可能是我排序的问题,我的数据排序是在程序中完成的,当时为了优化SQL性能,把sql的order by 语句都通过程序进行排序了,排序代码如下,

于是就把程序排序屏蔽掉,把排序加到SQL语句里面去执行,结果出来的结果要么是0 要么是全部结果,结果为0行时同样报死锁错误,因为有其它项目要忙,这个问题又搁置了几天。

 /// <summary>
        ///  对DataTable进行排序
        /// </summary>
        /// <param name="dt"></param>
        /// <param name="SortCols"></param>
        /// <returns></returns>
        public DataTable SortTable(DataTable dt, string SortCols)
        {
            if (dt == null)
            {
                return null;
            }
            if (dt.Rows.Count == 0)
            {
                return dt;
            }
            DataTable dtSort = new DataTable();
            dtSort = dt.Clone();
            DataRow[] RowArr = dt.Select(" 1=1", SortCols);
            foreach (DataRow row in RowArr)
            {
                dtSort.Rows.Add(row.ItemArray);
            }
            return dtSort;
        }

       周一的时候,由于客户的操作数据特别多,这种情况越来越严重,直接影响到了客户的正常工作,没办法,老板下命令一定要把这一块搞好,于是开始在百度上各种搜索,我是搞程序的,对数据库还算了解,因为没有DBA,这些数据库维护工作只能自己做,通过sql profiler提取记录,发现所有死锁基本上发生在这条语句和一个存储过程上,我存储过程是执行的n条sql语句,当时这样做而没有用事务去做的原因,是考虑到如果客户机突然断网,事务不会提交,就会造成阻塞或死锁,所以就把要执行的语句传入一个存储过程,让存储过程来执行,不知道这种做法对不对,我感觉应该会好点,存储过程如下,从昨天晚上一直在研究这个跟踪记录,发现有一个现象 在存储过程执行到插入A表时,这个时候另外一个查询的SQL语句可以运行,但是死锁往往是发生在这个时候,查询语句是对A表的关联查询,A表中有两个索引一个聚集索引,一个非聚集索引。昨天查了一天无果,上午开始考虑如果做读写分离这种问题是不是不会再发生,上午测试了一下SQL的复制功能,虽然延迟在秒级别,但效果还不是太理想,把情况给老板大致说了一下,老板的意思还是找出来死锁的原因,下午又查了一下午,刚开始怀疑是不是A表数据太大了(5000多万行),在插入的时候消耗过大引起的,后来用DBCC ShowContig扫描了一下 密度达到了99%多,于是开始怀疑是填充因子的问题,是不是太大了,导致插入的时候分页,后来想想我的聚集索引是加在时间上的,而时间列是用getdate()获取的默认值,不应该消耗太大,于是就先把A表清出去了一部分,准备明天再看结果。

      临睡觉前准备再看下跟踪语句,发现所有的死锁都是发生在上述所产的A表插入上和那个select语句上,于是开始搜索select 造成的死锁,查到了下面引用的这篇文章,发现里面讲述的和我这个状况非常相似,A表上的两个索引造成的死锁

存储过程执行插入时要用到A表的聚集索引的X锁,插入完成之后会更新A表的非聚集索引

查询的语句中由于查询列不包含在索引中 所以先使用了A表的非聚集索引 又请求A表的聚集索引查询不包含的字段


这样就发生了互斥,造成了死锁,文中给出了两种解决方法

一个是索引覆盖(把查询的输出列include现在的索引里面)  这种方法感觉不可行 不可能把这么多列加到索引里面
二种方法 是用SQL Server行隔离级别控制   明天测试一下这个方法 只要对其它业务流程不影响  就用这种方法


就到这了 洗洗睡了 希望对你们有帮助


ALTER PROCEDURE [dbo].[ExcuteTrans]
(
@SqlArr varchar(8000)
)
 AS


declare @nSql nvarchar(4000)
declare  @error int
declare  @rtnerror int

set @nSql=@SqlArr
set @error=0
set @rtnerror=0
set @nSql=Replace(@nSql,'#',' set @error=@@error+@error ')
set @nSql=@nSql+' set @error=@@error+@error select @rtnerror=@error'

begin tran

exec sp_executesql @nSql ,N'@rtnerror int  out,@error int',@rtnerror out,@error

if(@rtnerror>0)
 begin 
 rollback tran
 return 
end

commit tran





参考文章:sql server中高并发情况下 同时执行select和update语句死锁问题 (一)

http://blog.youkuaiyun.com/lishehe/article/details/42279147

<think>嗯,用户问的是如何查询数据库死锁数据。这个问题需要分步骤来思考。首先,不同的数据库系统处理死锁的方式不同,所以得考虑主流的几种,比如MySQL、SQL Server、Oracle和PostgreSQL。然后,每种数据库可能有不同的查询方法或者工具。 首先,我需要回忆一下各种数据库死锁日志机制。比如,MySQL可以通过开启InnoDB的监控来获取死锁信息,而SQL Server可能使用扩展事件或者Profiler工具。Oracle的话,可能有特定的数据字典视图,比如V$LOCK和V$SESSION,还有自动生成的跟踪文件。PostgreSQL可能需要调整日志配置,然后在日志中查找死锁信息。 接下来,用户可能需要具体的步骤,所以每个数据库的步骤要分开说明。例如,对于MySQL,需要提到设置innodb_status_output_locks,然后使用SHOW ENGINE INNODB STATUS命令。同时,用户可能需要知道如何分析输出的日志,找到LATEST DETECTED DEADLOCK部分。 还要考虑用户是否了解如何开启这些监控选项,是否需要重启服务或者动态设置。比如,在MySQL中,有些参数是动态的,可以在线设置,而有些可能需要配置文件修改后重启。需要提醒用户注意这一点。 另外,用户可能不仅想知道如何查询死锁数据,还需要如何预防或者解决死锁。虽然问题主要是查询,但可能简要提到预防措施会更好,比如合理设计事务、按顺序访问资源、设置锁超时等。 需要确保步骤清晰,避免使用过于专业的术语,但也要准确。例如,解释每个数据库的特定命令和日志位置,以及如何解读关键信息。比如,在SQL Server中使用扩展事件创建会话,然后查询xml_report字段中的死锁图。 还要注意用户可能的权限问题,比如是否有权限访问某些系统视图或修改配置参数。可能需要提醒用户需要管理员权限才能执行某些操作。 最后,总结部分要概括所有数据库的方法,并给出通用的建议,比如开启死锁日志、监控工具的使用和分析日志的方法。这样用户即使不熟悉特定数据库,也能有一个方向去查找资料。</think>以下分步骤介绍如何查询数据库死锁数据,涵盖主流数据库实现方式: 一、MySQL数据库 1. 启用InnoDB状态监控 ```sql SET GLOBAL innodb_status_output_locks = ON; -- 动态开启锁状态输出 ``` 2. 获取最近死锁信息 ```sql SHOW ENGINE INNODB STATUS\G ``` 在输出结果中查找 `LATEST DETECTED DEADLOCK` 段,包含: - 冲突的事务ID - 被锁定的表和行 - 等待资源的SQL语句 3. 日志文件监控(需配置) ```ini # my.cnf 配置 innodb_print_all_deadlocks = ON # 将所有死锁写入错误日志 ``` 二、SQL Server数据库 1. 使用扩展事件捕获 ```sql CREATE EVENT SESSION [Deadlock_Monitor] ON SERVER ADD EVENT sqlserver.xml_deadlock_report ADD TARGET package0.event_file(...); ``` 2. 查询死锁信息 ```sql SELECT event_data.value('(event/data/value/deadlock)[1]', 'varchar(max)') AS xml_report FROM sys.fn_xe_file_target_read_file(...) ``` 三、Oracle数据库 1. 查询数据字典视图 ```sql SELECT * FROM v$lock; -- 当前锁信息 SELECT * FROM v$session_wait; -- 会话等待事件 ``` 2. 分析跟踪文件 ```sql ALTER SYSTEM SET DIAGNOSTIC_DEST='/u01/app/oracle/diag'; -- 跟踪文件
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值