SQL Server 連接共用 (ADO.NET)

適用於 .NET Framework .NET .NET 標準

下載 ADO.NET

連接到資料庫伺服器通常需要執行幾個很費時的步驟。 必須先建立實體通道,例如通訊端或具名管道,接著完成與伺服器的初始交握、剖析連線字串資訊、通過伺服器對連線的驗證,並檢查是否要加入目前的交易等等。

實際上,大部分應用程式僅使用一個或幾個不同的連接組態。 這表示應用程式執行期間,將會重複開啟及關閉許多相同的連接。 為了將開啟連線的成本降至最低,Microsoft SqlClient Data Provider for SQL Server 使用稱為「連接共用」的最佳化技術。

連接共用可減少開啟新連接的必要次數。 「共用器」會維護實體連線的擁有權。 它會針對每個指定的連線組態維持一組作用中的連線,以管理連線。 只要使用者針對連接呼叫 Open,共用器便會查看集區中是否有可用的連接。 如果共用的連接可用,則共用器會將其傳回至呼叫端,而不會開啟新的連接。 當應用程式在連線上呼叫 Close 時,集區管理程式會將該連線返回已集區的使用中連線集合,而不是將它關閉。 連接一旦傳回至集區,便已備妥在下一次 Open 呼叫中重複使用。

只能共用具有相同組態的連接。 Microsoft SqlClient Data Provider for SQL Server 會同時保留數個集區,每個設定各一個。 使用整合安全性時,會按連接字串及 Windows 識別將連接分成多個集區。 連接也會根據是否登記於異動中而進行共用。 當使用 ChangePassword 時,SqlCredential 執行個體會影響連線集區。 即使使用者 ID 和密碼相同,不同的 SqlCredential 執行個體仍將使用不同的連線集區。

共用連接可顯著提高應用程式的效能及延展性。 預設會在 Microsoft SqlClient Data Provider for SQL Server 中啟用連接共用。 除非您明確停用,否則在應用程式中開啟及關閉連接時,共用器會對連接進行最佳化。 您也可以提供數個連接字串修飾參數,以控制連線集區行為。 如需詳細資訊,請參閱本主題後文的「使用連線字串關鍵字控制連線集區」。

重要

啟用連接共用時,如果發生逾時錯誤或其他登入錯誤,將會擲回例外狀況,而後續的連線嘗試將在接下來的 5 秒 (「blocking period」) 失敗。 如果應用程式嘗試在封鎖期間內連接,將再次擲回第一個例外狀況。 封鎖期間結束後的後續失敗將導致新的封鎖期間,此期間為前一個封鎖期間的兩倍,最多可達 1 分鐘 (最大值)。

注意

根據預設,"blocking period" 機制不適用 Azure SQL Server。 您可以透過修改 PoolBlockingPeriod 中的 ConnectionString 屬性來變更此行為,但 .NET Standard 除外。

集區建立及指派

第一次開啟連接時,會根據精確的比對演算法建立連接集區,該演算法可將集區與連接中的連接字串相關聯。 每個連接集區與不同的連接字串相關聯。 開啟新連接時,如果連接字串與現有集區並不完全相符,則會建立新集區。

注意

連線集區會依 處理序、應用程式定義域及 連接字串 分別建立;而在使用整合式安全性時,也會依 Windows 身分識別 分別建立。 連線字串也必須完全相符;相同連線若提供的關鍵字順序不同,將會分別納入不同的連線集區。

注意

如果連接字串中未指定 MinPoolSize 或其指定為零,則會在一段非作用中期間後關閉集區中的連接。 不過,如果指定的 MinPoolSize 大於零,則在卸載 AppDomain 且處理序結束之前,不會損毀連接集區。 維護非作用中或空的集區僅會導致最小的系統負荷量。

注意

發生致命錯誤時 (例如容錯移轉),集區會自動清空。

在下列 C# 範例中,會建立三個新的 SqlConnection 物件,但是只需要兩個連接集區來管理它們。 請注意,第一個及第二個連接字串的不同之處在於指派給 Initial Catalog 的值不同。

using (SqlConnection connection = new SqlConnection(
    "Integrated Security=SSPI;Initial Catalog=Northwind"))
        {
            connection.Open();
            // Pool A is created.
        }

    using (SqlConnection connection = new SqlConnection(
    "Integrated Security=SSPI;Initial Catalog=pubs"))
        {
            connection.Open();
            // Pool B is created because the connection strings differ.  
        }

    using (SqlConnection connection = new SqlConnection(
    "Integrated Security=SSPI;Initial Catalog=Northwind"))
        {  
            connection.Open();
            // The connection string matches pool A.  
        }

新增連線

針對每個唯一連接字串可建立連接集區。 建立集區時,會建立多個連接物件並加入集區,以滿足最小集區大小需求。 視需要將連線新增至集區,最多可達指定的集區大小上限 (預設值為 100)。 當連線被關閉或釋放時,會回到集區中。

要求 SqlConnection 物件時,如果存在可用的連接,則會從集區取得該物件。 若要連接可用,則連接必須未使用、具有相符的異動內容或不與任何異動內容關聯,並具有到伺服器的有效連結。

連線集區管理程式會在連線釋放回集區後重新分配這些連線,以滿足連線要求。 如果已達到最大集區大小,但仍沒有可用的連接,則會將要求排入佇列。 接著,連線集區管理程式會嘗試回收任何可用的連線,直到逾時為止 (預設值為 15 秒)。 如果連線逾時之前,連線集區管理程式無法滿足該請求,則會擲回例外。

警告

我們強烈建議您在使用完畢後務必關閉連線,讓連線返回集區。 您可以使用 Close 物件的 DisposeConnection 方法,或透過開啟 C# 中之 using 陳述式或 Visual Basic 中之 Using 陳述式內的所有連線來執行此動作。 未明確關閉的連線可能不會被加入集區或返回集區。 如需詳細資訊,請參閱 using 陳述式操作說明:處置系統資源 (Visual Basic)。

注意

請勿在您類別的 Finalize 方法中,對 ConnectionDataReader 或任何其他受控物件呼叫 CloseDispose。 在完成項目中,僅釋放您的類別直接擁有的非受控資源。 如果類別未擁有任何 Unmanaged 資源,請不要在類別定義中包含 Finalize 方法。 如需詳細資訊,請參閱垃圾收集

如需與開啟及關閉連線相關聯之事件的詳細資訊,請參閱 SQL Server 文件中的 Audit Login 事件類別Audit Logout 事件類別

移除連線

如果有設定 LoadBalanceTimeout (或 Connection Lifetime),當連接傳回集區時,會將其建立時間與目前時間進行比較,如果該時間範圍 (秒) 超過 LoadBalanceTimeout 指定的值,則會損毀連接。 這在叢集組態中很有用,可在執行中伺服器與剛連線的伺服器之間強制負載平衡。

如果未設定 LoadBalanceTimeout(或連線存留期)(預設值 = 0),則連線集區管理程式會在連線閒置約 4-8 分鐘後(以隨機的兩階段方式),將該連線自集區中移除;或者,如果集區管理程式偵測到與伺服器的連線已中斷,也會將其自集區中移除。

注意

只有在嘗試與伺服器通訊之後,才能偵測到中斷的連線。 如果發現連接已不再連接到伺服器,則會將其標記為無效。 無效的連線只有在關閉或回收時,才會從連線集區中移除。

如果某個連線是連到已消失的伺服器,即使連線集區管理程式尚未偵測到該中斷的連線並將其標記為無效,仍可能會從集區中取出這個連線。 這是因為,檢查連線是否仍然有效所帶來的額外負擔,會因需要再與伺服器進行一次往返通訊,而使使用連線集區器的好處消失。 發生這種情況時,第一次嘗試使用該連線時,會偵測到連線已中斷,並擲回例外。

清空集區

Microsoft SqlClient Data Provider for SQL Server 引進了兩種清除集區的新方法:ClearAllPoolsClearPoolClearAllPools 會清除指定提供者的連接集區, ClearPool 會清除與特定連接相關聯的連接集區。

注意

如果呼叫時有正在使用中的連接,則會適當地標記它們。 當它們關閉時,會將其捨棄,而不是歸還到集區。

交易支援

連線會從集區中取出,並根據交易內容進行指派。 除非已在連接字串中指定 Enlist=false,否則連接集區會確保將連接登記在 Current內容中。 當連線關閉並以已加入 System.Transactions 交易的狀態傳回集區時,系統會先將其保留;如此一來,下一次對該連線集區提出且使用相同 System.Transactions 交易的要求時,如果該連線仍可用,就會傳回同一個連線。 如果發出這類要求,而且沒有可用的集區連線,則會從集區的非交易部分取出一個連線,並將其登錄到交易中。 如果集區的兩個區域都沒有可用的連線,則會建立並登錄新的連線。

連接關閉時,會根據其異動內容將其釋放回集區,並置於適當的子區塊中。 因此,即使分散式交易仍處於暫止狀態,您仍可以關閉連接,而不會產生錯誤。 這可讓您稍後再認可或中止分散式異動。

使用連線字串關鍵字控制連線集區

ConnectionString 物件的 SqlConnection 屬性支援連接字串索引鍵/值配對,這些配對可用於調整連接共用邏輯的行為。 如需詳細資訊,請參閱ConnectionString

集區碎片化

集區碎裂是許多 Web 應用程式中的常見問題,因為這些應用程式可能會建立大量的集區,而且直到處理序結束時才會釋放這些集區。 這會使大量連接保持開啟狀態並消耗記憶體,導致效能降低。

整合式安全性導致的集區分裂

連線會依據連接字串加上使用者身分進行集區化。 因此,如果您在網站上使用基本驗證或 Windows 驗證,並使用整合安全性登入,則每個使用者會獲得一個集區。 雖然這會提升單一使用者之後續資料庫要求的效能,但該使用者無法利用其他使用者的連接。 這也會導致每個使用者至少存在一個與資料庫伺服器的連接。 這是特定 Web 應用程式架構的副作用,是開發人員在針對安全性與稽核需求方面必須考量的問題。

由多個資料庫造成的集區碎裂

許多網際網路服務提供者在單一伺服器上裝載多個網站。 他們可以使用單一資料庫確認表單驗證登入,然後開啟該使用者或使用者群組之特定資料庫的連接。 驗證資料庫的連接可供所有人共用和使用。 不過,每個資料庫存在單獨的連接集區,而這會增加伺服器連接的數目。

這也是應用程式設計的副作用。 有一個比較簡單的方法可避免此副作用,同時不會影響連接到 SQL Server 時的安全性。 不要為每個使用者或群組分別連線到個別的資料庫,而是連線到伺服器上的同一個資料庫,然後執行 Transact-SQL USE 陳述式,以切換至所需的資料庫。

下列程式碼片段會示範如何建立與 master 資料庫的初始連接,然後切換至 databaseName 字串變數中指定之目標資料庫。

// Assume that connectionString connects to master.  
using (SqlConnection connection = new SqlConnection(connectionString))
using (SqlCommand command = new SqlCommand())
{
    connection.Open();
    command.Connection = connection;
    command.CommandText = "USE DatabaseName";
    command.ExecuteNonQuery();
}

應用程式角色與連接共用

呼叫 sp_setapprole 系統預存程序來啟動 SQL Server 應用程式角色後,便無法重設該連接的安全性內容。 不過,啟用共用後,連接會傳回到集區,並且在重複使用共用連接時發生錯誤。

應用程式角色替代方案

建議您善加利用安全機制,以取代應用程式角色。