游客发表

mssql查询无响应

发帖时间:2026-10-17 04:53:24

下午三点十七分,询无响DBA老周给我发来一条消息:“那个报表库又卡死了。询无响”

我远程连上去看了一眼,询无响sys.dm_exec_requests里躺着十几个SESSION_ID,询无响状态全是询无响SUSPENDED,wait_type清一色写着LCK_M_X。询无响这不是询无响死锁,是询无响活活堵死——有个事务攥着表锁不撒手,其他人全在排队等钥匙。询无响

查sys.dm_tran_locks,询无响锁的询无响源头指向一个SPID 87。它的询无响last_wait_type是NULL,说明它根本没在等任何资源,询无响纯粹是询无响事务开着不提交,锁就赖着不放。询无响看它open_transaction_count,数值是1,但transaction_begin_time显示它已经跑了快四十分钟。

这种场景我见太多了。不是查询写得烂,是有人开了事务忘了提交。后台程序里写了个BEGIN TRAN,然后调了个外部API,结果API超时,代码没走COMMIT也没走ROLLBACK,连接池里的这个连接就这么僵着。锁不释放,后面所有要碰同一张表的查询全卡住。

老周想直接KILL 87,我拦了一下。先看它到底在跑什么——DBCC INPUTBUFFER(87),返回的是一串UPDATE语句,更新的是orders表的一行。好家伙,就一行。一行数据锁了四十分钟,把整个报表库的读操作全堵死了。

这种问题最坑的地方在于,你没法从执行计划里找到原因,因为阻塞根本不在执行计划层面。你要看的是锁等待链——谁在等谁,谁握着锁不放。用sys.dm_os_waiting_tasks看blocking_session_id,一层层往上追,最后追到87,它没有blocker,它就是源头。

处理倒是简单,KILL 87,事务回滚,锁释放,后面的查询瞬间跑完。但问题来了——为什么这行数据会被锁这么久?查了下代码,是业务方写了个批量导入的存储过程,循环里每处理一条数据就开个事务,但没加TRY...CATCH,一旦中间报错,事务就悬在那里。更糟的是,连接池里的连接不会因为语句报错而重置,事务状态被保留下来。

后来我在监控脚本里加了个定时任务,每五分钟扫一次sys.dm_tran_session_transactions,发现事务开启时间超过十分钟的,直接抓出来发告警。但说实话,这只是治标。真正的解决办法是让开发改代码——事务里别放外部调用,要么就加超时控制,要么就显式提交或回滚。

那天下午我盯着屏幕看了半小时,发现一个有意思的现象:阻塞期间,CPU使用率才百分之三,内存也充足,IO也没压力——系统资源全闲着,就是锁把路堵死了。很多人遇到查询无响应,第一反应是加索引、调参数、加内存,方向完全错了。你去看wait_type,如果满屏都是LCK_M_*,那跟性能没关系,纯粹是并发控制出问题了。

那次处理完,老周问我怎么判断是锁问题而不是慢查询。我说你就看一条:如果查询卡住但CPU没跑满,多半是锁;如果CPU飙到百分之百,那才可能是查询本身太慢。这个判断标准糙了点,但八九不离十。

后来那个报表库又卡过一次,我远程一看,wait_type还是LCK_M_X,但源头SPID变了。还是老毛病——新写的代码又踩了同一个坑。我叹了口气,把那条告警短信转发给开发组长,附了一句话:“代码不修,咱们俩就得一直干这活。”

    热门排行

    友情链接