前面学习了SQL Server如何解析、algebrize和优化你想执行的T-SQL。这里将介绍SQL Server如何执行查询计划。下面首先看看SQL Server 2005中体系结构如何变化的,以及SQLOS的介绍。
SQLOS
SQL Server 2005随着SQLOS的引入,基本的体系结构经历了一个重要改变。SQLOS这个组件为SQL Server的其他组件提供了基本的服务,如关系引擎和存储引擎。这个体系结构如图5-15所示。
SQLOS提供的主要服务是调度、内存管理(程序缓存及查询计划所在地),还提供了与当前讨论无关的很多服务。SQLOS实现了系统对象的层级框架,用于调度。图5-16显示了这些对象的基本层次----从父节点SQLOS,往下到worker、task和OS线程,实际完成工作的地方。调度的出发点和内存分配时内存节点。
内存节点
SQLOS内存节点是一个与节点关联的内存的逻辑容器,是带有共享内存的CPU的集合。总是有一个额外的内存节点用于专用管理员连接(DAC),这确保总能有可用的资源来服务DAC,即使其他所有的系统资源都已被使用。在一个没有NUMA的8处理器的SMP系统,有一个内存节点用于服务器一般性使用,还有一个用于DAC,如图5-17所示:
在一个有两个节点各4核的8处理器的NMUA系统,将有2个内存节点用于一般性使用,再有一个用于DAC,如图5-18所示:
通过查询DMV sys.dm_os_memory_nodes,你可以看到服务器上的内存节点的布局。然而,包含sys.dm_os_nodes的列node_state_desc会更有意义:
select c.node_id, c.memory_node_id, m.memory_node_id, c.node_state_desc , c.cpu_affinity_mask, m.virtual_address_space_reserved_kb from sys.dm_os_nodes as c inner join sys.dm_os_memory_nodes as m on c.node_id = m.memory_node_id
软NUMA
在一些场景,你或许会在工作上遇到SMP服务器,这时你依然可以受益于NUMA类型的SQL Server架构。要实现这一点,你可以使用软NUMA,这使你能够使用注册表设置,来告诉SQL Server配置成NUMA系统,使用你指定的CPU-to-memory-node映射。使用软NUMA的一个常见场景是SQL Server托管的应用程序有几个不同的用户组,这些用户有不同的查询需求。配置理论上的16处理器的软NUMA服务器,为2个NUMA节点各配4个CPU,第三个NUMA节点配一个8CPU的节点,接下来为3个节点配置连接到不同的端口,然后为每个级别的工作负载改变连接设置。负载A关联端口x并连接第一个NUMA节点;B关联y连第二个;其他负载关联z连第三个。
CPU节点
CPU节点是CPU的一个逻辑集合,它们共享一些公共资源,如缓存或内存。在SQLOS对象分层上,CPU节点位于内存节点下方。但是,一个内存节点可以有一个或多个CPU节点与之关联,一个CPU节点只能关联到一个内存节点。实践中,几乎所有的配置都是1:1的关系。CPU节点可通过DMV sys.dm_os_nodes查看:
select node_id, node_state_desc, memory_node_id, cpu_affinity_mask from sys.dm_os_nodes
处理器亲和(affinity)
CPU亲和是强制一个工作负载使用特定CPU的一种方式,是影响调度和SQL Server SQLO配置的另一种途径。管理CPU亲和有几个层面。SQL Server外面,你可以使用OS的CPU亲和设置来限制SQL Server可以使用的CPU。SQL Server内部的配置,你可以通过affinity mask和affinity64 mask配置选项指定SQL Server仅使用特定的CPU。这两个配置选项可以动态改变,对与CPU关联的调度者立即产生影响。你也可以使用affinity I/O mask选项来设置SQL Server的I/O亲和。这个选项可以让你强制任何I/O关联的活动仅在指定的CPU集合上运行。使用连接亲和(软NUMA),你可以把网络连接亲和到特定的内存节点。
调度器
调度器节点是调度活动的地方,调度针对任务发生。SQL Server启动时,会为每个CPU创建一个调度器,以及一些运行其他系统任务的额外调度器。如果处理器亲和设置的一些CPU对于实例是未启用的,那么与这些CPU关联的调度器将被设置为禁用状态。这使得SQL Server支持动态亲和设置。虽然每个CPU有一个调度器,但调度器不会绑定到一个特定的CPU,除了设置了CPU亲和。每个调度器由唯一的scheduler_id标识,0~254是为运行用户请求的调度器预留的,255是为DAC调度器预留的(加注:书中介绍有误,如下图所示,并非都是255),scheduler_id > 255的调度器是为系统预留的,常被分配给同一任务。下面的SQL显示了DMV sys.dm_os_schedulers的一些列的信息:
select parent_node_id, scheduler_id, cpu_id, status, scheduler_addressfrom sys.dm_os_schedulersorder by scheduler_id
任务
任务是完成一个工作单元的请求。任务本身不做任何事情,它仅仅是一个要完成的工作单元的容器。要实际做事,任务必须被一个调度器调度,并关联一个特定的工作者(worker),工作者才会实际做事。你可以通过sys.dm_os_tasks查看任务。要想精确地查看工作内容,可以使用如下SQL:
Select t.task_address, s.text From sys.dm_os_tasks as t inner join sys.dm_exec_requests as r on t.task_address = r.task_address Cross apply sys.dm_exec_sql_text (r.plan_handle) as s where r.plan_handle is not null
工作者
工作者是实际完成工作的地方,要做的工作包含在任务里。可通过DMV sys.dm_os_workers看到工作者。
线程
SQLOS还包括OS线程,可通过DMV sys.dm_os_threads查看。
调度
SQL Server使用非抢先调度模式,它会标记任务来表明该任务需要抢先被调度。被标记为抢先调度的代码不是由SQL Sever编写的,SQL Server编写的代码运行在SQL Server进程内,因此这会应用于CLR代码。查询优化器找到最佳计划后,SQL Server收到一个新请求时就会开始调度一个任务。为了这个用户请求,会创建一个任务对象,调度将从从那里开始。新创建的任务对象必须关联一个可用的工作者以便实际做事。当工作者关联新任务时,工作者的状态被设置为init。完成初始化设置后,状态会更改为runnable。有了可用的调度器后,工作者会关联到那个调度器,状态变为running。它会保持运行直到完成或释放控制。当它释放调度者的控制时,状态会变成suspended,释放的原因会记录成一个wait_type,当正在等待的项再次可用时,工作者的状态被改为runnable。现在,它返回再次等待可用调度器,循环不断重复直到任务完成。那时,任务被释放,工作者被释放,调度器可用于关联下一个需要运行的工作者。调度工作者的状态图如图5-19所示: