T-SQL注意事项(1)——SET NOCOUNT ON的去与留

 前言


用了一段时间T-SQL以后。哪怕本身没用过,也多多少少看过SSMS中的SET NOCOUNT ON命令,很是多性能优化文章中都有提到这个东西,它们建议尽量使用这个命令下降网络传输的压力。那么今天来看看它是不是个鸡肋。sql

 

SET NOCOUNT的做用

首先来看看这个命令的做用。依据 官方说明:阻止在结果集中返回显示受 Transact-SQL 语句或存储过程影响的行计数的消息。

在说明中的“备注”部分有这么一段话,注意红字部分:数据库

当 SET NOCOUNT 为 ON 时,将不向client发送存储过程当中每个语句的 DONE_IN_PROC 消息。  假设存储过程当中包括一些并不返回不少实际数据的语句,或者假设过程包括 Transact-SQL 循环,网络通讯流量便会大量下降,所以,将 SET NOCOUNT 设置为 ON 可显著提升性能  

假设你们对上面的DONE_IN_PROC有兴趣,可以看看这篇文章: https://msdn.microsoft.com/en-us/library/dd340553.aspx,但是我以为不是必需过于纠结。编程

如下建立一个測试样例,并作演示:缓存


USE tempdb
GO
 
IF object_id('NocountDemo', 'U') IS NOT NULL
    DROP TABLE NocountDemo
GO
 
CREATE TABLE NocountDemo(
    id INT identity(1, 1),
    NAME VARCHAR(64)
    )
GO
 
/*
插入測试数据
*/
INSERT INTO NocountDemo
SELECT NAME
FROM master..spt_values
 
/*
常规使用
*/
SELECT *
FROM NocountDemo
GO
 
/*
使用SET NOCOUNT选项
*/
SET NOCOUNT ON;
 
SELECT *
FROM NocountDemo
 
SET NOCOUNT OFF;


首先看看默认状况。也就是SET NOCOUNT OFF的结果:安全


 

而后看看开启NOCOUNT选项的结果:性能优化

 

 

 

默认状况下,不论什么SQL语句成功完毕后都会返回一些信息。而NOCOUNT用于控制是否返回影响行数,需要提醒的是。哪怕不是真正的SQL语句。比方开启包括实际运行计划功能,当运行计划成功完毕后(注意是完毕),也会返回影响行数到client(也就是这里的SSMS)。网络

但是从实操层面,你们是否差点儿没有关注过这个信息?确实,是否成功运行完很是多时候不需要查看这个信息,一些SELECT语句天然会返回数据结果。因此实际上这个信息常常是非必要的。架构

做为最佳实践。通常建议不返回影响行数,特别是存储过程。只是有时候它又有存在价值。比方应用程序嵌入的SQL语句的调试。并发

在很是多性能优化的文章中(甚至前面提到的官方文档)都提到。为了减轻网络压力,建议启用这个设置(注意一点。这个设置和其它很是多设置不一样,它默认是“开启”的,也就是说咱们需要作“关闭”操做,而很是多操做是需要作“开启”操做)。这个缘由貌似很是合理,只是做为DBA的习惯。我更愿意深究底层原理。因此如下再看个简单样例,插入一条数据后,循环更新100万次,并获取时间差:ide


SET NOCOUNT OFF;

DECLARE @i INT = 1;
DECLARE @x TABLE (a INT);

INSERT @x (a)
VALUES (1);

SELECT SYSDATETIME() AS [開始时间];

WHILE @i < 1000000
BEGIN
	UPDATE @x
	SET a = 1;

	SET @i += 1;
END

SELECT SYSDATETIME() AS [结束时间];

在我本机上运行状况例如如下图:




本文重点关注的是影响行数,因此咱们先看影响行数的状况,选择结果集中的【消息】页

从右边的滚动栏可以大概预估这个行数应该很是多。实际上每次insert都有一行。外加循环外层的SELECT SYSDATETIME()  ,总共10000002行。而后关闭返回影响行数,即便用SET NOCOUNT命令又一次运行:



为了不有人以为測试没考虑并发问题,我使用SQLQueryStress工具分别对上面两套语句模拟200个线程运行100次(本机配置过低没法运行太屡次):

默认状况重复运行屡次:




开启NOCOUNT的状况下:




对照一下时间,差别时间不明显,假设多运行几回可能会出现反而更慢的状况。难道网上说的是假的?先别下定论,那问题在哪里?咱们再回过头看看别人的描写叙述里面的keyword“网络传输”。貌似发现问题了,因为一直以来都在单机上面測试,而且检查一下SQL Server配置管理器中的网络协议,Shared Memory是开启了。也就是说数据都在内存中传输,跟“网络”没什么关系:



那么看来问题就是这个缘由。现在随便找台机器再试一下,我在国际版的Azure上开了个SQL Azure(开VM再装SQL Server实在耗时而且费用很多)。假设没有条件的可以找些測试server甚至别人的电脑试试。

现在我在本机直接连到美国的SQL Azure上。而后再次分别运行上面的两个语句(为了不时间太久,把100万次改成10万次):







时间对照以后发现,事实上相差不大。咱们最好仍是静下来想一下,即便100万次,产生的数据也就几十上百Bytes或者KB,对今时今日的硬件来讲不可能出现明显的性能提高,之因此网上有这个说法,很是多是当初的硬件资源存在局限。没法知足今天看起来不算大的数据量。但是不管怎样。我的看来,这些确实也有必定的消耗。做为编程习惯,在非必要的状况下仍是建议不返回。毕竟真的没什么人用。

 

问题提炼

对于这个问题,我想到了两件事情:怎样对待别人的“善意”和下降网络传输


1.怎样对待别人的“善意”


近期上下班路上在看一本书。看完再分享一下。书上多处地方提到一个句子:One thing doesn’t fit all。

说白了就是“放之四海而皆准”的反义词。我我的挺赞同这个观点。为何非要用一个产品去实现所有功能呢?今时今日大量系统的架构都使用了很是多混合技术,这也证实了这个观点的可行性。那么回归这个问题。当初别人提出这个优化或编程建议时,确实可能存在网络性能问题、甚至client(本例中的SSMS)的内存资源压力从而形成性能压力。正如20年前Oracle优化书籍中提到的一个表索引数不要超过5个、索引叶子节点不要缓存到内存这类建议同样。当年的硬件资源确实没办法很是好支持这些特性。再看今天,软硬件已经有了长足的发展,很是多当初是对的设置今天看起来已经无关重要甚至是错误的。因此在对待很是多所谓的“军规”、“铁律”时,仍是要本身实測一下或者论证一下。

从本例中看,它不会有什么严重的后果和风险。因此是可以測试的。但是有些风险比較大的最好谨慎測试。

我我的以为,做为一些编程规范,最好仍是保留下来。因为它和如下这个有关系。在编程的时候要有下降资源使用的惯性思惟。

那何时实用呢?前面提到了——查错

在我初为DBA的时候,有一次一个开发问我:为何明明插入了一条数据到一个表里面。而且成功了,表却没有数据。一開始我觉得是回滚,问她要完整的语句,拿过来以后发现就一个简单的insert命令。没有显式事务。而后我本身运行了一下,确实没数据。再看一下影响行数。竟然有两条(1 行受影响),这就引发个人注意了,检查表触发器。果真有一个触发器,而且功能就是一旦有插入动做,当即触发删除。

也就是来一个死一个。来两个死一双。

基于这些缘由,我以为详细问题详细分析才是最有意义的,哪来那么多所谓的意见建议,不考虑详细环境的建议都是耍流氓。


2.下降网络传输  

之因此当初有这个说法。很是大程度是因为网络传输压力。那么咱们借助这个问题,引伸其它资源合理使用的场景。这里说的是网络问题,那就说网络问题吧,单纯从数据库层面来讲。一般网络压力在哪里呢?据本人了解。大概在这些方面:

a)        需要传输的数据量,这是真正的网络压力。

量越大压力越明显,那么一般咱们要作的就是在返回数据时,尽量控制数据量,比方不要在非必要状况下使用SELECT *,尽量在离开数据层的时候就把数据量控制下来。

b)        其它功能传输的信息,比方SQL Server一些高可用或者负载分离功能。就拿复制功能来讲,为了使订阅server的数据与公布server的数据一致。公布server会依据实际配置实时或定时发送事务日志到订阅端重作,日志产生越多,需要传输的量就越多,对于这部分你能干预的地方就很是少。非要作的话。可以在配置公布项时仅仅选择需要同步的行与列。

c)        SQL语句,这部分实际上也不大,但是正如很是多编码规范中说的,尽量使用存储过程,当中一个理由就是直接传输SQL代码不只不安全。而且量一大的话,也是有开销的,有些SQL代码有还几百KB,对于使用频繁的系统而言,这也是一笔开销。固然不是说禁用,详细状况详细分析吧。

另外多说一句,对于超过8KB的SQL代码,SQL Server不缓存运行计划,意味着你要每次重编译可能全然同样的SQL代码,从而形成CPU、内存的压力。

d)        需要导入、导出数据库的其它格式文件,如TXT、Excel等,某些系统需要传输一些文件到server再进行导入或者导出数据到文件而后经过某些方式传输出数据库server以外。这部分可以经过改动业务逻辑来下降。但是能下降程度可能不高,那么对于这样的状况。可以考虑把数据库server和应用程序放在一个局域网中,而后把文件终于需要传输的发生地从数据库server移到应用程序所在的server。因为应用程序easy横向扩展。因此可以经过一些技术把应用程序的负载下降。这样即便文件传输的量不能下降。最起码对数据库层server的资源争用能有必定的缓解。


总的来讲,我我的建议保留这个功能的常态化关闭、排错时开启的说法。

最关键的数据实际仍是警示。让使用者时刻注意对资源的合理使用。

相关文章
相关标签/搜索