视图本身不加锁,死锁源头在基表访问顺序;SQL Server中视图仅为逻辑SELECT,执行时展开为对基表的实际查询,真正冲突源于多事务并发修改同一组基表时加锁顺序不一致,如事务A先锁orders再锁order_items、事务B顺序相反,形成循环等待。

视图本身不加锁,死锁源头在基表访问顺序
SQL Server 中的视图只是保存的 SELECT 语句,执行时会被展开为对底层表的实际查询。它自己不持有锁、不引发死锁;真正冲突的是多个事务通过视图(或直接)并发修改同一组基表时,因加锁顺序不一致形成的循环等待。比如一个事务先锁 orders 再锁 order_items,另一个反着来,就极易触发死锁。排查必须绕开“改视图”这个误区,直击表访问路径和事务行为。
用 sys.dm_tran_locks + sys.dm_exec_requests 快速定位阻塞链
别等报错 1205 才动手。日常监控可立刻发现潜在风险:
- 查出被阻塞的会话:
SELECT session_id, blocking_session_id, wait_type, wait_time, status FROM sys.dm_exec_requests WHERE blocking_session_id > 0 - 关联锁信息看谁在持什么资源:
SELECT resource_type, resource_description, request_mode, request_status FROM sys.dm_tran_locks WHERE request_session_id = @spid - 重点盯
resource_type = 'KEY'或'PAGE'—— 锁粒度越小越安全;若看到大量OBJECT级锁,说明很可能因缺失索引导致锁升级 - 结合
sys.dm_exec_sessions查program_name和host_name,快速判断是哪个应用模块在调用视图
从死锁图(xml_deadlock_report)里抓两个关键字段
启用 Extended Events 捕获 xml_deadlock_report 后,打开 XML 文件,核心只看两处:
-
process节点下的inputbuf:它显示每个牺牲者/幸存者最后执行的语句——大概率就是你那个视图被展开后的实际 SQL,比如SELECT * FROM v_order_summary WHERE status = 'pending' -
resource-list里的owner-list和waiter-list:对比两个 process 等待的同一key(如hobt_id=72057594046111744),就能确认哪两张表、哪几行在互相卡住 - 特别注意:如果
inputbuf显示的是存储过程名(如exec proc_update_order),要进过程里找是否嵌套调用了该视图,且是否在事务内执行
修复动作必须落在索引、顺序、事务三处,而非视图定义
改视图 WITH SCHEMABINDING 或加 NOLOCK 都治标不治本,甚至埋雷:
- 给视图中所有
JOIN字段建索引:比如v_orders JOIN users ON orders.user_id = users.id,则orders.user_id和users.id都得有索引,否则可能触发表扫描+锁升级 - 统一多表更新顺序:所有涉及
orders和order_items的存储过程/代码,必须严格按相同顺序访问(如总是先orders后order_items),连触发器和外键级联也要纳入约束 - 把视图查询移出长事务:常见错误是在
BEGIN TRAN里先查视图分页结果,再发 HTTP 请求;应拆成“查视图 → COMMIT → 处理业务逻辑”,避免锁持有时间不可控
最容易被忽略的是:同一张表多行更新时,WHERE id IN (5,1,9) 和 WHERE id IN (1,5,9) 在锁获取顺序上不同,可能成为死锁诱因——排序后再拼 IN 条件,能显著降低风险。

















