带NOLOCK的SELECT并行执行性能下降问题排查求助
排查思路与优化建议
这是个非常典型的高并发只读场景下的性能问题,咱们一步步拆解来排查:
1. 先从查询本身的执行计划入手
- 先看单查询的资源消耗:你的统计里
CpuPerExec接近2秒,IOPerExec有13935次,这说明单查询本身的资源开销就不小,并行执行时资源争抢会被放大。建议先检查执行计划:- 有没有缺失索引?可以看执行计划里的“缺失索引建议”,添加合适的索引能大幅降低IO和CPU消耗。
- CTE是否被重复执行?SQL Server里有些复杂CTE不会被自动物化,每次引用都会重新计算,改成临时表(
#temp)或者表变量(@temp)试试,强制物化中间结果,减少重复计算的开销。
- 关于
NOLOCK的使用:你用ReadUncommitted隔离级别没问题,但要注意——如果目标表有频繁的DML操作,NOLOCK会让查询扫描未提交的行版本,反而可能增加IO和CPU开销。另外,你的连接代码里那段BeginTransaction然后立刻Commit的操作完全多余,直接去掉就行:要么在查询开头加SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED,要么在连接字符串里配置Transaction Isolation Level=Read Uncommitted,这样更高效。
2. 排查资源争抢问题
- 从DMV的会话状态看,多个会话处于
runnable状态,这说明它们在等待CPU资源(runnable表示线程已经排队等待CPU时间片,但还没轮到执行)。可以查一下sys.dm_os_wait_stats里的等待类型:- 如果
SOS_SCHEDULER_YIELD累计值很高,说明CPU资源被占满,查询在争抢CPU; - 如果
CXPACKET等待很高,可能是查询的并行度设置不合理——比如服务器的MAXDOP(最大并行度)设置得太高,或者查询本身的并行执行计划导致线程之间互相等待。可以试试强制查询使用串行执行(OPTION (MAXDOP 1)),看并行场景下的性能有没有改善。
- 如果
- 检查磁盘IO瓶颈:并行执行时IO总量是单查询的数倍(你这里9次执行总IO达125422次),如果磁盘IOPS或吞吐量不够,所有查询都会排队等待IO。用
sys.dm_io_virtual_file_stats查看数据库文件的读写延迟,正常读延迟应该在20ms以内,超过的话说明磁盘IO是瓶颈,可能需要升级存储或者优化查询减少IO。
3. 连接与事务的优化
- 去掉连接代码里多余的事务操作:
con.BeginTransaction(System.Data.IsolationLevel.ReadUncommitted).Commit();这段代码没有任何实际作用,反而会增加数据库的事务日志开销,直接删除即可。 - 确认连接池配置:.NET Core默认连接池最大连接数是100,你这里4个并行查询肯定够,但如果后续并发量上去,要确保
Max Pool Size设置合理,避免出现连接等待的情况。
4. 缓存层面的优化
- 检查Buffer Pool缓存:你的统计里
AvgElapsedTime是2.5秒左右,但你说单查询执行耗时4-5秒,可能是冷缓存和热缓存的差异——第一次执行需要从磁盘读数据,之后数据被缓存到Buffer Pool里就快了。可以先跑几次查询预热缓存,再并行执行,看性能有没有明显提升。如果提升大,说明服务器内存不足,数据无法被持久缓存,需要增加内存或者优化查询减少数据读取量。 - 考虑结果集缓存:如果仪表板数据不需要实时更新,可以把查询结果缓存到.NET的
IMemoryCache或者Redis里,设置合理的过期时间(比如5分钟)。这样高并发时直接从缓存取数据,不用每次都查数据库,能彻底解决数据库的压力问题。
内容的提问来源于stack exchange,提问作者Jerome2606
相关产品推荐
相关产品推荐

