select from view 会卷入死锁,因其执行时展开为对基表的真实访问,若视图含无索引join、where或order by,易引发大量s锁与x锁冲突;需优化索引、缩短事务、统一加锁顺序。

视图本身不加锁,死锁根源在底层表的并发访问顺序、索引缺失和事务范围——改视图定义没用,得动表结构和事务逻辑。
为什么 SELECT FROM VIEW 会卷入死锁
SQL Server 或 MySQL 中的视图只是预存的 SELECT 语句,执行时完全展开为对基表的真实访问。一旦视图含 JOIN 多张表、WHERE 过滤无索引字段、或 ORDER BY + TOP 没覆盖索引,查询就可能触发 Clustered Index Scan 或大量 Key Lookup,导致申请成百上千个 S 锁。此时若有其他事务正对同一组主键/索引键做 UPDATE,持有 X 锁,双方互相等待,死锁即发。
- 典型现象:监控看到
wait_type = LCK_M_S或LCK_M_U长时间堆积,同时sys.dm_exec_requests显示阻塞链 - 关键误区:以为“只读视图=安全”,但 InnoDB 的间隙锁、SQL Server 的键范围锁,都会让纯 SELECT 参与死锁
- 特别注意:视图里用
SELECT *+JOIN,而被 JOIN 的表仅在id有主键,但实际关联走的是code字段 → 必须给code建索引,否则易锁升级
必须检查的三类索引字段
不是所有 WHERE 字段都该建索引,但以下三类不覆盖,死锁概率陡增:
-
JOIN条件字段:如orders.user_id = users.id→orders.user_id和users.id都要有索引(后者通常已有主键) -
WHERE过滤字段:如WHERE status IN ('pending', 'processing')→ 在status上建非聚集索引;若常查status = 'pending' AND created_at > '2026-01-01',优先建复合索引(status, created_at) -
ORDER BY字段:如视图常ORDER BY created_at DESC LIMIT 20→ 建倒序索引CREATE INDEX idx_created_desc ON table_name (created_at DESC),避免排序阶段持锁
验证方式:打开 SET STATISTICS XML ON,看执行计划中是否出现 Index Scan、Table Scan 或高频 Key Lookup —— 这些都是锁膨胀信号。
事务里调用视图是最危险的场景
视图查询本身耗时几十毫秒不可怕,可怕的是它嵌在一个持续数秒的事务里。常见高危模式:
- 事务内先
SELECT FROM view查出订单列表,再发起 HTTP 请求通知下游,最后才COMMIT - 用视图做分页:
SELECT * FROM view ORDER BY id OFFSET 10000 ROWS FETCH NEXT 20 ROWS ONLY,未加时间过滤,扫描历史百万行并持锁 - ORM 自动生成的“读+写”混合事务,读部分命中复杂视图,写部分更新同一张表
对策非常直接:
- 把视图查询移出事务体,只保留真正需要原子性的写操作(如扣库存+生成订单)
- 如必须在事务中读,且业务能容忍脏读,SQL Server 可加
WITH (NOLOCK)表提示;MySQL 可设SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED—— 但严禁用于资金、库存等强一致路径 - 避免在事务中执行含聚合、子查询、多层嵌套的视图;拆成简单
SELECT+ 应用层组装
统一加锁顺序比加索引还关键
即使索引齐全、事务很短,若两个事务以不同顺序更新相同表,照样死锁。例如:
- 事务 A:先
UPDATE orders WHERE id = 100,再UPDATE order_items WHERE order_id = 100 - 事务 B:先
UPDATE order_items WHERE order_id = 100,再UPDATE orders WHERE id = 100
解决方法不是靠数据库,而是靠代码约定:
- 所有涉及
orders和order_items的写操作,强制按“先主表后子表”顺序执行 - 批量更新时,对 ID 列显式排序:
UPDATE ... WHERE id IN (100, 200, 300) ORDER BY id(MySQL 支持,SQL Server 需用 CTE 或临时表保证顺序) - 用
SELECT ... FOR UPDATE ORDER BY id(MySQL)或SELECT ... WITH (UPDLOCK, SERIALIZABLE)(SQL Server)提前按固定顺序加锁
最容易被忽略的一点:死锁往往不是单个 SQL 的问题,而是多个模块各自封装了“安全”的视图或存储过程,却没人统筹它们的资源访问顺序。上线前必须做并发路径审计,而不是等压测时才发现 ABBA 循环。











