本文属于SQL Server T-SQL执行内幕系列

前言:

本文主体内容来自于:http://rusanu.com/2013/08/01/understanding-how-sql-server-executes-a-query/但是经常打不开,本人又在:https://www.codeproject.com/Articles/630346/Understanding-how-SQL-Server-executes-a-query 发现几乎跟原文一模一样的内容,并且网上大多收录这篇而不是原文。 不过提醒一下第二篇文章中对原文进行了少量的删减。

一开始想直译,但是看了内容之后,觉得略微有点难读,加上自己对这方面也有点想法,所以打算把它作为一个引子,在不影响作者内容跟信息准确性的前提下添加自己的看法。所以如果读者看原文的话,会发现跟原文并不完全一样。

本文不适合初学者看,除非你的学习能力很好,但是不代表所谓的初学者不能看,我只是给个忠告而已,因为内容较深入,可能会影响兴趣。

目录:

本系列内容较多,原文本身已经很多,加上个人添加的内容之后,篇幅过长,不便于阅读,过长的篇幅也容易造成阅读疲劳,所以我把它们拆分:

  • T-SQL执行内幕(1)——简介
  • T-SQL执行内幕(2)——Tasks、Workers、Threads、Scheduler、Sessions、Connections、Requests
  • T-SQL执行内幕(3)——解析和编译
  • T-SQL执行内幕(4)——优化
  • T-SQL执行内幕(5)——执行
  • T-SQL执行内幕(6)——返回结果
  • T-SQL执行内幕(7)——内存授予
  • T-SQL执行内幕(8)——数据存储
  • T-SQL执行内幕(9)——数据访问
  • T-SQL执行内幕(10)——读取数据
  • T-SQL执行内幕(11)——Read Ahead
  • T-SQL执行内幕(12)——锁、闩锁
  • T-SQL执行内幕(13)——写数据
  • T-SQL执行内幕(14)——DDL
  • T-SQL执行内幕(15)——备份、还原和DBCC
  • T-SQL执行内幕(16)——总结

SQL Server数据库引擎有两大核心:存储引擎(Storage Engine)和查询处理器(Query Processor),也叫关系引擎(Relational Engine)

  • 存储引擎:负责在磁盘与内存之间以某种方式读取数据。并且在这个过程中维护数据一致性及并发性。
  • 查询处理器/关系引擎:接受提交到SQL Server的所有查询,并为其选择最佳执行计划,然后把执行计划执行最后返回结果。

语句以T-SQL形式提交,而T-SQL是高级声明性语言,只告诉数据库引擎“要做什么”,但是不会告诉它“要怎么做”,而怎么做这一步,就是查询处理器中的查询优化器的工作。下面来详解。    

正文:

如果你是一个普通数据库开发人员,那了解增删改查的T-SQL写法基本上就能开始工作了,但是随着时间的推移和外界的不断变化,除非你转到别的领域,不然迟早你要面对这个问题——为什么我的SQL慢?这个问题衍生出另外三个有意思的问题:

  1. 怎样发现慢查询或者高开销查询?(Troubleshooting)
  2. 怎样改进(PerformanceTuning)
  3. 监控效果(Monitoring)

之所以说这些是“问题”,是因为大部分的人包括我自己在很多时候都知道要做这些,但是怎么做?要回答这些越来越深入的问题,首先必须先了解根源——TSQL的运行内幕。了解T-SQL是如何运行的,那么大部分的语句及其相关问题都能从中得到或多或少的启发。才能更好地回答“为什么我的数据库性能差”。

但是篇幅原因,本系列只是主要介绍SQLServer 的执行机制。闲话不说,先上图:

上图是一个T-SQL从发起到得到结果的流程示意图(最左下角是发起,最右下角是返回)。下面对一些后续要用到的术语和这个图进行简要介绍。图中有几个重要术语:请求(request)、任务(task)、工作线程(worker),下面将一一简介。如果觉得概念比较乱,可以直接先跳到T-SQL执行内幕(2)——Tasks、Workers、Threads、Scheduler、Sessions、Connections、Requests一节。

Requests

请求,SQL Server是一个客户端-服务器平台,与数据库交互的唯一方式就是通过发送包含有命令的请求给数据库。而客户端与服务器直接的通讯协议称为TDS(Tabular Data Stream),如果读者有兴趣可以看一下MSDN的文档:https://msdn.microsoft.com/en-us/library/dd304523.aspx,服务器的请求列表可见sys.dm_exec_requests 。

应用程序使用诸如SqlClient、OleDB、ODBC、JDBC等驱动来实现这种协议。当应用程序需要数据库完成任何事情时,都会通过TDS协议发送一个请求(request)给数据库引擎。

简单来说,每次对数据库的操作都会以“请求”的形式发送给数据库服务器。所以请求是T-SQL生命周期的开始。上图左下角绿色块。

请求的主要分类有以下三类:Batch Request、RPC Request、Bulk Load Request

Batch Request:

批请求,即一个请求中包含了一个或多个T-SQL文本(语句)。这种类型无参数,但可以包含本地变量。通常由SqlCommand.ExecuteReader()/ExecuteNonQuery()/ExecuteScalar()/ExecuteXmlReader ()等SqlCommand类并且参数列表为空的对象从客户端发起。

另外我们最常用的在SSMS中输入一些普通SQL语句并执行的方式也是批请求。过去监控Long Running Query的时候常用的Profiler/SQL Trace工具,如果要抓批请求所使用的语句,需要选择SQL:BatchStarting事件。这是批请求开始时的语句,也可以把SQL:BatchCompleted事件也包含进去以便计算运行时间。

除此之外,Batch 对应的处理速度(可以在性能计数器中找到SQLServer: SQL Statistics: Batch Requests/Sec),也从侧面看出整个系统的性能,是很重要的指标,不过不要单方面下定论,这个指标并不能单纯指出问题,通常只能直接得知系统的“繁忙程度”而已。我不想在一开始就过于深入地讨论如何优化,先沉下心来把原理搞懂了,很多问题就自然有了解决方法。

Remote Procedure Call Request:

简称RCP请求,这种类型的请求包含任意数量参数的需要执行的RPC名字或Procedure ID。比如sp_execute,第一个参数是一段要执行的T-SQL文本。每个RPC是独立的,不能混在其他SQL语句中。

最常见的RPC请求就是在客户端调用带有参数的存储过程。另外一个不严谨的区分就是RPC通常是外部应用(如Windows 服务、Web Services等发出的请求)。对这种请求的监控可以使用Profiler/Trace的RPC:Starting事件。我们可以从下图的所属类别大概看出,RPC事件归到存储过程事件中,而Batch归到TSQL事件中,但是我觉得这个并不需要过多关注,除非你想做更深入的研究:

Bulk Load Request:

Bulkload是一个特殊的请求,专用于bulk insert操作,比如使用bcp.exe或者SqlBulkcopy类进行大容量导入。跟前面两种请求不同,它是唯一一种“先执行再通过TDS协议传输请求”的请求。

小结

当请求通过TDS到达服务器之后,SQL Server会创建一个任务(Task)来处理请求,可以简单理解为,当request到达SQL Server之后,后续操作都发生在SQL Server内部。从执行流程来说,现在已经是:Requests→TDS→Tasks

Tasks

任务,上面说过,task(任务)是请求从开始到完成的表现方式。task可以有子任务(subtask),如果有多个task,SQL Server会使用task queue(任务队列)来存放任务列表。如果请求是SQL 批,则task为整个批而不是单独语句。在SQL批中的单独语句不创建新的任务,除非语句(不是指批)以并行方式执行时会产生子任务来并行执行。一旦请求返回结果并且客户端进行数据处理完毕(如使用SqlDataReader读取数据),请求就算完成。

服务器的任务列表可见sys.dm_os_tasks。如果需要了解会话ID对应的Windows线程ID,可以使用下面语句查看,得到线程ID之后,使用Windows性能监视器来监视线程的性能。这个在查询一些服务是否有性能问题时比较有用,但是这个语句不返回正处于sleeping状态的会话:

SELECT STasks.session_id, SThreads.os_thread_id  FROM sys.dm_os_tasks AS STasks  INNER JOIN sys.dm_os_threads AS SThreads  ON STasks.worker_address = SThreads.worker_address  WHERE STasks.session_id IS NOT NULL  ORDER BY STasks.session_id;
GO

当一个新请求到达服务器并且创建一个对应的任务时,首先会处于PENDING(挂起状态)。任务状态可以有:

  • PENDING:正在等待工作线程,worker。
  • RUNNABLE:可运行但正在等待接收量程。
  • RUNNING:当前正在Scheduler中运行。
  • SUSPENDED:拥有worker但是正在等待某些事件。sys.dm_os_schedulers
  • DONE:已经完成。
  • SPINLOOP:陷入自旋锁。

在这一阶段,SQL Server无法得知请求的具体内容,为了执行任务,SQL Server需要指派一个Worker用于服务这个任务。到这一步,执行流程就到了:Requests→TDS→Tasks→Workers。

Workers

工作线程,Worker是SQL Server线程池(Thread pool),当服务器启动时,根据SQL Server的max_worker_threads(下图红圈部分)按需创建。只有worker能执行代码,并且空闲的Workers会等待挂起(pending)的任务变成可用(Runnable)之后,worker才执行某个任务。从worker开始执行任务到任务完成之前,这个worker处于busy状态。当没有可用的worker时,任务(task)就会变成pending状态等待有空闲的worker执行它为止。

对于工作线程数,默认值为0,意味着自动配置,对绝大多数系统而言是最佳设置,但是并不代表永远最佳,一般情况下,每个查询会创建一个单独的操作系统线程来服务请求(1:1),但是如果服务器的连接达到数以百计的时候,为每个请求分配一个线程会占用大量的系统资源,此时这个配置可以使SQL Server可以为更多的查询请求创建一个工作线程(N:1),从而提高性能。官方建议如下:

CPU 数

32 位计算机

64 位计算机

<= 4 个处理器

256

512

8 个处理器

288

576

16 个处理器

352

704

32 个处理器

480

960

当实际查询请求数量小于这个最大工作线程数时,会使用1:1的方式,如果超过最大线程数时,SQL Server就会把工作线程集中在线程池(Thread Pool)中进行有效调度。

但是注意如果所有的Worker都处于活动状态,SQL Server可能出现停止响应的状态,直到有工作线程可用为止。此时需要使用专用管理员连接(Dedicated Administrator Connection,DAC)连到SQL Server进行kill操作。

对于SQL 批请求,worker会执行整个批而不是单独的SQL。这就引出一个问题“SQL批中的语句能否并行执行?”答案是否定的,因为他们是串行执行,每个Thread(worker)每次只执行批中的一个SQL,完成后继续执行批里面的下一个SQL。对于那些使用并行执行(DOP配置要>1)的语句,会创建子任务,每个子任务使用相同的方式执行:创建一个PENDING的子任务然后等待worker(由于执行这个批的worker已经标注为占用/繁忙,所以这里的worker是另外一个不同的worker)执行。Worker的清单可以查询DMV: sys.dm_os_workers。换句话说,对于一个并行执行语句,会有多个Worker来协助批处理执行,但是也是串行执行。

当分配了Worker之后,执行流程就到了:Requests→TDS→Tasks→Workers→编译/优化

小结:

本节作为本系列的开篇,介绍了一些基础概念,主要集中在从客户端发起请求到SQL Server的过程,下一节将介绍Tasks、Workers、Threads、Scheduler、Sessions、Connections、Requests这几个概念,以便更好地深入学习。

T-SQL执行内幕(1)——简介相关推荐

  1. mybatis delete返回值_面试:谈谈你对MyBatis执行过程之SQL执行过程理解

    前言 在了解了MyBatis初始化加载过程后,我们也应该研究看看SQL执行过程是怎样执行?这样我们对于Mybatis的整个执行流程都熟悉了,在开发遇到问题也可以很快定位到问题. 更重要的,在面试中遇到 ...

  2. oracle sql 执行计划分析_Oracle SQL调优系列之看懂执行计划explain

    1.文章写作前言简介 SQL调优系列博客链接:SQL调优专栏 之前曾经拜读过<收获,不止sql调优>一书,此书是国内DBA写的一本很不错的调优类型的书,是一些很不错的调优经验的分享.虽然读 ...

  3. mysql 执行计划extra_SQL优化 MySQL版 -分析explain SQL执行计划与Extra

    Extra 作者 : Stanley 罗昊 [转载请注明出处和署名,谢谢!] 注:此文章必须有一定的Mysql基础,或观看执行计划入门篇传送门: https://www.cnblogs.com/Sta ...

  4. 如何测试sql服务器的性能测试,SQL执行效率和性能测试方法

    对于做管理系统和分析系统的程序员,复杂SQL语句是不可避免的,面对海量数据,有时候经过优化的某一条语句,可以提高执行效率和整体运行性能.如何选择SQL语句,本文提供了两种方法,分别对多条SQL进行量化 ...

  5. SQL执行过程中的性能负载点

    一.SQL执行过程 1.用户连接数据库,执行SQL语句: 2.先在内存进行内存读,找到了所需数据就直接交给用户工作空间: 3.内存读失败,也就说在内存中没找到支持SQL所需数据,就进行物理读,也就是到 ...

  6. 一阵骚操作,我把SQL执行效率提高了10000000倍!

    点击上方"方志朋",选择"设为星标" 做积极的人,而不是积极废人 作者:风过无痕-唐 http://www.cnblogs.com/tangyanbo/p/44 ...

  7. Oracle查看SQL执行计划的方式

    Oracle查看SQL执行计划的方式 获取Oracle sql执行计划并查看执行计划,是掌握和判断数据库性能的基本技巧.下面案例介绍了多种查看sql执行计划的方式: 基本有以下几种方式: 1.通过sq ...

  8. 一通骚操作,我把SQL执行效率提高了10000000倍!

    作者:风过无痕-唐 http://www.cnblogs.com/tangyanbo/p/4462734.html 场景 我用的数据库是mysql5.6,下面简单的介绍下场景 课程表: create ...

  9. 一条SQL要28秒 mysql_一条简单的 SQL 执行超过 1000ms,纳尼?

    阅读本文大概需要 2.8 分钟. MySQL 对我说 "Too young, too naive!" ▌大概过程 在测试环境 Docker 容器中,在跨进程调用服务的时候,A 应用 ...

最新文章

  1. java web 手机验证_javaWeb发送手机短信验证码(云通讯)
  2. mac VMware Fusion 虚拟机键盘可以使用,鼠标无法使用排查思路及解决方法
  3. python3 lambda函数字典排序_排序字典表理解中的lambda函数
  4. php 禁用通知,推送消息能不能区分禁止通知和卸载两种类型?
  5. 一台微型计算机性能的主要参数及其意义,微型计算机的主要性能指标.ppt
  6. Linux C 多线程编程----互斥锁与条件变量-转
  7. JPA与EJB3的关系
  8. WePhone开发者被逼自杀案续,在翟欣欣身上的各种消费清单明细及细节曝光
  9. Knowledge Representation笔记
  10. 2022-3-26JavaSE试卷-答案
  11. 领取1024节日勋章(程序员必看)
  12. 杰理之音箱版本 SDK200 单音 VCOMO 直推,喇叭没声音问题【篇】
  13. 100+大数据开源处理工具汇总
  14. dockerfile构建一个(python+flask+html)镜像 + 上传到阿里云私有仓库 + 部署到k8s---全过程
  15. 自动驾驶路径规划:A*(Astar)算法
  16. 【电力电子】【2011.08】通过谐波电流注入改善三相整流器输入功率因数
  17. 什么是数组,数组的定义,数组的遍历
  18. @Transactional的介绍和使用
  19. Python urlparse总结
  20. 地面站安装前固件调试_PixHawk飞控和Mission Planner地面站安装调试

热门文章

  1. 华微电子否认与奇虎360有“绯闻”
  2. 微软活动目录的物理结构
  3. IT技术视频分享,直接获取
  4. 安装完waves11之后电脑无声音了
  5. Mysql密码修改无效问题
  6. 亚马逊 SEO 的排名因素
  7. 专访微软研究院俞栋:基于深度学习的语音识别及CNTK的演进
  8. 二手小米手机价格表图片(2022.2.21)
  9. 【AD笔记】--原理图原理图库原理图
  10. 构造/析构/赋值运算--龙之介《Effective C++》实验室