SELECT…INTO语句长时间处于RUNNABLE状态并阻塞其他查询的问题求助
SELECT…INTO语句长时间处于RUNNABLE状态并阻塞其他查询的问题求助
嘿,我太懂这种闹心的感觉了——本来想靠临时表规避阻塞,结果反而卡了3小时还把其他查询都堵了,完全事与愿违。咱们一步步拆解下问题,找找可能的原因和解决办法。
先贴出你的完整脚本方便分析:
IF OBJECT_ID('tempdb..#Products_temp') IS NOT NULL BEGIN DROP TABLE #Products_temp END GO SELECT p.product_id, p.price, p.cost, i.qty INTO #Products_temp FROM Warehouse_Products p WITH (NOLOCK) LEFT OUTER JOIN Warehouse_Inventory i WITH (NOLOCK) ON p.product_id = i.product_id GO DELETE FROM cache.Products GO INSERT INTO cache.Products SELECT * FROM #Products_temp
先搞懂:RUNNABLE状态到底意味着什么?
这个状态说明你的查询已经拿到了必要的锁,正在等待CPU资源,或者是在等待磁盘IO完成(有时候IO等待也会被标记为RUNNABLE,需要结合具体指标判断)。正常6-7分钟的脚本突然卡3小时,大概率是下面几个环节出了问题:
可能的原因&对应的解决建议
1. TempDB拖了后腿
SELECT...INTO会自动在tempdb里创建临时表并写入数据,如果tempdb配置不合理,很容易成为瓶颈:
- 检查磁盘IO:用
sys.dm_io_virtual_file_stats查看tempdb数据文件的读写等待时间,如果IO延迟很高,说明磁盘性能不够(比如用了慢机械盘,或者磁盘队列太长),优先换更快的存储。 - 优化tempdb文件配置:如果tempdb只有一个数据文件,赶紧加几个和第一个文件大小完全相同的数据文件(避免单文件竞争);把自动增长步长改成固定值(比如1GB,别用百分比),避免频繁小幅度增长导致的延迟。
- 检查空间是否充足:如果tempdb快满了,自动增长会频繁触发,直接拖慢写入速度。
2. WITH (NOLOCK)的反向效果
你加了NOLOCK想减少锁,但它不是万能的:
- NOLOCK会读取脏数据,而且如果源表(Warehouse_Products/Warehouse_Inventory)正在大量写入,会导致查询遇到大量页分裂,反而变慢;
- 优化器可能因为NOLOCK无法准确预估行数,生成低效的执行计划(比如本来该用哈希连接,结果用了嵌套循环)。
- 建议先去掉NOLOCK测试一下,如果源表写入不频繁,短时间的共享锁反而能让执行计划更优,速度更快。
3. 替换SELECT...INTO为手动创建临时表
自动创建临时表会有额外的元数据操作开销,而且无法提前加索引优化:
IF OBJECT_ID('tempdb..#Products_temp') IS NOT NULL BEGIN DROP TABLE #Products_temp END GO -- 手动创建临时表,和cache.Products的数据类型严格对齐 CREATE TABLE #Products_temp ( product_id INT, -- 换成你实际的数据类型 price DECIMAL(18,2), cost DECIMAL(18,2), qty INT ) -- 给临时表加聚集索引,加速后续写入cache.Products的操作 CREATE CLUSTERED INDEX IX_Products_temp_product_id ON #Products_temp(product_id) GO -- 用INSERT INTO代替SELECT...INTO INSERT INTO #Products_temp SELECT p.product_id, p.price, p.cost, i.qty FROM Warehouse_Products p WITH (NOLOCK) LEFT OUTER JOIN Warehouse_Inventory i WITH (NOLOCK) ON p.product_id = i.product_id GO
4. 检查服务器整体负载
当时服务器是不是CPU跑满了?或者内存不足导致大量分页?
- 用
sys.dm_os_wait_stats查看等待类型:如果是SOS_SCHEDULER_YIELD说明CPU资源不够,查询在排队;如果是PAGEIOLATCH_*说明是磁盘IO瓶颈。
5. 优化cache.Products的更新逻辑
你现在用DELETE FROM cache.Products会持有全表锁,而且删除大量数据很慢,换成TRUNCATE TABLE cache.Products会快很多——TRUNCATE是DDL操作,锁更少,速度是DELETE的几十倍(注意:TRUNCATE不能回滚,且如果表有触发器会失效,根据你的场景判断是否可用)。
最后总结排查顺序
先查tempdb的磁盘IO和配置 → 看服务器CPU/内存负载 → 查看查询执行计划是否低效 → 试试手动创建临时表+去掉NOLOCK → 替换DELETE为TRUNCATE。
备注:内容来源于stack exchange,提问作者Joe Defill
相关产品推荐
相关产品推荐

