0

0

SQLSERVER读懂语句运行的统计信息

php中文网

php中文网

发布时间:2016-06-07 15:44:36

|

1254人浏览过

|

来源于php中文网

原创

SQLSERVER读懂语句运行的统计信息 对于语句的运行,除了执行计划本身,还有一些其他因素要考虑,例如语句的编译时间、执行时间、做了多少次磁盘读等。 如果DBA能够把问题语句单独测试运行,可以在运行前打开下面这三个开关,收集语句运行的统计信息。 这些信

SQLSERVER读懂语句运行的统计信息

对于语句的运行,除了执行计划本身,还有一些其他因素要考虑,例如语句的编译时间、执行时间、做了多少次磁盘读等。

如果DBA能够把问题语句单独测试运行,可以在运行前打开下面这三个开关,收集语句运行的统计信息。

这些信息对分析问题很有价值。

<span>1</span> <span>SET</span> <span>STATISTICS</span> TIME <span>ON</span>
<span>2</span> <span>SET</span> <span>STATISTICS</span> IO <span>ON</span>
<span>3</span> <span>SET</span> <span>STATISTICS</span> PROFILE <span>ON</span>

 

SET STATISTICS TIME ON



请先来看看SET STATISTICS TIME ON会返回什么信息。先运行语句:

<span> 1</span> <span>DBCC</span><span> DROPCLEANBUFFERS
</span><span> 2</span> <span>--</span><span>清除buffer pool里的所有缓存数据</span>
<span> 3</span> <span>DBCC</span><span> freeproccache
</span><span> 4</span> <span>GO</span>
<span> 5</span> 
<span> 6</span> <span>--</span><span>清除buffer pool里的所有缓存的执行计划</span>
<span> 7</span> <span>SET</span> <span>STATISTICS</span> TIME <span>ON</span>
<span> 8</span> <span>GO</span>
<span> 9</span> <span>USE</span> <span>[</span><span>AdventureWorks</span><span>]</span>
<span>10</span> <span>GO</span>
<span>11</span> <span>SELECT</span> <span>DISTINCT</span>(<span>[</span><span>ProductID</span><span>]</span>),<span>[</span><span>UnitPrice</span><span>]</span> <span>FROM</span> <span>[</span><span>dbo</span><span>]</span>.<span>[</span><span>SalesOrderDetail_test</span><span>]</span>
<span>12</span> <span>WHERE</span> <span>[</span><span>ProductID</span><span>]</span><span>=</span><span>777</span>
<span>13</span> <span>GO</span>
<span>14</span> <span>SET</span> <span>STATISTICS</span> TIME <span>OFF</span>
<span>15</span> <span>GO</span>

 

除了结果集之外,SQLSERVER还会返回下面这两段信息

<span> 1</span> <span>SQL Server 分析和编译时间: 
</span><span> 2</span>    CPU 时间 <span>=</span> <span>15</span> 毫秒,占用时间 <span>=</span> <span>104</span><span> 毫秒。
</span><span> 3</span> <span>SQL Server 分析和编译时间: 
</span><span> 4</span>    CPU 时间 <span>=</span> <span>0</span> 毫秒,占用时间 <span>=</span> <span>0</span><span> 毫秒。
</span><span> 5</span> 
<span> 6</span> (<span>4</span><span> 行受影响)
</span><span> 7</span> 
<span> 8</span> <span>SQL Server 执行时间:
</span><span> 9</span>    CPU 时间 <span>=</span> <span>171</span> 毫秒,占用时间 <span>=</span> <span>1903</span><span> 毫秒。
</span><span>10</span> <span>SQL Server 分析和编译时间: 
</span><span>11</span>    CPU 时间 <span>=</span> <span>0</span> 毫秒,占用时间 <span>=</span> <span>0</span> 毫秒。

大家知道SQLSERVER执行语句是分以下阶段:分析-》编译-》执行

根据表格的统计信息分析出比较合适的执行计划,然后编译语句,最后执行语句

 

下面说一下上面的输出是什么意思:


1、CPU时间 :这个值的含义指的是在这一步,SQLSERVER所花的纯CPU时间是多少。也就是说,语句花了多少CPU资源

2、占用时间 :此值指这一步一共用了多少时间。也就是说,这是语句运行的时间长短,有些动作会发生I/O操作,产生了I/O等待,

或者是遇到阻塞、产生了阻塞等待。总之时间用掉了,但是没有用CPU资源。所以占用时间比CPU时间长是很正常的 ,但是CPU时间是

语句在所有CPU上的时间总和。如果语句使用了多颗CPU,而其他等待几乎没有,那么CPU时间大于占用时间也是正常的

3、分析和编译时间:这一步,就是语句的编译时间。由于语句运行之前清空了所有执行计划,SQLSERVER必须要对他编译。

这里的编译时间就不为0了。由于编译主要是CPU的运算,所以一般CPU时间和占用时间是差不多的。如果这里相差比较大,

就有必要看看SQLSERVER在系统资源上有没有瓶颈了。

这里他们是一个15毫秒,一个是104毫秒

4、SQLSERVER执行时间: 语句真正运行的时间。由于语句是第一次运行,SQLSERVER需要把数据从磁盘读到内存里,这里语句的

运行发生了比较长的I/O等待。所以这里的CPU时间和占用时间差别就很大了,一个是171毫秒,而另一个是1903毫秒

总的来讲,这条语句花了104+1903+186=2193毫秒,其中CPU时间为15+171=186毫秒。语句的主要时间应该是都花在了I/O等待上

 

现在再做一遍语句,但是不清除任何缓存

<span>1</span> <span>SET</span> <span>STATISTICS</span> TIME <span>ON</span>
<span>2</span> <span>GO</span>
<span>3</span> 
<span>4</span> <span>SELECT</span> <span>DISTINCT</span>(<span>[</span><span>ProductID</span><span>]</span>),<span>[</span><span>UnitPrice</span><span>]</span> <span>FROM</span> <span>[</span><span>dbo</span><span>]</span>.<span>[</span><span>SalesOrderDetail_test</span><span>]</span>
<span>5</span> <span>WHERE</span> <span>[</span><span>ProductID</span><span>]</span><span>=</span><span>777</span>
<span>6</span> 
<span>7</span> <span>GO</span>
<span>8</span> <span>SET</span> <span>STATISTICS</span> TIME <span>OFF</span>
<span>9</span> <span>GO</span>

这次比上次快很多。输出时间统计信息是:

<span> 1</span> <span>SQL Server 分析和编译时间: 
</span><span> 2</span>    CPU 时间 <span>=</span> <span>0</span> 毫秒,占用时间 <span>=</span> <span>0</span><span> 毫秒。
</span><span> 3</span> <span>SQL Server 分析和编译时间: 
</span><span> 4</span>    CPU 时间 <span>=</span> <span>0</span> 毫秒,占用时间 <span>=</span> <span>0</span><span> 毫秒。
</span><span> 5</span> 
<span> 6</span> (<span>4</span><span> 行受影响)
</span><span> 7</span> 
<span> 8</span> <span>SQL Server 执行时间:
</span><span> 9</span>    CPU 时间 <span>=</span> <span>156</span> 毫秒,占用时间 <span>=</span> <span>169</span><span> 毫秒。
</span><span>10</span> <span>SQL Server 分析和编译时间: 
</span><span>11</span>    CPU 时间 <span>=</span> <span>0</span> 毫秒,占用时间 <span>=</span> <span>0</span> 毫秒。

由于执行计划被重用,“SQL分析和编译时间” CPU时间是0,占用时间是0

由于数据已经缓存在内存里,不需要从磁盘上读取,SQL执行时间 CPU时间是156,占用时间这次和CPU时间非常接近,是169。

这里省下运行时间1903-169=1734毫秒,从这里可以再次看出,缓存对语句执行性能起着至关重要的作用

为了不影响其他测试,请运行下面的语句关闭SET STATISTICS TIME ON

<span>1</span> <span>SET</span> <span>STATISTICS</span> TIME <span>OFF</span>
<span>2</span> <span>GO</span>

 

 

 

SET STATISTICS IO ON


这个开关能够输出语句做的物理读和逻辑读的数目。对分析语句的复杂度有很重要的作用

还是以刚才那个查询作为例子

<span>1</span> <span>DBCC</span><span> DROPCLEANBUFFERS
</span><span>2</span> <span>GO</span>
<span>3</span> <span>SET</span> <span>STATISTICS</span> IO <span>ON</span>
<span>4</span> <span>GO</span>
<span>5</span> 
<span>6</span> <span>SELECT</span> <span>DISTINCT</span>(<span>[</span><span>ProductID</span><span>]</span>),<span>[</span><span>UnitPrice</span><span>]</span> <span>FROM</span> <span>[</span><span>dbo</span><span>]</span>.<span>[</span><span>SalesOrderDetail_test</span><span>]</span>
<span>7</span> <span>WHERE</span> <span>[</span><span>ProductID</span><span>]</span><span>=</span><span>777</span>
<span>8</span> <span>GO</span>

他的返回是:

<span>1</span> (<span>4</span><span> 行受影响)
</span><span>2</span> 表 <span>'</span><span>SalesOrderDetail_test</span><span>'</span>。扫描计数 <span>5</span>,逻辑读取 <span>15064</span> 次,物理读取 <span>0</span> 次,预读 <span>15064</span> 次,lob 逻辑读取 <span>0</span> 次,lob 物理读取 <span>0</span> 次,lob 预读 <span>0</span> 次。

各个输出的含义是:


:表的名称。这里的表就是SalesOrderDetail_test

 

扫描计数:执行的扫描次数。按照执行计划,表格被扫描了几次。一般来讲大表扫描的次数越多越不好。唯一的例外是如果执行计划选择了并发运行,

由多个thread线程同时做一个表的读取,每个thread读其中的一部分,但是这里会显示所有thread的数目。也就是有几个thread在并发做,

就会有几个扫描。这时数目大一点没问题的。

 

逻辑读取:从数据缓存读取的页数。页数越多,说明查询要访问的数据量就越大,内存消耗量越大,查询也就越昂贵。

可以检查是否应该调整索引,减少扫描的次数,缩小扫描范围

顺便说一下这个逻辑读取的统计原理:为什麽显示出来的结果的单位不是Page,也不是K或KB。SQLSERVER

里在做读和写的时候,会运行到某一段特定的代码。每调用一次这个代码,Reads/Write就会加1。所以这个值比较大

那语句一定做了比较多的I/O,但是不能通过这个值计算出I/O的绝对数量,这个值反映的是逻辑读写量不是物理读写量

<span>1</span> 逻辑读取 <span>15064</span> 次

 

 

物理读取:从磁盘读取的页数

 

预读:为进行查询而预读入缓存的页数

 

物理读取+预读:就是SQLSERVER为了完成这句查询而从磁盘上读取的页数。如果不为0,说明数据没有缓存在内存里。运行速度一定会受到影响

 

LOB逻辑读取:从数据缓存读取的text、ntext、image、大值类型(varchar(max)、nvarchar(max)、varbinary(max))页的数目

千博企业网站管理系统免费开源版2010 Build 0418
千博企业网站管理系统免费开源版2010 Build 0418

千博企业网站管理系统主要面向大中型企业电子商务网站的构建与运营管理进行设计研发,拥有极为灵活的产品架构、极强的可扩展性与可伸缩性,可广泛适合于新闻资讯门户、企业内部知识门户、报社/杂志阅读、影音资讯、视频音频在线播放、法律顾问、政务公开、企业办公信息化等网络业务管理平台的建设,最大限度地满足客户现今乃至未来的应用需求。借助于千博企业网站管理系统极强的灵活性和便捷的可扩展性,企业级客户能够迅速流畅的

下载

 

LOB物理读取:从磁盘读取的text、ntext、image、大值类型页的数目

 

LOB预读:为进行查询而放入缓存的text、ntext、image、大值类型页的数目

 

然后再来运行一遍,不清空缓存

<span>1</span> <span>SET</span> <span>STATISTICS</span> IO <span>ON</span>
<span>2</span> <span>GO</span>
<span>3</span> 
<span>4</span> <span>SELECT</span> <span>DISTINCT</span>(<span>[</span><span>ProductID</span><span>]</span>),<span>[</span><span>UnitPrice</span><span>]</span> <span>FROM</span> <span>[</span><span>dbo</span><span>]</span>.<span>[</span><span>SalesOrderDetail_test</span><span>]</span>
<span>5</span> <span>WHERE</span> <span>[</span><span>ProductID</span><span>]</span><span>=</span><span>777</span>
<span>6</span> <span>GO</span>

结果集返回:

<span>1</span> 表 <span>'</span><span>SalesOrderDetail_test</span><span>'</span>。扫描计数 <span>5</span>,逻辑读取 <span>15064</span> 次,物理读取 <span>0</span> 次,预读 <span>0</span> 次,lob 逻辑读取 <span>0</span><span> 次,
</span><span>2</span> lob 物理读取 <span>0</span> 次,lob 预读 <span>0</span> 次。

这次逻辑读取不变,还是15064页。但是物理读取和预读都是0了。说明数据已经缓存在内存里
第二次运行不需要再从磁盘上读一遍,节省了时间

 

为了不影响其他测试,请运行下面语句关闭SET STATISTICS IO ON

<span>1</span> <span>SET</span> <span>STATISTICS</span> IO <span>OFF</span>
<span>2</span> <span>GO</span>

 

 

SET STATISTICS PROFILE ON


这是三个设置中返回最复杂的一个,他返回语句的执行计划,以及语句运行在每一步的实际返回行数统计。

通过这个结果,不仅可以得到执行计划,理解语句执行过程,分析语句调优方向,也可以判断SQLSERVER是否

选择了一个正确的执行计划。

<span>1</span> <span>SET</span> <span>STATISTICS</span> PROFILE <span>ON</span>
<span>2</span> <span>GO</span>
<span>3</span> <span>SELECT</span> <span>COUNT</span>(b.<span>[</span><span>SalesOrderID</span><span>]</span><span>)
</span><span>4</span> <span>FROM</span> <span>[</span><span>dbo</span><span>]</span>.<span>[</span><span>SalesOrderHeader_test</span><span>]</span><span> a
</span><span>5</span> <span>INNER</span> <span>JOIN</span> <span>[</span><span>dbo</span><span>]</span>.<span>[</span><span>SalesOrderDetail_test</span><span>]</span><span> b
</span><span>6</span> <span>ON</span> a.<span>[</span><span>SalesOrderID</span><span>]</span><span>=</span>b.<span>[</span><span>SalesOrderID</span><span>]</span>
<span>7</span> <span>WHERE</span> a.<span>[</span><span>SalesOrderID</span><span>]</span><span>></span><span>43659</span> <span>AND</span> a.<span>[</span><span>SalesOrderID</span><span>]</span><span><</span><span>53660</span>
<span>8</span> <span>GO</span>

返回的结果集很长,下面说一下重要字段


SQLSERVER读懂语句运行的统计信息

SQLSERVER读懂语句运行的统计信息

注意:这里是从最下面开始向上看的,也就是说从最下面开始一直执行直到得到结果集所以(行1)里的rows字段显示的值就是这个查询返回的结果集。

而且有多少行表明SQLSERVER执行了多少个步骤,这里有6行,表明SQLSRVER执行了6个步骤!!

 

Rows:执行计划的每一步返回的实际行数

Executes:执行计划的每一步被运行了多少次

StmtText:执行计划的具体内容。执行计划以一棵树的形式显示。每一行都是运行的一步,都会有结果集返回,也都会有自己的cost

EstimateRows:SQLSERVER根据表格上的统计信息,预估的每一步的返回行数。在分析执行计划时,

我们会经常将Rows和EstimateRows这两列做对比,先确认SQLSERVER预估得是否正确,以判断统计信息是否有更新

EstimateIO:SQLSERVER根据EstimateRows和统计信息里记录的字段长度,预估的每一步会产生的I/O cost

EstimateCPU:SQLSERVR根据EstimateRows和统计信息里记录的字段长度,以及要做的事情的复杂度,预估每一步会产生的CPU cost

TotalSubtreeCost:SQLSERVER根据EstimateIO和EstimateCPU通过某种计算公式,计算出每一步执行计划子树的cost

(包括这一步自己的cost和他的所有下层步骤的cost总和),下面介绍的cost说的都是这个字段值

Warnings:SQLSERVER在运行每一步时遇到的警告,例如,某一步没有统计信息支持cost预估等。

Parallel:执行计划的这一步是不是使用了并行的执行计划

 

从上面结果可以看出执行计划分成4步,其中第一步又分成并列的两个子步骤

步骤a1(第5行):从[SalesOrderHeader_test]表里找出所有a.[SalesOrderID]>43659 AND a.[SalesOrderID]

因为表在这个字段上有一个聚集索引,所以SQL可以直接使用这个索引的seek

SQL预测返回10000条记录,实际也就返回了10000条记录.。这个预测是准确的。这一步的cost是0.202(totalsubtreecost)

步骤a2(第6行):从[SalesOrderDetail_test]表里找出所有 a.[SalesOrderID]>43659 AND a.[SalesOrderID]

因为表在这个字段上有一个非聚集索引,所以SQL可以直接使用这个索引的seek

这里能够看出SQL聪明的地方。虽然查询语句只定义了[SalesOrderHeader_test]表上有a.[SalesOrderID]>43659 AND a.[SalesOrderID]

但是根据语义分析,SQL知道这个条件在[SalesOrderDetail_test]上也为真。所以SQL选择先把这个条件过滤然后再做join。这样能够大大降低join的cost

在这一步SQL预估返回50561条记录,实际返回50577条。cost是0.127,也不高

步骤b(第4行):将a1和a2两步得到的结果集做一个join。因为SQL通过预估知道这两个结果集比较大,所以他直接选择了Hash Match的join方法。

SQL预估这个join能返回50313行,实际返回50577行。因为SQL在两张表的[SalesOrderID]上都有统计信息,所以这里的预估非常准确

这一步的cost等于totalsubtreecost减去他的子步骤,0.715-0.202-0.127=0.386。由于预估值非常准确,可以相信这里的cost就是实际每一步的cost

步骤c(第3行):在join返回的结果集基础上算count(*)的值这一步比较简单,count(*)的结果总是1,所以预测值是正确的。

其实这一步的cost是根据上一步(b)join返回的结果集大小预估出来的。我们知道步骤b的预估返回值非常准确,所以这一步的预估cost也不会有什么大问题

这棵子树的cost是0.745,减去他的子节点cost,他自己的cost是0.745-0.715=0.03。是花费很小的一步

步骤b(第2行):将步骤c返回的值转换为int类型,作为结果返回

这一步是上一步的继续,更为简单。convert一个值的数据类型所要的cost几乎可以忽略不计。所以这棵子树的cost和他的子节点相等,都是0.745。

也就是说,他自己的cost是0

 

通过这样的方法,用户可以了解到语句的执行计划、SQLSERVER预估的准确性、cost的分布

 

 最后说一下:不同SQLSERVER版本,不同机器cost可能会不一样,例如SQL2005 ,SQL2008

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
Golang 生态工具与框架:扩展开发能力
Golang 生态工具与框架:扩展开发能力

《Golang 生态工具与框架》系统梳理 Go 语言在实际工程中的主流工具链与框架选型思路,涵盖 Web 框架、RPC 通信、依赖管理、测试工具、代码生成与项目结构设计等内容。通过真实项目场景解析不同工具的适用边界与组合方式,帮助开发者构建高效、可维护的 Go 工程体系,并提升团队协作与交付效率。

1

2026.02.24

Golang 性能优化专题:提升应用效率
Golang 性能优化专题:提升应用效率

《Golang 性能优化专题》聚焦 Go 应用在高并发与大规模服务中的性能问题,从 profiling、内存分配、Goroutine 调度、GC 机制到 I/O 与锁竞争逐层分析。结合真实案例讲解定位瓶颈的方法与优化策略,帮助开发者建立系统化性能调优思维,在保证代码可维护性的同时显著提升服务吞吐与稳定性。

2

2026.02.24

Golang 面试题精选:高频问题与解答
Golang 面试题精选:高频问题与解答

Golang 面试题精选》系统整理企业常见 Go 技术面试问题,覆盖语言基础、并发模型、内存与调度机制、网络编程、工程实践与性能优化等核心知识点。每道题不仅给出答案,还拆解背后的设计原理与考察思路,帮助读者建立完整知识结构,在面试与实际开发中都能更从容应对复杂问题。

1

2026.02.24

Golang 运行与部署实战:从本地到云端
Golang 运行与部署实战:从本地到云端

《Golang 运行与部署实战》围绕 Go 应用从开发完成到稳定上线的完整流程展开,系统讲解编译构建、环境配置、日志与配置管理、容器化部署以及常见运维问题处理。结合真实项目场景,拆解自动化构建与持续部署思路,帮助开发者建立可靠的发布流程,提升服务稳定性与可维护性。

3

2026.02.24

Golang 疑难杂症解决指南:常见问题排查与优化
Golang 疑难杂症解决指南:常见问题排查与优化

《Golang 疑难杂症解决指南》聚焦开发过程中常见却棘手的问题,从并发模型、内存管理、性能瓶颈到工程化实践逐步拆解。通过真实案例与调试思路,帮助开发者定位问题根因,建立系统化排查方法。不只给出答案,更强调分析路径与工具使用,让你在复杂 Go 项目中具备持续解决问题的能力。

1

2026.02.24

Golang 入门学习路线:从零基础到上手开发
Golang 入门学习路线:从零基础到上手开发

Golang 入门路线涵盖从零到上手的核心路径:首先打牢基础语法与切片等底层机制;随后攻克 Go 的灵魂——接口设计与 Goroutine 并发模型;接着通过 Gin 框架与 GORM 深入 Web 开发实战;最后在微服务与云原生工具开发中进阶,旨在培养具备高性能并发处理能力的后端工程师。

0

2026.02.24

中国研究生招生信息网官方网站入口 研招网网页版在线入口
中国研究生招生信息网官方网站入口 研招网网页版在线入口

中国研究生招生信息网入口(https://yz.chsi.com.cn) 此网站是研究生报名入口的唯一官方网站

95

2026.02.24

苹果官网入口与在线访问指南_中国站点快速直达与iPhone查看方法
苹果官网入口与在线访问指南_中国站点快速直达与iPhone查看方法

本专题汇总苹果官网最新可用入口及中国站点访问方式,涵盖官网直达链接、iPhone官方页面查看方法与常见访问说明,帮助用户快速进入苹果官方网站,便捷了解产品信息与官方服务。

14

2026.02.24

Asianfanfics官网入口与访问指南_AFF官方平台最新登录地址
Asianfanfics官网入口与访问指南_AFF官方平台最新登录地址

本专题系统整理Asianfanfics(AFF)官方网站最新可用入口,涵盖官方平台最新直达地址、官网登录方式及中文访问指引,帮助用户快速、安全地进入AFF平台浏览与使用相关内容。

15

2026.02.24

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Django 教程
Django 教程

共28课时 | 4.5万人学习

XML教程
XML教程

共142课时 | 7.5万人学习

进程与SOCKET
进程与SOCKET

共6课时 | 0.4万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号