SQL并行执行计划的并发调度由优化器动态决定,需平衡资源利用与依赖约束:依据估算工作量、运行时资源、数据分片可行性确定DOP;Producer/Consumer/Synchronizer类算子分工协作;依赖关系通过同步点与信号量控制;常见瓶颈包括资源争用、数据倾斜和Gather单点瓶颈。

SQL数据库的并行执行计划中,算子并发调度不是简单地“越多越快”,而是由查询优化器结合系统资源、数据分布、算子特性与依赖关系动态决定的。核心逻辑在于:在保证结果正确性和执行依赖的前提下,最大化利用CPU、内存和I/O带宽,同时避免资源争用和调度开销反噬性能。
并行度(DOP)的确定依据
并行度并非固定值,通常由以下因素共同决定:
- 优化器估算:基于表大小、谓词选择率、索引可用性等估算总工作量,再结合配置阈值(如min_grant_percent、cost_threshold_for_parallelism)判断是否启用并行;
- 运行时资源约束:受max degree of parallelism (MAXDOP)、可用工作线程数、内存授予(Memory Grant)限制;
- 数据分片可行性:只有支持水平切分的算子(如扫描、哈希连接、排序)才能真正并行;而某些算子(如全局聚合、TOP-N、序列化输出)天然需要串行合并点,会形成“并行-串行”转换边界。
算子级并发调度行为
不同算子在并行计划中承担不同角色,调度方式差异显著:
- Producer类算子(如Parallelism(Gather Stream)、Table Scan):每个线程独立处理一个数据分区(如页范围、分区表子集),产出数据流到共享线程间队列;
- Consumer类算子(如Hash Join Build/Probe、Sort):Build端常并行构建多个局部哈希表,Probe端则按数据分布将Probe行路由到对应线程;Sort可能先局部排序再归并(Merge Join式归并或Top-N优化);
- Synchronizer类算子(如Gather Streams、Repartition Streams、Distribute Streams):负责跨线程协调数据流动——Gather用于最终串行输出,Repartition用于重分区(如为Join重新哈希分发),Distribute用于广播或哈希分发驱动表。
依赖关系与执行时序控制
并行计划仍严格遵循数据依赖和控制依赖:
- 子树内部存在隐式同步点,例如Hash Join的Build必须全部完成,Probe才能开始;
- 多个并行分支之间若无数据依赖(如UNION ALL的两个分支),可完全异步启动;若有依赖(如Nested Loop外层驱动内层),则外层线程需逐批喂入数据,内层按需并行执行;
- 调度器通过轻量信号量(如SOS_SCHEDULER_YIELD)、任务队列状态和线程本地缓冲区水位来协调节奏,避免空转或死锁。
常见调度瓶颈与调优提示
实际执行中,并行优势易被以下问题抵消:
- 过度并行导致线程争抢内存或闩锁(如PAGEIOLATCH_SH、CXPACKET高等待),此时降低MAXDOP或增加cost threshold更有效;
- 数据倾斜使部分线程负载远高于其他线程(如哈希分布不均),可尝试加随机前缀扰动哈希键,或改用Merge Join;
- Gather点成为单点瓶颈,尤其在大结果集返回时,可考虑分页、流式消费或禁用并行(OPTION (MAXDOP 1))评估收益比。










