用了一段時間T-SQL以後。哪怕本身沒用過,也多多少少看過SSMS中的SET NOCOUNT ON命令,很是多性能優化文章中都有提到這個東西,它們建議儘量使用這個命令下降網絡傳輸的壓力。那麼今天來看看它是不是個雞肋。sql
假設你們對上面的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,對今時今日的硬件來講不可能出現明顯的性能提高,之因此網上有這個說法,很是多是當初的硬件資源存在侷限。沒法知足今天看起來不算大的數據量。但是不管怎樣。我的看來,這些確實也有必定的消耗。做爲編程習慣,在非必要的狀況下仍是建議不返回。畢竟真的沒什麼人用。
對於這個問題,我想到了兩件事情:怎樣對待別人的「善意」和下降網絡傳輸
近期上下班路上在看一本書。看完再分享一下。書上多處地方提到一個句子:One thing doesn’t fit all。
說白了就是「放之四海而皆準」的反義詞。我我的挺贊同這個觀點。爲何非要用一個產品去實現所有功能呢?今時今日大量系統的架構都使用了很是多混合技術,這也證實了這個觀點的可行性。那麼迴歸這個問題。當初別人提出這個優化或編程建議時,確實可能存在網絡性能問題、甚至client(本例中的SSMS)的內存資源壓力從而形成性能壓力。正如20年前Oracle優化書籍中提到的一個表索引數不要超過5個、索引葉子節點不要緩存到內存這類建議同樣。當年的硬件資源確實沒辦法很是好支持這些特性。再看今天,軟硬件已經有了長足的發展,很是多當初是對的設置今天看起來已經無關重要甚至是錯誤的。因此在對待很是多所謂的「軍規」、「鐵律」時,仍是要本身實測一下或者論證一下。
從本例中看,它不會有什麼嚴重的後果和風險。因此是可以測試的。但是有些風險比較大的最好謹慎測試。
我我的以爲,做爲一些編程規範,最好仍是保留下來。因爲它和如下這個有關係。在編程的時候要有下降資源使用的慣性思惟。
那何時實用呢?前面提到了——查錯
在我初爲DBA的時候,有一次一個開發問我:爲何明明插入了一條數據到一個表裏面。而且成功了,表卻沒有數據。一開始我覺得是回滾,問她要完整的語句,拿過來以後發現就一個簡單的insert命令。沒有顯式事務。而後我本身運行了一下,確實沒數據。再看一下影響行數。竟然有兩條(1 行受影響),這就引發個人注意了,檢查表觸發器。果真有一個觸發器,而且功能就是一旦有插入動做,當即觸發刪除。
也就是來一個死一個。來兩個死一雙。
基於這些緣由,我以爲詳細問題詳細分析纔是最有意義的,哪來那麼多所謂的意見建議,不考慮詳細環境的建議都是耍流氓。
之因此當初有這個說法。很是大程度是因爲網絡傳輸壓力。那麼咱們藉助這個問題,引伸其它資源合理使用的場景。這裏說的是網絡問題,那就說網絡問題吧,單純從數據庫層面來講。一般網絡壓力在哪裏呢?據本人瞭解。大概在這些方面:
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的資源爭用能有必定的緩解。
總的來講,我我的建議保留這個功能的常態化關閉、排錯時開啓的說法。
最關鍵的數據實際仍是警示。讓使用者時刻注意對資源的合理使用。