通过上一篇文章,我介绍了 SqlServer 应用程序 www.sxzhongrui.com_os_waiting_tasks 引起的问题(第 1 部分)。我谈到了 www.sxzhongrui.com_exec_requests 和 www.sxzhongrui.com_os_waiting_tasks 之间的并行等待。得到了不同的结果。在本文中,我们将讨论我的第二个问题:为什么并行计划(4 个线程)同时有这么多等待? SQL并行是如何执行的? ! ! !

    先贴上一篇文章中www.sxzhongrui.com_os_waiting_tasks的结果图如下:

     我们分析一下这个结果的task_address,可以看到去重后实际上只有9个任务。换句话说,一个并行(4个线程,不同的配置,不同的情况)将有9个任务。有线程、任务、工作人员和调度程序。这些是什么?有必要先说一下,因为在写这篇博客之前我也很困惑。

调度程序

对于每一个逻辑CPU,SQLSERVER都会有一个与之对应的调度器,它代表了SQL层面的CPU对象。只有获得了调度器所有权的Task Worker才能在这个逻辑CPU上运行。

所谓逻辑CPU就是SQLSERVER从Windows层面看到的CPU数量。如果是双核CPU,那么一个物理CPU被SQL视为两个逻辑CPU。如果系统仍然使用

超线程,即 SQLSERVER 的 4 个逻辑 CPU

规则:每个调度器上的最大worker数量等于SQLSERVER的最大线程数除以调度器数量。在同一时间点,只有一个具有调度程序的 Worker 可以处于运行状态,其他 Worker 必须处于等待状态。 。这可以减少每个逻辑CPU上运行的线程数量,减少上下文切换,并提供可扩展性。调度程序是SQLSERVER的一个逻辑概念,它与物理CPU无关。换句话说,Windows 可以安排调度程序在该 CPU 上运行一段时间,在该 CPU 上运行一段时间。

但是,如果在 sp_configure 中设置了 CPU 亲和性掩码,则调度程序将固定在特定的 CPU 上

工人

每个worker对应一个线程(或纤程),是SQLSERVER任务的执行单元。 SQLSERVER不直接调度线程/纤程,而是调度worker,以便SQLSERVER可以控制

任务调度

规则:每个工作线程将代表一个线程(或纤程)并绑定到一个调度程序。如果调度器固定在某个CPU上(通过设置CPU亲和性掩码),那么worker也将固定在某个CPU上。每个调度器都有一个worker的上限,可以根据SQLSERVER的工作负载来创建或释放worker。每个worker都会运行一个完整的任务。任务完成之前不会退出,除非任务主动进入等待状态。

只有当有新任务要运行并且当前没有空闲的worker时,调度程序才会创建一个新的worker。

如果某个worker空闲时间超过15分钟,调度程序可能会删除该worker及其对应的线程。当SQLSERVER遇到内存压力时,它也会删除大量空闲的worker以节省多页内存开销。为 CPU 和 SQLSERVER 版本的各种组合自动配置的最大工作线程数。 32 位计算机。 64 位计算机。 <=4 7 16 352 704
32 480 960

任务

在工作线程上运行的最小任务单元。最简单的任务是简单的批处理。例如,客户发送了以下请求:

选择@@SERVERNAME
GO
选择 GETDATE()

那么这两个批次分别是两个任务。 SQLSERVER会先给第一批分配一个worker(select@@servername),将结果返回给客户端,然后分配第二批

(选择 getdate())一个工人。这两个工人可能是不同的工人。即使任务开始在不同的调度程序上运行,它也不会从工作线程中删除。例如,如果一条select语句被其他连接阻塞,那么worker就无法继续运行,只能进入等待状态。但是这个select任务不会释放worker并让他去做其他任务。所以结果就是这个worker对应的线程会进入等待状态

屈服

SQLOS任务调度算法的核心是逻辑调度器上运行的所有worker都是非抢占式的。 Worker 始终在调度器上运行,直到运行完毕或者主动将调度器让给其他 Worker。这种“放弃”调度程序的动作称为让步。每个调度程序都会有一个可运行列表。所有等待CPU运行的worker都会在这个列表中排队,采用先进先出的算法,等待SQL分配给他。调度程序运行 SQLSERVER 定义。调度器中有很多让步规则来约束任务的运行时间。如果任务比较复杂,无法快速完成,那么会保证任务在适当的时间yield,不会占用调度器太多的时间。

常见时间点:

1。每次worker想要读取数据页时,SQLSERVER都会检查worker在调度程序上运行了多长时间。如果超过4ms,就会进行让步。

2。每次对 64KB 结果集进行排序时,都会进行让步。

3。在语句编译过程中(该过程占用较多CPU资源),经常会出现yield

4。如果client不能及时移除结果集,worker将会yield

5。一批中的每句话完成后,都会进行一次yield

通常来说,即使某个任务耗时很长,它使用的worker也会频繁yield,不会长时间占用CPU。如果有许多工作线程同时在调度程序上运行,SQLSERVER 会通过自动生成工作线程来调度并发运行。这比 Windows 使用的上下文切换更有效

    附手绘图

    另一篇推荐文章是SQL SERVER SQLOS任务调度。微软亚太区官方博客

    我们对SQL SERVER SQLOS的任务调度有了一个大概的了解。我们回到我们的并行话题,看一下这个并行执行的调度:

    一个并行处理被分配给9个任务,并且还启用了9个worker,由4个调度器调度。每个调度程序都有一个请求数据和另一个等待数据。所以人们申请数据是可以理解的,但是他们还在等什么呢?我个人的理解和目前的执行计划有关。四个线程获取到数据后,需要进行汇总操作。 SQL不会等待数据获取才开始线程接收。相反,接收线程将在获取数据时等待。

 

 问题解决了吗?既然已经标注为中长了,你看我还有疑问! ! ! !让我们继续下一篇文章…