在这一期的性能调优培训里,我想详细讲解下SQL Server里的并行执行计划(Parallel Execution Plans) 。执行一个有并行执行计划的查询,意味着SQL Server从执行计划里进行必须的运算符需要使用多线程。首先我会介绍下并行执行计划里最常用的运算符,然后我们来分析下SQL Server是如何决定是否要使用并行执行计划。
并行运算符(Parallel Operators)
对于并行执行计划最常见的误解就是多线程只用于整个执行计划。这是错误的,因为SQL Server可以分配多个工作线程给所有可并行(parallelism-aware)运算符。这就意味者一个大型的并行计划可以消耗大量的线程。SQL Server在并行计划里区分2类运算符:可并行(parallelism-aware)运算符,和称为交换运算符(Exchange Operators):
- 很多传统的运算符可以使用多线程来进行它们的工作,它们是可并行(parallelism-aware)运算符:索引扫描(Index Scan),索引查找(Index Seek),嵌套循环(Nested Loop),哈希连接(Hash Join),排序(Sort)等。
- 交换运算符(Exchange Operators)是在并行执行计划里,用来分发和合并多线程之间的行。
SQL Server使用下列3个交换运算符:
- Distribute Streams: 在并行计划中,从一个单线程里区域转变为多线程区域(接收记录的单个输入流,并生成多个输出流。)。
- Repartition Streams: 在线程间重新分配行(例如,当前一个运算符是并行哈希连接)(处理多个流并生成多个记录流。)。
- Gather Streams:在并行计划中,从多线程区域转变为单线程区域(处理几个输入流并通过组合这几个输入流生成单个记录输出流。)。
当你阅读并行执行计划时,你总会看到2种运算符的结合。每个执行计划必须生成一个单线程的结果,因此在并行执行计划的结尾,你总能看到Gather Streams运算符。
查询优化器是否生成一个并行执行计划也取决于你使用的查询结构是否会阻止并行计划,例如:
- T-SQL 语句和SQL公共语言运行库的自定义函数(SQLCLR UDFs);
- 内建函数,像OBJECT_ID(), ERROR_NUMBER(), @@TRANCOUNT等;
在并行计划里,也有很多查询结构会强制串行区域(这部分操作会使用并行计划):
- 系统表扫描(System Table Scans)
- 序列函数(Sequence Functions)
- 向后扫描(Backward Scans)
- 递归查询(Recursive Queries)
- 表值函数(TVFs)
- TOP
在你的并行计划里,串行区域越少,你的查询越快。当你下次写你的查询时,好好考虑这点。
什么时候使用并行(When to go parallel?)
每个执行计划都会被SQL Server分配一个成本因素(Cost Factor) 。成本因素就是用来告诉SQL Server这个执行计划有多贵的简单数字。这个数字越大,执行计划运行的关联成本就越高。
SQL Server有一个称为并行开销阀值(cost threshold for parallelism)的配置选项,用来定义成本因素,在哪个点查询优化会考虑使用并行计划。默认这个配置选项是5,也就说查询高于这个成本值,只要并行计划可行,就会使用并行计划。
当并行计划被查询优化器编译好后,最大并行度(Max Degree of parallelism(MAXDOP))选项定义对执行计划中的每个并行运算符可用线程数。我刚才提过,并行执行计划里的每个运算符都可以用多线程运行,而不是整个执行计划。当然在并行执行计划里,线程可以被前一个运算符共享和重用。可以通过服务器属性->高级->并行配置这些属性:
默认最大并行度(MAXDOP)选项值是0,因此SQL Server会默认将并行查询穿过所有分配给SQL Server的CPU内核。当你使用NUMA(Non Uniform Memory Access)系统时,这个会导致性能问题。最佳实践是限制MAXDOP选项值,在NUMA节点里的核心数(包括超线程的核心数)范围内。这样就可以保证SQL Server的并行计划呆在NUMA节点内。
小结
这一期的性能调优培训里我讲解了SQL Server里的并行执行计划,你学到了在并行执行计划里涉及到的各种运算符,还有在SQL Server里你如何配置并行度。如果你想对并行计划有更深入的了解,可以看下…………(此次内容待完善)。
看完这篇文章,性能调优培训的第3个月就已经结束了,在下一个月我会讲解下SQL Server里的统计信息。请继续关注!