直播中
該堆棧的頂部是 API 或?qū)ο髱鞂印?yīng)用程序通過對(duì)象庫公開的 API 函數(shù)或接口連接到 Microsoft® SQL Server。用于訪問 SQL Server 的 API 示例包括 ODBC 和 DB-Library。用于訪問 SQL Server 的對(duì)象庫示例包括 OLE DB、ADO 和 ADO.NET。由于 ADO 最終使用 OLE DB 與服務(wù)器通信,因此 Windows 應(yīng)用程序在與 SQL Server 通信時(shí)實(shí)際上只使用兩個(gè)常用的對(duì)象庫,即 OLE DB 和 ADO.NET。由于通過 ADO 或 ADO.NET 進(jìn)行連接通常比通過 ODBC 進(jìn)行連接更普遍(但 SQL Server 的查詢分析器和企業(yè)管理器仍通過 ODBC 進(jìn)行連接),因此本文將從 ADO/OLE DB 和 ADO.NET 的角度介紹 SQL Server 連接體系結(jié)構(gòu)的客戶端。如今,大多數(shù)應(yīng)用程序均通過對(duì)象庫(而非 ODBC 或類似 API)連接到 SQL Server?! ?
ADO 和 OLE DB
OLE DB 客戶端(也稱作使用者)通過客戶端提供程序與服務(wù)器以及其他后端程序進(jìn)行通信。此提供程序是一組 COM 組件(一個(gè)或多個(gè)),用于將應(yīng)用程序請(qǐng)求轉(zhuǎn)換為網(wǎng)絡(luò)進(jìn)程間通信 (IPC) 請(qǐng)求。在使用 SQL Server 的情況下,最常用的 OLE DB 提供程序是 SQLOLEDB,它是 Microsoft 為 SQL Server 提供的 OLE DB 提供程序。SQLOLEDB 隨附于 SQL Server 中,并作為 Microsoft 數(shù)據(jù)訪問組件 (MDAC) 庫的一部分安裝。
為了使用 ADO 與 SQL Server 進(jìn)行通信,應(yīng)用程序首先使用 Connection 對(duì)象建立與服務(wù)器的連接。ADO 的 Connection 對(duì)象接受一個(gè)連接字符串,該字符串指定要使用的 OLE DB 提供程序以及傳遞給它的參數(shù)。如果應(yīng)用程序使用 SQLOLEDB 提供程序連接到 SQL Server,則該字符串中將顯示“SQLOLEDB”。
ADO 應(yīng)用程序還可以通過 ODBC 連接到 SQL Server。為此,應(yīng)用程序?qū)⑹褂眠m用于 ODBC 的 OLE DB 提供程序,并指定在其連接字符串中引用目標(biāo) SQL Server 的 ODBC 數(shù)據(jù)源。這種情況下,應(yīng)用程序與 OLE DB 進(jìn)行通信,同時(shí) ODBC 的 OLE DB 提供程序調(diào)用相應(yīng)的 ODBC API,以便與 SQL Server 進(jìn)行會(huì)話。
ADO.NET
ADO.NET 應(yīng)用程序通常使用 .NET Framework Data Provider for SQL Server 連接到 SQL Server。該本機(jī)提供程序使 ADO.NET 對(duì)象能夠與 SQL Server 直接進(jìn)行通信。通常,應(yīng)用程序使用 SqlConnection 對(duì)象建立連接,然后使用 SqlCommand 對(duì)象向服務(wù)器發(fā)送命令,并接收服務(wù)器返回的結(jié)果。SqlDataAdapter 和 SqlDataReader 類通常與 SqlCommand 一起使用,以便通過托管的代碼應(yīng)用程序與 SQL Server 進(jìn)行交互?! ?
通過 OleDbConnection 類,ADO.NET 應(yīng)用程序還可以使用 SQLOLEDB OLE DB 提供程序與 SQL Server 進(jìn)行交互。此外,它們可以通過 OdbcConnection 類使用 ODBC 訪問 SQL Server。因此,僅通過托管代碼,您就有三種不同的方法從應(yīng)用程序訪問 SQL Server。從故障排除的角度而言,了解這些方法是非常有用的,因?yàn)樗梢詭椭鷮⒂龅降呐c連接相關(guān)的問題歸結(jié)到特定的數(shù)據(jù)訪問層或庫?!?/P>
客戶端 Net-Library
該堆棧中的下一層是 Net-Library。Net-Library 在 API 或?qū)ο髱欤☉?yīng)用程序使用它與 SQL Server 進(jìn)行通信)與網(wǎng)絡(luò)協(xié)議(用于與網(wǎng)絡(luò)交換數(shù)據(jù))之間提供了一個(gè)通道。SQL Server 為所有主要的網(wǎng)絡(luò)協(xié)議提供了 Net-Library。這些庫以透明方式將客戶端發(fā)出的請(qǐng)求發(fā)送到 SQL Server,并將服務(wù)器發(fā)出的響應(yīng)返回給客戶端??梢允褂?SQL Server 的客戶端網(wǎng)絡(luò)實(shí)用程序配置適用于特定客戶端的 Net-Library。支持的客戶端協(xié)議包括 TCP/IP、命名管道、NWLink、多協(xié)議 (RPC) 和其他一些協(xié)議。
尤其值得一提的 Net-Library 是共享內(nèi)存 Net-Library。顧名思義,該 Net-Library 使用 Windows 的共享內(nèi)存功能在 SQL Server 客戶端與服務(wù)器之間進(jìn)行通信。顯然,這意味著客戶端與服務(wù)器必須位于同一臺(tái)物理計(jì)算機(jī)上。
由于它能夠繞過物理網(wǎng)絡(luò)堆棧,因此共享內(nèi)存 Net-Library 要比其他 Net-Library 快得多。對(duì)共享內(nèi)存區(qū)域的訪問受到同步對(duì)象的保護(hù),因此客戶端與服務(wù)器之間的通信速度主要受限于 Windows 對(duì)內(nèi)核對(duì)象進(jìn)行調(diào)度的能力,以及進(jìn)程與共享內(nèi)存區(qū)域之間進(jìn)行數(shù)據(jù)復(fù)制的能力?! ?
可以在連接時(shí)將某個(gè)時(shí)間段或(本地)指定為您的計(jì)算機(jī)名,來指示使用共享內(nèi)存 Net-Library。也可以在連接時(shí)為計(jì)算機(jī)\實(shí)例名加上前綴 lpc:,來指示要使用共享內(nèi)存 Net-Library?! ?
注意,即使連接到同一臺(tái)計(jì)算機(jī)上的 SQL Server,共享內(nèi)存 Net-Library 也未必就是最佳的連接選項(xiàng)。在某些情況下,客戶端與服務(wù)器之間的直接連接可能限制它的擴(kuò)展性。與應(yīng)用程序整體體系結(jié)構(gòu)中的其他元素一樣,應(yīng)始終對(duì)給定技術(shù)解決方案進(jìn)行全面的測試,然后才能判斷它是否有良好的擴(kuò)展性以及是否比其他方法更快。
連接
客戶端進(jìn)行連接時(shí),SQL Server 的用戶模式計(jì)劃程序 (UMS) 組件將它指定給特定的計(jì)劃程序。啟動(dòng)時(shí),SQL Server 為系統(tǒng)上的每個(gè) CPU 創(chuàng)建一個(gè)單獨(dú)的 UMS 計(jì)劃程序。當(dāng)客戶端連接到服務(wù)器時(shí),這些客戶端將指定給具有最少連接數(shù)的計(jì)劃程序。連接后,客戶端將不會(huì)更換計(jì)劃程序 - 它將始終受到指定計(jì)劃程序的控制,直到連接斷開。
這對(duì)與服務(wù)器建立多個(gè)連接的應(yīng)用程序很重要。如果應(yīng)用程序性能較差,或無法在它的多個(gè)連接上平均分配工作,則在該應(yīng)用程序的某些連接之間可能造成不必要的 CPU 資源爭用,而其他連接實(shí)際上卻處于空閑狀態(tài)?! ?
例如,應(yīng)用程序與雙處理器計(jì)算機(jī)上運(yùn)行的 SQL Server 建立了四個(gè)連接,連接 1 和 3 隸屬于處理器 0,連接 2 和 4 隸屬于處理器 1。如果應(yīng)用程序的大部分工作通過連接 1 和 3 執(zhí)行,則這兩個(gè)連接將爭用 CPU 0,而 CPU 1 實(shí)際上可能仍處于空閑狀態(tài)。這種情況下,應(yīng)用程序只能斷開某些連接或重新連接某些連接,并希望連接 1 和 3 隸屬于不同的 CPU (連接時(shí)無法指定處理器隸屬關(guān)系),或在它的連接上重新分配工作負(fù)荷,以便每個(gè)連接的工作負(fù)荷更加均衡。當(dāng)然,后一種情況要遠(yuǎn)好于前一種情況?! ?
連接內(nèi)存
SQL Server 為客戶端請(qǐng)求的每個(gè)連接保留三個(gè)數(shù)據(jù)包緩沖區(qū)。每個(gè)緩沖區(qū)的大小取決于 sp_configure 存儲(chǔ)過程指定的默認(rèn)網(wǎng)絡(luò)數(shù)據(jù)包大小。如果默認(rèn)網(wǎng)絡(luò)數(shù)據(jù)包大小小于 8 KB,則這些數(shù)據(jù)包的內(nèi)存將由 SQL Server 的緩沖池提供。否則,該內(nèi)存將由 SQL Server 的 MemToLeave 區(qū)域分配。
值得一提的是,.NET Framework Data Provider for SQL Server 的默認(rèn)網(wǎng)絡(luò)數(shù)據(jù)包大小為 8KB,因此,與托管代碼客戶端連接關(guān)聯(lián)的緩沖區(qū)通常由 SQL Server 的 MemToLeave 區(qū)域提供。而典型的 ADO 應(yīng)用程序卻不同,它們的默認(rèn)數(shù)據(jù)包大小為 4 KB,因此緩沖區(qū)將由 SQL Server 緩沖池分配。
事件
連接后的客戶端請(qǐng)求通常分為兩種廣泛類別:語言事件和遠(yuǎn)程過程調(diào)用。盡管還存在其他類別,但大多數(shù)由 SQL Server 客戶端發(fā)送到服務(wù)器的請(qǐng)求由以下兩種類型之一構(gòu)成:語言事件是從客戶端發(fā)送到服務(wù)器的 一組 T-SQL。例如,如果調(diào)用 ADO Command 對(duì)象(其 CommandText 屬性設(shè)置為 T-SQL 查詢,CommandType 屬性設(shè)置為 adCmdText)的 Execute 方法,則查詢將作為語言事件提交給服務(wù)器。同樣,如果將 CommandType 設(shè)置為 adCmdTable 并調(diào)用 Execute 方法,則 ADO 將生成一個(gè)內(nèi)部查詢(它將選擇 CommandText 屬性標(biāo)識(shí)的表中的所有列),并將它作為語言事件提交給服務(wù)器。另一方面,如果將 CommandType 設(shè)置為 adStoredProc,則調(diào)用 Execute 將使 ADO 向服務(wù)器提交一個(gè)遠(yuǎn)程過程調(diào)用請(qǐng)求,以執(zhí)行 CommandText 屬性中列出的存儲(chǔ)過程?! ?
為何要關(guān)心將請(qǐng)求作為語言事件還是作為 RPC 提交給服務(wù)器呢?通常,這是因?yàn)?RPC 的功能更為出色,特別是在重復(fù)調(diào)用具有不同篩選值的同一查詢時(shí)。盡管 SQL Server 可以自動(dòng)將普通的語言事件請(qǐng)求參數(shù)化,但這種能力非常有限。它從不嘗試自動(dòng)將某些類型的查詢參數(shù)化。這可能會(huì)導(dǎo)致基本相同的查詢產(chǎn)生不同的執(zhí)行,從而只因?yàn)檫@些不同的執(zhí)行提供不同的值,而導(dǎo)致在服務(wù)器上白白浪費(fèi)計(jì)劃編譯的成本。這通常不是您所希望的結(jié)果 - 您希望針對(duì)查詢的第一次執(zhí)行編譯一個(gè)新的計(jì)劃,然后將該計(jì)劃重復(fù)用于具有不同參數(shù)的執(zhí)行?! ?
而 RPC 則通過顯式參數(shù)化查詢(而不是依賴服務(wù)器參數(shù)化查詢)來支持計(jì)劃重復(fù)使用。為過程的第一次執(zhí)行生成一個(gè)計(jì)劃后,隨后的執(zhí)行將自動(dòng)重復(fù)使用該計(jì)劃,即使它們提供的參數(shù)值不同。與通過語言事件調(diào)用存儲(chǔ)過程相比,使用 RPC 調(diào)用存儲(chǔ)過程不僅節(jié)省了計(jì)劃編譯所需的執(zhí)行時(shí)間和 CPU 資源,還增強(qiáng)了 SQL Server 內(nèi)存資源的利用率,因?yàn)樗苊饬巳哂鄨?zhí)行計(jì)劃所浪費(fèi)的內(nèi)存?! ?
在執(zhí)行動(dòng)態(tài) T-SQL 時(shí),通常首選 sp_executesql 而不是 EXEC() 也出于同樣的原因。Sp_executesql 的工作方式是:使用指定的查詢創(chuàng)建一個(gè)存儲(chǔ)過程,然后使用提供的參數(shù)調(diào)用它。與 EXEC() 不同,sp_executesql 提供了一個(gè)允許您參數(shù)化動(dòng)態(tài) T-SQL 并支持計(jì)劃重復(fù)使用的機(jī)制。使用 sp_executesql 執(zhí)行的動(dòng)態(tài)查詢比使用 EXEC() 的查詢能夠在更大程度上避免不必要的編譯和資源消耗。
TDS
從客戶端發(fā)送到 SQL Server 的 RPC、語言事件和其他類型的請(qǐng)求被格式化為稱作表格數(shù)據(jù)流 (TDS) 的 SQL Server 特定數(shù)據(jù)格式。TDS 是 SQL Server 客戶端和服務(wù)器之間使用的“語言”。對(duì)于它的確切格式將不作介紹,但是,如果客戶端要與 SQL Server 進(jìn)行通信,就必須使用 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。其他版本保持向后兼容?! ?
服務(wù)器端 Net-Library
在服務(wù)器端,客戶端請(qǐng)求最初由 SQL Server 為偵聽特定網(wǎng)絡(luò)協(xié)議而建立的偵聽器接收。這些偵聽器由服務(wù)器上的網(wǎng)絡(luò)庫以及服務(wù)器端的 Net-Library(在它們與服務(wù)器之間提供管道)構(gòu)成。您可以使用 SQL Server 網(wǎng)絡(luò)實(shí)用程序配置服務(wù)器偵聽的協(xié)議。SQL Server 與客戶端支持同樣范圍的網(wǎng)絡(luò)協(xié)議(處理群集的情況除外)。對(duì)于群集化的 SQL Server,只有 TCP/IP 和命名管道可用?! ?
SQL Server 為偵聽客戶端請(qǐng)求所使用的每個(gè)網(wǎng)絡(luò)協(xié)議設(shè)置一個(gè)線程,并使用 Windows 的 I/O 完成端口機(jī)制等待和有效處理請(qǐng)求。從網(wǎng)絡(luò)接收到 TDS 數(shù)據(jù)包時(shí),Net-Library 偵聽器將其重新匯編為它們的原始客戶端請(qǐng)求,并將這些請(qǐng)求傳遞到 SQL Server 的命令處理層,即開放式數(shù)據(jù)服務(wù) (ODS)。
將結(jié)果返回到客戶端
服務(wù)器在準(zhǔn)備將特定客戶端請(qǐng)求的結(jié)果返回時(shí),將使用最初接收請(qǐng)求時(shí)所用的網(wǎng)絡(luò)堆棧。它通過服務(wù)器端 Net-Library 將結(jié)果發(fā)送到相應(yīng)的網(wǎng)絡(luò)協(xié)議,隨后這些結(jié)果將通過網(wǎng)絡(luò)以 TDS 格式返回到客戶端。
在客戶端上,客戶端 Net-Library 將從服務(wù)器接收的 TDS 數(shù)據(jù)包從 IPC 層重新匯編,并將其繼續(xù)轉(zhuǎn)發(fā)到初始化該請(qǐng)求的 API 或?qū)ο髱??! ?
小結(jié)
盡管涉及了所有組件,但 SQL Server 客戶端與服務(wù)器之間的往返過程卻相當(dāng)快 - 特別是在使用內(nèi)存 Net-Library 時(shí),亞秒響應(yīng)時(shí)間非常普遍。構(gòu)建和調(diào)整您自己的 SQL Server 客戶端應(yīng)用程序時(shí),以下幾個(gè)與數(shù)據(jù)相關(guān)的問題值得注意:
• 如果應(yīng)用程序與 SQL Server 運(yùn)行在同一臺(tái)計(jì)算機(jī)上,則建議您使用共享內(nèi)存 Net-Library(如果尚未使用它)?;诠蚕韮?nèi)存 Net-Library 的連接通常比其他類型的連接快很多。在注意上述內(nèi)容的同時(shí),還應(yīng):始終全面測試解決方案并將它與其他可行方案進(jìn)行對(duì)比,這樣才能判斷它是否確實(shí)更好或更快。事實(shí)勝于雄辯。
• 由于客戶端在第一次連接時(shí)將指定給特定的 UMS 計(jì)劃程序,并只有在斷開連接后,才會(huì)擺脫該計(jì)劃程序的控制,因此確保在應(yīng)用程序與服務(wù)器建立的連接上均衡分配工作負(fù)荷非常重要。工作負(fù)荷不均衡可導(dǎo)致不必要的 CPU 爭用并降低資源使用率。
• 在服務(wù)器上配置的默認(rèn)網(wǎng)絡(luò)數(shù)據(jù)包大小以及客戶端在連接時(shí)指定的網(wǎng)絡(luò)數(shù)據(jù)包大小將直接影響它們?cè)诜?wù)器上所需的內(nèi)存量和分配內(nèi)存的池。對(duì)服務(wù)器進(jìn)行擴(kuò)展性和速度配置時(shí),應(yīng)記住這一點(diǎn)。還應(yīng)記住,默認(rèn)情況下,ADO.NET 應(yīng)用程序的網(wǎng)絡(luò)數(shù)據(jù)包大小比 ADO 應(yīng)用程序的更大。
• 通常,在向服務(wù)器發(fā)送請(qǐng)求時(shí),應(yīng)首選 RPC 而非語言事件。為此,應(yīng)在使用的 ADO 或 ADO.NET 對(duì)象中設(shè)置相應(yīng)的屬性。
• 執(zhí)行動(dòng)態(tài) T-SQL 時(shí),應(yīng)在可能的情況下使用 sp_executesql 代替 EXEC()。唯一例外的情況是,當(dāng)使用 EXEC() 的功能將查詢片斷連接而成的動(dòng)態(tài)查詢字符串的大小超過單個(gè)本地變量的存儲(chǔ)大小時(shí)(這種情況非常少見)。
• 當(dāng)遇到客戶端問題,并且懷疑它可能和連接服務(wù)器時(shí)所用的對(duì)象庫或 API 有關(guān)時(shí),可以使用的一個(gè)故障排除技巧就是更改所用的客戶端機(jī)制,這樣可以將問題歸結(jié)為特定的組件。例如,假設(shè)您升級(jí) MDAC 并開始在 SQL Server 錯(cuò)誤日志中看到 17805 錯(cuò)誤,這表明客戶端 ADO 應(yīng)用程序發(fā)送的 TDS 數(shù)據(jù)包的格式不正確。您可能嘗試讓應(yīng)用程序轉(zhuǎn)為使用 ODBC 的 OLE DB 提供程序,如果您可以較為容易地做到這一點(diǎn),應(yīng)看看該問題是否與 SQLOLEDB 提供程序有一定的關(guān)系。相反,如果基于 ADO 的應(yīng)用程序一直通過 ODBC 進(jìn)行連接,則可以切換到 SQLOLEDB,看看這是否能解決問題,或至少幫助您縮小問題的范圍。
• 同樣,在對(duì)連接問題進(jìn)行故障排除時(shí),更改正在使用的 Net-Library 有時(shí)會(huì)有所幫助。如果使用 TCP/IP,命名管道也許值得一試。例如,如果 DHCP 服務(wù)器出現(xiàn)問題,并且沒有有效的 IP 地址,則您將無法使用 TCP/IP 連接到 SQL Server。通過切換到命名管道,可以快速地將問題歸結(jié)為 TCP/IP 特定的因素上。另一方面,如果在切換 Net Library 后仍存在同樣的問題,則可以排除 Net-Library 方面的問題。問題的原因可能是服務(wù)器已關(guān)閉,或在您與服務(wù)器之間的某處網(wǎng)絡(luò)基礎(chǔ)設(shè)施無法正常工作。最后,還可以容易地更改應(yīng)用程序使用的 Net-Library,而不必更改應(yīng)用程序本身,這樣就為您提供一個(gè)幫助縮小問題范圍的工具。盡管從長遠(yuǎn)角度而言,使用某一特定 Net-Library 并不可行,但讓客戶端臨時(shí)使用它可以幫助您縮小連接相關(guān)問題的范圍。