【介绍】
SQL Server
通过
WORKER, SCHEDULER, TASK
等来对任务进行调度和处理。了解这些概念,对于了解
SQL Server
内部是如何工作,是非常有帮助的。
通常来讲,
SCHEDULER
个数是跟
CPU
个数相匹配的
。除了几个系统的
SCHEDULER
以外,每一个
SCHEDULER
都映射到一个
CPU
,如下面的查询结果所示,我们有四个
CPU
,也就有相应四个
SCHEDULER
。
SELECT cpu_count,scheduler_count,scheduler_total_count,max_workers_count FROM sys.dm_os_sys_info
而
WORKER (
又称为
WORKER THREAD),
则是工作线程。在一台服务器上,我们可以有多个工作线程。因为每一个工作线程要耗费资源,所以,
SQL Server
有一个最大工作线程数。一个
TASK
进来,系统会给它分配一个工作线程进行处理。但是当所有的工作线程都在忙,而且已经达到了最大工作线程数,
SQL Server
就要等待,直到有一个忙的工作线程被释放。最大工作线程数可以通过下面的查询得到。
SQL SERVER
并不是一开始就把这些所有的工作线程都创建,而是依据需要而创建。
SELECT cpu_count,scheduler_count,scheduler_total_count,max_workers_count FROM sys.dm_os_sys_info
TASK
是由
BATCH
而来。我们知道,一个连接,可以包含多个
BATCH
,而每个
BATCH
则可以分解成多个
TASK
。如下面某一个连接要做的事情。这个连接要做的有两个
BATCH
,而每个
BATCH
,如
SELECT * FROM TABLE_B
,因为可以支持并行化查询,所以可能会被分解成多个
TASK
。具体
BATCH
怎么分解成
TASK
,以及分解成多少个,则是由
SQL Server
内部决定的。
INSERT INTO TABLE_B VALUES (‘aaa’)
GO
SELECT * FROM TABLE_B
GO
【关系】
我们初步了解了
Connection, Batch, Task, Worker, Scheduler, CPU
这些概念,那么,它们之间的关系到底是怎么样呢?
如上图所示,左边是很多连接,每个连接有一个相应的
SPID
,只要用户没有登出,或者没有
timeout,
这个始终是存在的。标准设置下,对于用户连接数目,是没有限制的。
在每一个连接里,我们可能会有很多
batch
,在一个连接里,
batch
都是按顺序的。只有一个
batch
执行完了,才会执行下面一个
batch
。因为有很多连接,所以从
SQL Server
层面上看,同时会有很多个
batch
。
SQL Server
会做优化,每一个
batch
,可能会分解成多个
task
以支持如并行查询。这样,在
SQL
层面上来看,同时会有很多个
TASK
。
SQL Server
上,每一个
CPU
通常会对应一个
Scheduler,
有几个额外的系统的
Scheduler
,只是用来执行一些系统任务。对用户来讲,我们只需要关心
User Scheduler
就可以了。如果有
4
个
CPU
的话,那么通常就会有
4
个
User Scheduler
。
每个
Scheduler
上,可以有多个
worker
对应。
Worker
是真正的执行单元,
Scheduler
(对
CPU
的封装)是执行的地方。
Worker
的总数受
max worker thread
限制。每一个
worker
在创建的时候,自己需要申请
2M
内存空间。如果
max worker thread
为
1024
,并且那些
worker
全部创建的话,至少需要
2G
空间。所以太多的
worker
,会占用很多系统资源。
【跟踪】
我们了解了
Connection, Batch, Task, Worker, Scheduler, CPU
之间的关系,下面我们用
DMV
跟踪一下运作的流程。
步骤一:
执行下面的脚本,创建一个测试数据库和测试数据表
CREATE DATABASE Test4;
GO
USE Test4;
GO
CREATE TABLE Test
(ID INT,
name NVARCHAR(50)
);
INSERT INTO Test
VALUES(1,'aaa');
步骤二:
打开一个查询窗口,执行下面的语句,注意,我们这里并没有
commit transaction.
USE Test4
BEGIN TRAN;
UPDATE Test SET name = 'bbb'
WHERE [ID] = 1;
步骤三:
打开另外一个窗口,执行下面的语句,我们会看到,下面的查询会一直在执行,因为我们前面的一个
transaction
并没有关闭。从查询窗口,我们可以看到,下面语句执行的
SPID
为
58
USE Test4
SELECT
*
FROM
Test
步骤四:查看连接。
从下面的查询来看,我们的连接对应的
SPID
是
58
,被
block
住了。
SELECT * FROM sys.sysprocesses where spid=56
步骤五:查看
batch
我们查看
SQL Profiler,
看到我们的
Batch
是
SELECT * FROM TEST
步骤六:查看
TASK
用下面的
DMV,
我们可以看到,针对
SESSION_ID=58
的,只有一个
task. (
地址为
0x0064F048),
而针对该
TASK
的
worker
地址为
: 0x803081A0
。同时我们也可以看到该
worker
运行在
Scheduler 0
上面。
步骤七:查看
WORKER
从下面的查询可以知道,这个
WORKER
已经执行了
5291
个
task
了。这个
worker
相应的
Scheduler
地址是
0x00932080