1. 程式人生 > >如何診斷和調優,才能輕鬆與資料庫“timeout”說再見?

如何診斷和調優,才能輕鬆與資料庫“timeout”說再見?

作者介紹

許昌永,高階DBA,微軟SQLServerMVP,十年以上SQLServer使用經驗。曾就職於騰訊公司,從事了六年遊戲行業SQLServer資料庫開發和管理。目前就職於跨境電商DX.COM三年多,負責公司SQLServer和MongoDB的資料庫架構設計、高可用部署、運維管理和效能優化等工作。翻譯出版了書籍《PowerShellV3——SQLServer2012資料庫自動化運維權威指南》,文末將有許老師的新書贈送,不容錯過~

一、超時分析

下面是使用者訪問一個Web站點的常見錯誤:

Web站點

詳細錯誤描述如下:

詳細錯誤描述

以上輸出非常清晰地描述了,對於這個操作的超時時間結束,而實際上工作並沒有完成。

我們常常會發現“timeout”錯誤,那麼具體是在哪個訪問階段、受哪個設定影響報出來的呢?下面,我們通過典型的Web應用架構來分析下超時問題。

Web應用架構

結合上面的訪問關係圖,可以看到:我們從資料庫例項的角度出發,它會收到來自Web端的訪問、使用者的直接訪問,它也可能同時訪問其他資料庫例項。那麼,連線就分為傳入連線(Incomingconnection)訪問和傳出連線(Outgoingconnection)訪問。那我們就根據不同階段的訪問來分類超時問題。

傳入連線:Web端訪問超時

首先,針對傳入連線,我們來看看Web端的訪問超時:有ASP.NET請求超時、WebService請求超時、IIS請求超時、資料庫連線超時和查詢超時。

ASP.NET請求超時

ASP.NET頁面的執行超時時間可以在多個地方設定。

來自MSDN的解釋:

httpRuntime是配置asp.Nethttp執行時設定,以確定如何處理對asp.Net應用程式的請求。

executionTimeout:表示允許執行請求的最大時間限制,單位為秒。預設值為90秒。

maxRequestLength:指示ASP.Net支援的最大檔案上載大小。該限制可用於防止因使用者將大量檔案傳遞到該伺服器而導致的拒絕服務攻擊。指定的大小以KB為單位。預設值為4096KB(4MB)。

1、全域性超時時間

伺服器上如果有多個網站,希望統一設定一下超時時間,則需要設定Machine.config檔案中的ExecutionTimeout屬性值。Machine.config檔案位於%SystemRoot%\Microsoft.NET\Framework\%VersionNumber%\CONFIG\目錄中。

例如:

<httpRuntimeexecutionTimeout=”90″maxRequestLength=”4096″

useFullyQualifiedRedirectUrl=”false”minFreeThreads=”8″

minLocalRequestFreeThreads=”4″appRequestQueueLimit=”100″/>

2、單個站點超時時間

Web.config配置檔案中設定http請求執行時間:

<system.web>

<httpRuntimemaxRequestLength=”102400″executionTimeout=”720″/>

</system.web>

這裡設定的為720秒,前面的屬性maxRequestLength一般用於使用者上傳檔案限制大小!預設一般為4096KB(4MB)。

3、單個頁面請求超時時間

對於單個頁面,可以使用Server.ScriptTimeout來設定超時。

Server.ScriptTimeout=120;

注意:如果在Web.config裡設定了debug屬性,例如:

<compilationdebug=”true”targetFramework=”4.0″>

此時,ScriptTimeout會被忽略。

WebService請求超時

擴大代理類的超時限制,預設是90秒,即在呼叫方法前指定超時時間。

YourWebServiceyws=newYourWebService();

yws.Timeout=1200000;//20分鐘,單位是毫秒

如果將Timeout屬性設定為Timeout.Infinite,則指示該請求無超時。即使XMLWebservices客戶端可以將Timeout屬性設定為無超時,Web伺服器仍可以在伺服器端使請求超時。

IIS請求超時

在IISManager中,選中Sites,點選右側的WebsiteDefaults,在Limits屬性列表中,設定連線超時時間ConnectionTime-out(seconds)。預設值為120秒。

IIS請求超時

連線超時有助於減少由空閒連線消耗的處理資源損失。啟用連線超時時,IIS會在連線級別執行以下型別的連線超時:客戶端已向伺服器傳送了資料,現處於空閒狀態造成的連線超時。

已建立了與伺服器的連線,但客戶端未傳送資料時造成的伺服器偵聽超時。響應超時(基於可配置的最小位元組數/秒的值)。請求超時,它禁止客戶端向伺服器傳送不合理的慢速請求(例如,1位元/秒)。

資料庫連線超時

在.NET的SqlConnection類,有ConnectionTimeout屬性,獲取終止嘗試並生成錯誤之前在嘗試建立連線時所等待的時間。等待連線開啟所需的時間(以秒為單位)。預設值為15秒。在這個時間內,如果連線沒有建立,我們將會看到這個錯誤。值為0表示無限制。

主要通過連線字串中的ConnectTimeout來進行控制,如下:

<connectionStrings>

<addname=”conn”connectionString=”userid=crm;Password=crmpwd;initialcatalog=DBName;Server=DBServerFQDN;ConnectTimeout=30;”providerName=”System.Data.SqlClient”/>

</connectionStrings>

資料庫查詢超時

在.NET的SqlCommand類,有CommandTimeout屬性,獲取或設定在終止嘗試執行命令並生成錯誤之前的等待時間。等待命令執行所需的時間(以秒為單位)。預設值為30秒。如果請求正在執行,並且沒有在超時時間內完成,那麼我們將看到這個錯誤。值為0表示無限制。

主要是通過SqlCommand.CommandTimeout來進行控制。如下:

SqlCommandcommand=newSqlCommand(queryString,connection);

//Settingcommandtimeoutto1second

command.CommandTimeout=1;

我們另外再介紹一種超時,在.NET的SqlBulkCopy類,有BulkCopyTimeout屬性,超時之前操作完成所允許的秒數。如果操作超時,事務便不會提交,而且所有已複製的行都會從目標表中移除。使用SqlBulkCopy批量載入資料時的預設超時設定為30秒。

每次對資料庫連線時,我們有時候會碰到連線超時或者命令超時,這兩個超時是不一樣的。以ADO.NET為例,當客戶端和伺服器端連線時,碰到的超時情況主要有下面幾種:

  • 當從連線池獲取一個連線時,碰到超時。
  • 當建立一個全新連線(而不是從連線池獲取)時,碰到超時。
  • 當傳送一個命令(command)到SQLServer時,超時。
  • 當傳送命令(連線字串帶有“contextconnection=true”屬性)到SQLServer時,超時。
  • 當不是顯示的傳送一個命令(implicitly)到SQLServer時,碰到超時。
  • 當執行非同步命令時,(BeginExecute)碰到超時。
  • 當從伺服器端,獲取行時,碰到超時。
  • 當用Bulkcopy方式,上傳資料時,碰到超時。

這些超時主要是通過連線字串中的ConnectTimeout和SqlCommand.CommandTimeout來進行控制。前面兩種是登入超時由ConnectionTimeout來決定什麼時候超時,後面幾種是命令超時由CommandTimeout來決定什麼時候超時。

特別注意:“超時時間已到。在操作完成之前超時時間已過或伺服器未響應”。類似這種錯誤,一般是SqlCommand.CommandTimeout或者SqlBulkCopy.BulkCopyTimeout的時間超時,而不是SqlConnection.ConnectionTimeout。

傳入連線:SSMS訪問超時

接著,針對傳入連線,我們來看看使用者通過SQLServerManagementStudio即SSMS訪問資料庫時的超時設定。

SSMS訪問超時 SSMS訪問超時

這裡,我們可以設定SSMS工具的連線和查詢超時時間。連線超時的預設值為15秒。而查詢超時的預設值為0,表示查詢會一直執行直到完成。

傳出連線:資料庫跨例項遠端訪問超時

最後,針對傳出連線,我們來看看資料庫跨例項遠端訪問的超時設定。

資料庫從一個例項訪問到另一個例項,可以通過以下方式檢視到。

檢視配置選項的設定:

配置選項

遠端登入超時

遠端登入超時選項指定了,從登入遠端伺服器失敗返回前等待的秒數。例如,如果你嘗試登入到一個遠端伺服器,而伺服器宕機了,遠端登入超時幫助你在你的機器停止嘗試登入前,不用無限等待下去。這個選項的預設值為10秒。值為0表示無限等待。

在SQLServer2008中,這個選項的預設值為20秒。

遠端登入超時選項影響了異構查詢的OLEDB提供者產生的連線。

這個設定不用重啟服務立即生效。

SQL Server 2008

SQLServer2014的遠端登入超時時間預設為10秒。下面的指令碼可以修改該值:

EXECsp_configure’remotelogintimeout’,35;

GO

RECONFIGURE;

GO

遠端查詢超時

遠端查詢超時選項指定了,在SQLServer超時前一個遠端操作花費了多少秒。預設值為600秒,允許10分鐘的等待。這個值應用於資料庫引擎發起的作為遠端查詢的傳出連線。這個值對於資料庫引擎收到的查詢無效。為了禁止超時,可以設定為為0。那麼查詢將會一直等待直到取消。

對於異構查詢,遠端查詢超時指定了,在查詢超時前,一個遠端提供者應該等待結果的秒數。(使用DBPROP_COMMANDTIMEOUT行集屬性在命令物件初始化)。如果被遠端提供者支援,這個值也被使用者設定DBPROP_COMMANDTIMEOUT。在指定的數秒後,這將導致任何其他的操作超時。

對於遠端儲存過程,遠端查詢超時指定的秒數為,在遠端儲存過程超時之前,在傳送一個遠端EXEC語句之後花費的時間。

這個設定不用重啟服務立即生效。

遠端查詢超時

SQLServer2014的遠端查詢超時為10分鐘,可以通過以下指令碼修改該值:

EXECsp_configure’remotequerytimeout’,0;

GO

RECONFIGURE;

GO

遠端伺服器和連結伺服器的對應選項

在配置遠端訪問的時候,可以設定連結伺服器的超時選項,也分連線超時和查詢超時。

設定語法如下:

sp_serveroption[@server=]’server’

,[@optname=]’option_name’

,[@optvalue=]’option_value’;

22

二、資料庫超時診斷和調優

傳入連線:連線超時

對於連線超時,首先可以檢視ConnectivityRingBuffer中的LoginTimers型別錯誤來分析,如果想獲得更詳盡的資訊,再通過抓包工具networkmonitor。

SQLServer2008中包含一個新功能,旨在幫助解決特別棘手的連線問題。

這個新功能是ConnectivityRingBuffer,它可以捕捉每一個由伺服器發起的連線關閉記錄(server-initiatedconnectionclosure),包括每一個session或登入失敗事件。為了進行有效的故障排除,RingBuffer會嘗試提供客戶端的故障和伺服器的關閉動作之間的關係資訊。只要伺服器線上,最高1K的RingBuffer就會被儲存,1000條記錄後,Buffer開始迴圈覆蓋,即從最老的記錄開始覆蓋。

ConnectivityRingBuffer的記錄是能夠使用DMV查詢的:

SELECTCAST(recordASXML)FROMsys.dm_os_ring_buffers

WHEREring_buffer_type=’RING_BUFFER_CONNECTIVITY’

首先我們從連線的RingBuffer資料返回的XML來入手。執行上面的語句,得到下面的結果:

Ring Buffer資料返回

點選XML的超連結,開啟檔案內容看到更可讀的內容,包括一條基本的Ring Buffer連線超時記錄。

連線超時

可以看到在XML文件中有許多相當有用的資訊。像SniConsumerError,State和RemoteHost這些。

特別注意的是,RecordType節點,對於我們上面的截圖來看標識為“LoginTimers”,說明是連線超時資訊。為了識別這種型別的連線RingBuffer,我們可以查詢SniConsumerError程式碼號,準確定位是什麼錯誤導致的。

RecordType包含那些值?

Error–連線錯誤

LoginTimers–連線超時

ConnectionClose–殺掉程序

可以通過如下指令碼,將XML資料轉化為可讀資訊:

指令碼

執行上面的查詢後,將得到下面的可讀結果。在這個查詢中,我們關聯Ring Buffer資料和sys.messages檢視去抓取Error id的文字。通過這個資訊我們可以跟蹤到精確的導致Error:Login失敗的資訊。

Error:Login失敗

傳入連線:查詢超時

對於查詢超時,針對SQL Server 2012以下的版本,使用Profiler的TSQL_Duration模板的基礎上,新增“Errors and Warnings”下的“Attention”,根據捕獲到的Attention結合上下文去查詢相應的語句;對於SQL Server 2012及以上版本,直接使用擴充套件事件監控sqlserver.attention事件,直接輸出sql_text。

以下為XE指令碼:

XE指令碼

調優建議

對於連線耗時,當然務必要找到具體原因,是網路問題還是驗證問題;對於查詢超時,多為語句效能問題導致,如阻塞、未使用合理的索引、輸出資料量太大等原因。對於臨時解決問題,可以在連線配置裡、或在程式裡的語句級引數屬性調大配置值。應及時找出問題的根源並解決。

傳入連線:資料庫跨例項遠端訪問超時

連結伺服器遠端訪問導致的連線超時和查詢超時,我們可以在目標資料庫伺服器上來使用以上方法來監控和分析。

調優建議

當使用連結伺服器(LinkedServers)時,最昂貴的代價就是網路頻寬間大量資料的傳輸。在正確的伺服器書寫正確的程式碼是非常重要的,因為每一個錯誤都會導致在網路頻寬上付出非常昂貴的代價。

儘量避免使用連結伺服器向遠端推送資料,而是使用LinkedServer.DatabaseName.dbo.TableName為源從遠端拉取資料。

跨伺服器查詢時,為了在兩臺伺服器之間的資料集之間執行JOIN操作,SQLServer需要將資料從一臺伺服器傳送到另外一臺伺服器。如果傳送的資料是一個非常大的表,這個過程可能會非常痛苦。通常來說,資料會從遠端伺服器傳送到本地伺服器。為了防止大量資料在伺服器之間大傳送,你可以通過在查詢條件中過濾資料,通過一個遠端儲存過程只取回相關資料來達到目的,萬一你需要使用INNERJOIN關聯兩個不同伺服器之間的資料集,而且本地表的資料量遠小於遠端伺服器的那個表。你可以使用REMOTEJOINHINT,這樣就會將資料從本地伺服器將資料傳送到遠端伺服器,從而提高效能。

正如JOIN操作,UNIION不同伺服器之間的兩個資料集必定導致從遠端伺服器傳送資料到本地伺服器。即使你執行遠端查詢合併(UNION)同一個遠端伺服器的兩個資料集,還是會先將兩個資料集傳送到本地伺服器,然後UNION兩個資料集,可以通過遠端儲存過程,函式或檢視先UNION資料庫來阻止這個。

避免書寫太複雜的查詢語句。優化器不能總是能明白你需要做什麼,尤其是你的SQL語句中使用了連結伺服器時,保持SQL指令碼簡單。

當資料庫位於同一個例項時不要使用連結伺服器。而是直接使用跨庫訪問Database.dbo.TableName來訪問。

連結伺服器的濫用可能會導致資料庫出現很多ASYNC_NETWORK_IO等待事件。你可以通過釋出-訂閱或者作業將資料集(表)資料先同步到本地伺服器,然後將SQL指令碼中的連結伺服器去掉,這樣對SQL查詢效能有非常大的提升,尤其是查詢比較頻繁或資料量大的SQL語句。

對於連結伺服器的物件呼叫,儘量使用同義詞,這樣簡化了管理。

文章出處:DBAplus社群(訂閱號ID:dbaplus)