SQL Server 串連基礎知識
摘要:SQL Server 2000 用戶端通過由 API、物件程式庫和協議組成的堆棧串連到 SQL Server。Ken Henderson 將逐一介紹該堆棧的每個組成部分,並說明它們如何工作以及如何進行互動操作。
引言
該堆棧的頂部是 API 或物件程式庫層。應用程式通過物件程式庫公開的 API 函數或介面串連到 Microsoft SQL Server。用於訪問 SQL Server 的 API 樣本包括 ODBC 和 DB-Library。用於訪問 SQL Server 的物件程式庫樣本包括 OLE DB、ADO 和 ADO.NET。由於 ADO 最終使用 OLE DB 與伺服器通訊,因此 Windows 應用程式在與 SQL Server 通訊時實際上只使用兩個常用的物件程式庫,即 OLE DB 和 ADO.NET。由於通過 ADO 或 ADO.NET 進行串連通常比通過 ODBC 進行串連更普遍(但 SQL Server 的查詢分析器和企業管理器仍通過 ODBC 進行串連),因此本文將從 ADO/OLE DB 和 ADO.NET 的角度介紹 SQL Server 串連體繫結構的用戶端。如今,大多數應用程式均通過物件程式庫(而非 ODBC 或類似 API)串連到 SQL Server。
ADO 和 OLE DB
OLE DB 用戶端(也稱作使用者)通過用戶端提供者與伺服器以及其他後端程式進行通訊。此提供者是一組 COM 組件(一個或多個),用於將應用程式請求轉換為網路處理序間通訊 (IPC) 請求。在使用 SQL Server 的情況下,最常用的 OLE DB 提供者是 SQLOLEDB,它是 Microsoft 為 SQL Server 提供的 OLE DB 提供者。SQLOLEDB 隨附於 SQL Server 中,並作為 Microsoft 資料訪問組件 (MDAC) 庫的一部分安裝。
為了使用 ADO 與 SQL Server 進行通訊,應用程式首先使用 Connection 對象建立與伺服器的串連。ADO 的 Connection 對象接受一個連接字串,該字串指定要使用的 OLE DB 提供者以及傳遞給它的參數。如果應用程式使用 SQLOLEDB 提供者串連到 SQL Server,則該字串中將顯示“SQLOLEDB”。
ADO 應用程式還可以通過 ODBC 串連到 SQL Server。為此,應用程式將使用適用於 ODBC 的 OLE DB 提供者,並指定在其連接字串中引用目標 SQL Server 的 ODBC 資料來源。這種情況下,應用程式與 OLE DB 進行通訊,同時 ODBC 的 OLE DB 提供者調用相應的 ODBC API,以便與 SQL Server 進行會話。
ADO.NET
ADO.NET 應用程式通常使用 .NET Framework Data Provider for SQL Server 串連到 SQL Server。該本機提供者使 ADO.NET 對象能夠與 SQL Server 直接進行通訊。通常,應用程式使用 SqlConnection 對象建立串連,然後使用 SqlCommand 對象向伺服器發送命令,並接收伺服器返回的結果。SqlDataAdapter 和 SqlDataReader 類通常與 SqlCommand 一起使用,以便通過託管的代碼應用程式與 SQL Server 進行互動。
通過 OleDbConnection 類,ADO.NET 應用程式還可以使用 SQLOLEDB OLE DB 提供者與 SQL Server 進行互動。此外,它們可以通過 OdbcConnection 類使用 ODBC 訪問 SQL Server。因此,僅通過Managed 程式碼,您就有三種不同的方法從應用程式訪問 SQL Server。從故障排除的角度而言,瞭解這些方法是非常有用的,因為它可以協助您將遇到的與串連相關的問題歸結到特定的資料訪問層或庫。
用戶端 Net-Library
該堆棧中的下一層是 Net-Library。Net-Library 在 API 或物件程式庫(應用程式使用它與 SQL Server 進行通訊)與網路通訊協定(用於與網路交換資料)之間提供了一個通道。SQL Server 為所有主要的網路通訊協定提供了 Net-Library。這些庫以透明方式將用戶端發出的請求發送到 SQL Server,並將伺服器發出的響應返回給用戶端。可以使用 SQL Server 的用戶端網路公用程式配置適用於特定用戶端的 Net-Library。支援的用戶端協議包括 TCP/IP、具名管道、NWLink、多協議 (RPC) 和其他一些協議。
尤其值得一提的 Net-Library 是共用記憶體 Net-Library。顧名思義,該 Net-Library 使用 Windows 的共用記憶體功能在 SQL Server 用戶端與伺服器之間進行通訊。顯然,這意味著用戶端與伺服器必須位於同一台實體電腦上。
由於它能夠繞過物理網路堆棧,因此共用記憶體 Net-Library 要比其他 Net-Library 快得多。對共用記憶體地區的訪問受到同步對象的保護,因此用戶端與伺服器之間的通訊速度主要受限於 Windows 對核心對象進行調度的能力,以及進程與共用記憶體地區之間進行資料複製的能力。
可以在串連時將某個時間段或(本地)指定為您的電腦名稱,來指示使用共用記憶體 Net-Library。也可以在串連時為電腦/執行個體名加上首碼 lpc:,來指示要使用共用記憶體 Net-Library。
注意,即使串連到同一台電腦上的 SQL Server,共用記憶體 Net-Library 也未必就是最佳的串連選項。在某些情況下,用戶端與伺服器之間的直接連接可能限制它的擴充性。與應用程式整體體繫結構中的其他元素一樣,應始終對給定技術解決方案進行全面的測試,然後才能判斷它是否有良好的擴充性以及是否比其他方法更快。
串連
用戶端進行串連時,SQL Server 的使用者模式排程器 (UMS) 組件將它指定給特定的排程器。啟動時,SQL Server 為系統上的每個 CPU 建立一個單獨的 UMS 排程器。當用戶端串連到伺服器時,這些用戶端將指定給具有最少串連數的排程器。串連後,用戶端將不會更換排程器 - 它將始終受到指定排程器的控制,直到串連斷開。
這對與伺服器建立多個連線應用程式程式很重要。如果應用程式效能較差,或無法在它的多個串連上平均分配工作,則在該應用程式的某些串連之間可能造成不必要的 CPU 資源爭用,而其他串連實際上卻處於空閑狀態。
例如,應用程式與雙處理器電腦上啟動並執行 SQL Server 建立了四個串連,串連 1 和 3 隸屬於處理器 0,串連 2 和 4 隸屬於處理器 1。如果應用程式的大部分工作通過串連 1 和 3 執行,則這兩個串連將爭用 CPU 0,而 CPU 1 實際上可能仍處於空閑狀態。這種情況下,應用程式只能斷開某些串連或重新串連某些串連,並希望串連 1 和 3 隸屬於不同的 CPU (串連時無法指定處理器隸屬關係),或在它的串連上重新分配工作負載,以便每個串連的工作負載更加均衡。當然,後一種情況要遠好於前一種情況。
串連記憶體
SQL Server 為用戶端請求的每個串連保留三個資料包緩衝區。每個緩衝區的大小取決於 sp_configure 預存程序指定的預設網路資料包大小。如果預設網路資料包大小小於 8 KB,則這些資料包的記憶體將由 SQL Server 的緩衝池提供。否則,該記憶體將由 SQL Server 的 MemToLeave 地區分配。
值得一提的是,.NET Framework Data Provider for SQL Server 的預設網路資料包大小為 8KB,因此,與Managed 程式碼用戶端串連關聯的緩衝區通常由 SQL Server 的 MemToLeave 地區提供。而典型的 ADO 應用程式卻不同,它們的預設資料包大小為 4 KB,因此緩衝區將由 SQL Server 緩衝池分配。
事件
串連後的用戶端請求通常分為兩種廣泛類別:語言事件和遠端程序呼叫。儘管還存在其他類別,但大多數由 SQL Server 用戶端發送到伺服器的請求由以下兩種類型之一構成:語言事件是從用戶端發送到伺服器的 一組 T-SQL。例如,如果調用 ADO Command 對象(其 CommandText 屬性設定為 T-SQL 查詢,CommandType 屬性設定為 adCmdText)的 Execute 方法,則查詢將作為語言事件提交給伺服器。同樣,如果將 CommandType 設定為 adCmdTable 並調用 Execute 方法,則 ADO 將產生一個內部查詢(它將選擇 CommandText 屬性標識的表中的所有列),並將它作為語言事件提交給伺服器。另一方面,如果將 CommandType 設定為 adStoredProc,則調用 Execute 將使 ADO 向伺服器提交一個遠端程序呼叫請求,以執行 CommandText 屬性中列出的預存程序。
為何要關心將請求作為語言事件還是作為 RPC 提交給伺服器呢?通常,這是因為 RPC 的功能更為出色,特別是在重複調用具有不同篩選值的同一查詢時。儘管 SQL Server 可以自動將普通的語言事件請求參數化,但這種能力非常有限。它從不嘗試自動將某些類型的查詢參數化。這可能會導致基本相同的查詢產生不同的執行,從而只因為這些不同的執行提供不同的值,而導致在伺服器上白白浪費計劃編譯的成本。這通常不是您所希望的結果 - 您希望針對查詢的第一次執行編譯一個新的計劃,然後將該計劃重複用於具有不同參數的執行。
而 RPC 則通過顯式參數化查詢(而不是依賴伺服器參數化查詢)來支援計劃重複使用。為過程的第一次執行產生一個計劃後,隨後的執行將自動重複使用該計劃,即使它們提供的參數值不同。與通過語言事件調用預存程序相比,使用 RPC 調用預存程序不僅節省了計劃編譯所需的執行時間和 CPU 資源,還增強了 SQL Server 記憶體資源的利用率,因為它避免了冗餘執行計畫所浪費的記憶體。
在執行動態 T-SQL 時,通常首選 sp_executesql 而不是 EXEC() 也出於同樣的原因。Sp_executesql 的工作方式是:使用指定的查詢建立一個預存程序,然後使用提供的參數調用它。與 EXEC() 不同,sp_executesql 提供了一個允許您參數化動態 T-SQL 並支援計劃重複使用的機制。使用 sp_executesql 執行的動態查詢比使用 EXEC() 的查詢能夠在更大程度上避免不必要的編譯和資源消耗。
TDS
從用戶端發送到 SQL Server 的 RPC、語言事件和其他類型的請求被格式化為稱作表格式資料流 (TDS) 的 SQL Server 特定資料格式。TDS 是 SQL Server 用戶端和伺服器之間使用的“語言”。對於它的確切格式將不作介紹,但是,如果用戶端要與 SQL Server 進行通訊,就必須使用 TDS。
目前,SQL Server 支援三種版本的 TDS:TDS 8.0(適用於 SQL 2000 用戶端)、TDS 7.0(適用於 SQL Server 7.0 用戶端)和 TDS 4.2(適用於 SQL Server 4.2、6.0 和 6.5 用戶端)。完全支援所有 SQL Server 2000 功能的版本只有 TDS 8.0。其他版本保持向後相容。
伺服器端 Net-Library
在伺服器端,用戶端請求最初由 SQL Server 為偵聽特定網路通訊協定而建立的接聽程式接收。這些接聽程式由伺服器上的網路程式庫以及伺服器端的 Net-Library(在它們與伺服器之間提供管道)構成。您可以使用 SQL Server 網路公用程式設定管理員偵聽的協議。SQL Server 與用戶端支援同樣範圍的網路通訊協定(處理群集的情況除外)。對於群集化的 SQL Server,只有 TCP/IP 和具名管道可用。
SQL Server 為偵聽用戶端請求所使用的每個網路通訊協定設定一個線程,並使用 Windows 的 I/O 完成連接埠機制等待和有效處理請求。從網路接收到 TDS 資料包時,Net-Library 接聽程式將其重新彙編為它們的原始用戶端請求,並將這些請求傳遞到 SQL Server 的命令處理層,即開放式資料服務 (ODS)。
將結果返回到用戶端
伺服器在準備將特定用戶端請求的結果返回時,將使用最初接收請求時所用的網路堆棧。它通過伺服器端 Net-Library 將結果發送到相應的網路通訊協定,隨後這些結果將通過網路以 TDS 格式返回到用戶端。
在用戶端上,用戶端 Net-Library 將從伺服器接收的 TDS 資料包從 IPC 層重新彙編,並將其繼續轉寄到初始化該請求的 API 或物件程式庫。
小結
儘管涉及了所有組件,但 SQL Server 用戶端與伺服器之間的往返過程卻相當快 - 特別是在使用記憶體 Net-Library 時,亞秒回應時間非常普遍。構建和調整您自己的 SQL Server 用戶端應用程式時,以下幾個與資料相關的問題值得注意:
• |
如果應用程式與 SQL Server 運行在同一台電腦上,則建議您使用共用記憶體 Net-Library(如果尚未使用它)。基於共用記憶體 Net-Library 的串連通常比其他類型的串連快很多。在注意上述內容的同時,還應:始終全面測試解決方案並將它與其他可行方案進行對比,這樣才能判斷它是否確實更好或更快。事實勝於雄辯。 |
• |
由於用戶端在第一次串連時將指定給特定的 UMS 排程器,並只有在中斷連線後,才會擺脫該排程器的控制,因此確保在應用程式與伺服器建立的串連上均衡分配工作負載非常重要。工作負載不均衡可導致不必要的 CPU 爭用並降低資源使用率。 |
• |
在伺服器上配置的預設網路資料包大小以及用戶端在串連時指定的網路資料包大小將直接影響它們在伺服器上所需的記憶體量和分配記憶體的池。對伺服器進行擴充性和速度配置時,應記住這一點。還應記住,預設情況下,ADO.NET 應用程式的網路資料包大小比 ADO 應用程式的更大。 |
• |
通常,在向伺服器發送請求時,應首選 RPC 而非語言事件。為此,應在使用的 ADO 或 ADO.NET 對象中設定相應的屬性。 |
• |
執行動態 T-SQL 時,應在可能的情況下使用 sp_executesql 代替 EXEC()。唯一例外的情況是,當使用 EXEC() 的功能將查詢片斷串連而成的動態查詢字串的大小超過單個本地變數的儲存大小時(這種情況非常少見)。 |
• |
當遇到用戶端問題,並且懷疑它可能和串連伺服器時所用的物件程式庫或 API 有關時,可以使用的一個故障排除技巧就是更改所用的用戶端機制,這樣可以將問題歸結為特定的組件。例如,假設您升級 MDAC 並開始在 SQL Server 錯誤記錄檔中看到 17805 錯誤,這表明用戶端 ADO 應用程式發送的 TDS 資料包的格式不正確。您可能嘗試讓應用程式轉為使用 ODBC 的 OLE DB 提供者,如果您可以較為容易地做到這一點,應看看該問題是否與 SQLOLEDB 提供者有一定的關係。相反,如果基於 ADO 的應用程式一直通過 ODBC 進行串連,則可以切換到 SQLOLEDB,看看這是否能解決問題,或至少協助您縮小問題的範圍。 |
• |
同樣,在對串連問題進行故障排除時,更改正在使用的 Net-Library 有時會有所協助。如果使用 TCP/IP,具名管道也許值得一試。例如,如果 DHCP 伺服器出現問題,並且沒有有效 IP 位址,則您將無法使用 TCP/IP 串連到 SQL Server。通過切換到具名管道,可以快速地將問題歸結為 TCP/IP 特定的因素上。另一方面,如果在切換 Net Library 後仍存在同樣的問題,則可以排除 Net-Library 方面的問題。問題的原因可能是伺服器已關閉,或在您與伺服器之間的某處網路基礎設施無法正常工作。最後,還可以容易地更改應用程式使用的 Net-Library,而不必更改應用程式本身,這樣就為您提供一個協助縮小問題範圍的工具。儘管從長遠角度而言,使用某一特定 Net-Library 並不可行,但讓用戶端臨時使用它可以協助您縮小串連相關問題的範圍。 |
參考資料
The Guru's Guide to SQL Server Architecture and Internals(英文)
SQL Server for Developers
Ken Henderson 與愛妻和孩子一同居住在德克薩斯州達拉斯的郊區。他先後編著了八本書,內容涉及各種技術相關的主題,包括最近發行的“The Guru's Guide to SQL Server Architecture and Internals”(Addison-Wesley, 2003)。Ken 是達拉斯小牛隊的忠實球迷,業餘時間喜歡運動、園藝和照看孩子。