为何SQL Server读取大表全量数据时不采用并行执行计划?
问题描述
创建如下SQL Server表结构:
CREATE TABLE [dbo].[GpsData] ( [GpsDataID] [int] IDENTITY(1,1) NOT NULL, [UnitID] [int] NOT NULL, [Longitude] [float] NOT NULL, [Latitude] [float] NOT NULL, [GpsTimeStamp] [datetime] NOT NULL, [SpeedKmpH] [tinyint] NULL, [OdoKm] [float] NULL, [DataStatusID] [tinyint] NULL, [DateCreated] [datetime] NOT NULL, PRIMARY KEY CLUSTERED ([GpsDataID] ASC) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY] ) ON [PRIMARY]
插入200万行数据后,执行以下三类查询均能获得并行执行计划:
- 主键范围过滤查询:
SELECT * FROM GpsData WHERE GpsDataID < 3000
- 等效全表的条件查询:
SELECT * FROM GpsData WHERE GpsDataID % 2 = 0 OR GpsdataID % 2 = 1
- 非主键列过滤查询:
SELECT * FROM GpsData WHERE Longitude > 1
但执行纯全表扫描查询时:
SELECT * FROM GpsData
却无法生成并行执行计划。按常理全表扫描可由多线程分别扫描表的不同部分并合并结果,为什么会出现这种差异?
原因分析
这是SQL Server查询优化器基于成本估算和并行执行规则做出的选择,核心原因如下:
- 串行全表扫描的综合成本更低:对于纯全表扫描,SQL Server认为单线程顺序读取聚集索引的开销,远低于启动并行执行的额外成本——包括线程分配、任务协调、结果合并等操作的开销,这些额外消耗抵消了多线程带来的收益,因此优化器选择串行计划。
- 带过滤的查询触发并行阈值:另外三个查询都包含过滤逻辑,要么需要执行范围查找,要么需要对每一行计算条件(如取模、数值比较),这些操作的CPU开销更高,优化器估算出的执行成本超过了默认的并行执行阈值(Cost Threshold for Parallelism,默认值为5),因此会生成并行计划来分摊CPU负载。
- 聚集索引的顺序IO优势:该表的聚集主键
GpsDataID让数据完全有序存储,串行扫描可以利用顺序IO的高效性,连续读取数据页;而并行扫描需要将数据页拆分给多个线程,反而会破坏顺序IO的连续性,导致IO效率下降,优化器会综合权衡这一点选择更优方案。 - 优化器的特殊处理逻辑:SQL Server对纯全表扫描的并行计划生成有特殊限制,除非表的规模极大(远超过200万行)或服务器CPU资源极度充裕,否则不会轻易启用并行。而带过滤条件的查询因为有额外计算开销,更容易满足并行触发的成本条件。
内容的提问来源于stack exchange,提问作者Markus
相关产品推荐
相关产品推荐

