zoukankan      html  css  js  c++  java
  • SQLSERVER独特的任务调度算法"SQLOS"

     SQLSERVER独特的任务调度算法"SQLOS"

    微软开发SQLOS的背景:

    SQLSERVER作为一个企业级数据库平台,一个基本的要求就是要有能力顺畅地同时处理成百上千的用户请求,SQLSERVER要使线程调度

    得更加适应高并发的数据库应用。

    由于以上背景:
    SQLSERVER在Windows的基础上开发出了一套自己的任务调度机制。所以SQLSERVER作为一个应用程序,又抽象出一般由操作系统代为

    管理的功能,例如:
    任务调度管理子系统
    内存管理
    错误,异常处理机制
    死锁侦测和解决机制
    运行第三方代码(dll,extended SP等)机制

    SQLSERVER的管理功能组件又叫SQLOS SQL OPERATING SYSTEM ,而内存管理和任务调度管理是SQLOS的两大核心内容

    对于SQLSERVER来讲,除了从DAC dedicated administrator connection过来的连接,其他用户连接对SQL来讲都是同等重要的,

    而这样的连接在同一个时间点,可能会有成百上千。 所以SQLSERVER完全依赖Windows任务调度是不行的。

    SQLSERVER的这一套任务调度机制的特点:

    1、只有需要运行任务的连接才会被分配线程。出于空闲状态的连接,在SQLSERVER里会以一组数据结构表示,所以不会占用线程资源。大大降低

    SQLSERVER进程需要的线程数目

    2、对于每一个CPU,SQLSERVER内部会有一个调度(scheduler),由这个scheduler决定在某个时间点,到底是哪个SQLSERVER线程去运行。

    所以在Windows层面,每个CPU最多只会对应一个处于运行状态的线程。大大降低Windows层面的上下文切换context switch

    实践证明:很多有着1000~2000个并发用户的SQLSERVER,线程数也只需要一两百个。SQLSERVER完成的批处理量每秒钟可以达到3000~4000个。

    SQLOS的几个概念:

     scheduler

    对于每个逻辑CPU,SQLSERVER会有一个scheduler与之对应,在SQL层面上代表CPU对象,只有拿到scheduler所有权的任务worker才能在这个逻辑CPU上运行

    所谓逻辑CPU,就是SQLSERVER从Windows层面上看到的CPU数目,如果是一个双核的CPU,那么一个物理CPU在SQL看来就是两个逻辑CPU。如果系统还使用了

    超线程hyper-threaded ,那对SQLSERVER来讲就是4个逻辑CPU

    规则: 每个scheduler上的最大worker数目等于SQLSERVER的最大线程数除以scheduler的数目 ,在同一个时间点,只能有一个拥有scheduler的worker处于运行

    状态,其他worker都必须处于等待状态。这样能降低每个逻辑CPU上的处于正在运行状态的线程数目,降低context switch,提供可扩展性

    scheduler是SQLSERVER的一个逻辑概念,他不与物理CPU相绑定。也就是说,一个scheduler可以被Windows安排一会儿在这个CPU上,一会儿在那个CPU上。

    但是,如果在sp_configure里设置了CPU affinity mask,那么scheduler就会固定在某个特定的CPU上

    worker

    每个worker跟一个线程(或纤程fiber)相对应,是SQLSERVER任务的执行单位。SQLSERVER不直接调度线程/纤程,而是调度worker,使得SQLSERVER能够控制

    任务调度

    规则: 每个worker会固定代表一个线程(或纤程),并且和一个scheduler相绑定。如果scheduler是固定在某个CPU上的(通过设置CPU affinity mask),那么

    worker也会固定在某个CPU上

    每个scheduler有worker的上限值,并且可以根据SQLSERVER工作负荷创建或释放worker,每次worker都会去运行一个完整的任务(task)。在任务做完之前不会

    退出,除非这个任务主动进入等待状态。

    scheduler只在有新任务要运行,而当前没有空闲的worker的情况下,才会创建新的worker。

    某个worker空闲超过15分钟,scheduler可能会删除这个worker,以及其对应的线程。当SQLSERVER遇到内存压力的时,也会大量删除处于空闲状态的worker,以

    节省multi-page的内存开销

    各种CPU和SQLSERVER版本组合自动配置的最大工作线程数
    CPU数                 32位计算机                        64位计算机
    <=4                     256                                  512
    8                          288                                  576
    16                        352                                  704
    32                        480                                  960

    task

    在worker上运行的最小任务单元。最简单的task就是一个简单batch。例如,客户发过来下面的请求:

    1 SELECT @@SERVERNAME 
    2 GO
    3 SELECT GETDATE() 
    4 GO 

    那么这两个batch就分别是两个task。SQLSERVER会先分配给第一个batch(select @@servername)一个worker,将结果返回给客户端,再分配第二个batch

    (select getdate())一个worker。这两个worker可能是不同的worker,甚至在不同的scheduler上

    只要一个task开始运行,他就不会从这个worker上被移出。例如,如果一个select语句被其他连接阻塞住,worker就不能继续运行,只能进入等待状态。但是这个

    select task 不会将这个worker释放,让他做其他任务。所以结果是这个worker所对应的线程会进入等待状态

    yielding

     SQLOS的任务调度算法的核心,就是所有在逻辑scheduler上运行的worker都是非抢占式的 (non-preemptive)。worker始终在scheduler上运行,直到他运行结束,或者主动将scheduler让出给其他worker为止。这个“让出”scheduler的动作,我们叫yieding

    每个scheduler都会有一个runnable列表,所有等待CPU运行的worker都会在这个列表里排队,以先进先出的算法,等待SQL分配给他scheduler运行

    SQLSERVER定义了很多yieding的规则,约束一个task在scheduler运行的时间。如果task比较复杂,不能很快完成,会保证task在合适的时间点做yieding,不至于占用scheduler太多时间。

    常见时间点:

    1、当worker每次要去读数据页的时候,SQLSERVER会检查这个worker已经在scheduler上运行了多久,如果已经超过4ms,就做yielding

    2、每做64KB的结果集排序,就会做一次yielding

    3、在做语句编译compile的过程中(这个过程比较占CPU资源),经常会有yieding

    4、如果客户端不能及时把结果集取走,worker就会做yieding

    5、一个batch里的每一句话做完,都会做一次yieding

    正常来讲,哪怕一个task要做很久,他使用的worker是会经常做yieding的,不会长时间占用CPU不放。如果在一个scheduler上同时有很多worker要运行,

    SQLSERVER通过worker自动yielding的方式调度并发运行。这个比Windows用上下文切换context switch更有效

     下面用图来说明SQLOS的任务调度算法:

    总结:

    对于每个CPU,SQLSERVER都会有一个scheduler与之对应。在每个scheduler里,会有若干个worker,对应
    于每个线程。在客户端发过来请求之后,SQL会将其分解成一个或多个task。根据每个scheduler的繁忙程度,
    task会被分配到某个scheduler上。如果scheduler里有空闲的worker,task就会被分配到某个worker上。
    如果没有,scheduler会创建新的worker,供task使用。如果scheduler里的worker已经到了他的上限值,
    而他们都有task要运行,那么新的task只好进入等待worker的状态

    使用下面两个SQL语句可以查看当前SQLSERVER有多少个workers在工作,有多少个tasks在运行

    1 SELECT * FROM sys.dm_os_workers
    2 SELECT * FROM  sys.dm_os_tasks

    ----------------------------------------------------------华丽的分割线---------------------------------------------------------

    说一下上面的图的含义

    SELECT * FROM sys.dm_os_workers

    其他列的解释大家可以看一下MSDN

    http://msdn.microsoft.com/zh-cn/library/ms178626(v=SQL.105).aspx

    ------------------------------------------------------------------------------------------------------------

    SELECT * FROM  sys.dm_os_tasks

    任务的状态可以是下列选项之一

    PENDING:正在等待工作线程。

    RUNNABLE:可运行,但正在等待接收量程。

    RUNNING:当前正在计划程序中运行。

    SUSPENDED:具有工作线程,但正在等待事件。

    DONE:已完成。

    SPINLOOP:陷入自旋锁

    MSDN的解释:http://msdn.microsoft.com/zh-cn/library/ms174963(v=SQL.105).aspx

    久没有写文章了,希望大家看了我的文章能够更加深入了解SQLSERVER

    要睡了,熬不住了~

    ----------------------------------------------------------

    2013-6-3补充

    附网上流传的手稿图一张

    2014-2-2补充:

    SQLOS所用的DLL

  • 相关阅读:
    免费素材下载:淡蓝色的PSD格式UI套件
    分享一个CSS3的网格系统架构 ResponsiveAeon
    最新收集的超棒Mobile/Web UI和用户体验设计
    一个帮助你针对不同标签自动填入内容的轻量级javascript类库 fixiejs
    发现任何VB函数、插件、甚至按键精灵对“文件下载”窗口后台失效
    android 界面 滑入 效果
    分布式HeadLoop
    VB ListView
    android 下载保存图片
    网址
  • 原文地址:https://www.cnblogs.com/lyhabc/p/2728724.html
Copyright © 2011-2022 走看看