CLR 整合架構 - CLR 主控環境

適用於:SQL ServerAzure SQL 受控執行個體

SQL Server與 .NET Framework Common Language Runtime (CLR) 整合可讓資料庫程式設計人員使用 Visual C#、Visual Basic .NET 和 Visual C++ 等語言。 程式設計人員可以使用這些語言所撰寫的商務邏輯種類包括函數、預存程序、觸發程序、資料類型和彙總。

CLR 功能垃圾收集的記憶體、先占式執行緒、中繼資料服務 (類型反映) 、程式碼驗證性和程式碼存取安全性。 CLR 會使用中繼資料來找出並載入類別、配置記憶體中的執行個體、解析方法引動過程、產生機器碼、強制使用安全性,和設定執行階段內容界限。

CLR 和SQL Server在處理記憶體、執行緒和同步處理的方式中,與執行時間環境不同。 本文說明這兩個執行時間整合的方式,以便統一管理所有系統資源。 本文也涵蓋 CLR 程式碼存取安全性 (CAS) 和SQL Server安全性整合的方式,為使用者程式碼提供可靠且安全的執行環境。

CLR 架構的基本概念

在 .NET Framework 中,程式設計人員會以高階語言撰寫,實作定義其結構 (例如,類別的欄位或屬性) 和方法的類別。 這些方法中有部分可以是靜態函數。 編譯器會產生稱為元件的檔案,其中包含 Microsoft 中繼語言中編譯的程式碼 (MSIL) ,以及包含所有相依元件參考的資訊清單。

注意

組件是 CLR 架構中的重要元素。 它們是 .NET Framework 中,應用程式程式碼之封裝、部署與版本控制的單位。 您可以使用組件來部署資料庫內部的應用程式程式碼,並提供一個統一的方式來管理、備份與還原完整資料庫應用程式。

組件資訊清單包含組件的相關中繼資料,描述程式中定義的所有結構、欄位、屬性、類別、繼承關聯性、函數以及方法。 資訊清單會建立組件識別、指定組成組件實作的檔案、指定組成組件的類型和資源、列舉對其他組件的編譯時間相依性,並且指定組件正確執行所需的權限集合。 執行階段時會使用這個資訊來解析參考、強制執行版本繫結原則,以及驗證載入組件的完整性。

.NET Framework 支援註解類別、屬性 (Property)、函數和方法的自訂屬性 (Attribute),以及應用程式可以在中繼資料中擷取的其他資訊。 所有 .NET Framework 編譯器都可以在不解譯的情況下使用這些註解,並將其當做組件中繼資料儲存。 這些註解可以使用與其他任何中繼資料相同的方式檢查。

Managed 程式碼是在 CLR 中以 MSIL 執行,而非直接由作業系統執行。 Managed 程式碼應用程式會取得自動記憶體回收、執行階段類型檢查和安全性支援等 CLR 服務。 這些服務會協助提供統一的 Managed 程式碼應用程式行為 (與平台和語言無關)。

CLR 整合的設計目標

當使用者程式碼在 CLR 裝載的環境中執行時,SQL Server (稱為 CLR 整合) ,則適用下列設計目標:

可靠性 (安全性)

系統不允許使用者程式碼執行危害 Database Engine 程序完整性的作業,例如,彈出要求使用者回應或結束程序的訊息方塊。 使用者程式碼應該無法覆寫 Database Engine 記憶體緩衝區或內部資料結構。

延展性

SQL Server和 CLR 有不同的內部模型,可用於排程和記憶體管理。 SQL Server支援合作式的非先占式執行緒模型,其中線程會定期產生執行,或在等候鎖定或 I/O 時產生執行。 CLR 支援先佔式執行緒模型。 如果在SQL Server內執行的使用者程式碼可以直接呼叫作業系統執行緒基本類型,則它不會妥善整合到SQL Server工作排程器中,而且可能會降低系統的延展性。 CLR 不會區分虛擬和實體記憶體,但SQL Server直接管理實體記憶體,而且必須在可設定的限制內使用實體記憶體。

用於執行緒、排序和記憶體管理的不同模型對於調整為支援數千個並行使用者工作階段的關聯式資料庫管理系統 (RDBMS) 會呈現整合性問題。 此架構應該確認針對執行緒、記憶體和同步處理原始物件直接呼叫應用程式開發介面 (API) 的使用者程式碼不會危害系統的延展性。

安全性

存取資料表和資料行等資料庫物件時,在資料庫中執行的使用者程式碼必須遵循SQL Server驗證和授權規則。 此外,資料庫管理員應該能夠從資料庫中執行的使用者程式碼控制作業系統資源的存取權,例如檔案和網路存取權。 此作法很重要,因為與 Transact-SQL) 這類非受控語言不同的受控程式設計語言 (提供 API 來存取這類資源。 系統必須提供安全的方式,使用者程式碼才能存取 Database Engine 進程以外的電腦資源。 如需相關資訊,請參閱 CLR Integration Security

效能

在 Database Engine 中執行的 Managed 使用者程式碼應該具有相當於伺服器外部執行之相同程式碼的計算效能。 從受控使用者程式碼存取資料庫的速度不如原生 Transact-SQL。 如需詳細資訊,請參閱 CLR 整合的效能

CLR Services

CLR 提供許多服務,以協助達成 CLR 與SQL Server整合的設計目標。

類型安全性驗證

類型安全程式碼是只以妥善定義的方式存取記憶體結構的程式碼。 例如,在有一個有效的物件參考上,類型安全程式碼可以存取和實際欄位成員對應的固定位移上的記憶體。 然而,如果程式碼存取的記憶體,是位於該物件所屬記憶體範圍以內或以外的任意位移時,即不是類型安全。 當組件載入 CLR 時,在要使用 Just-In-Time (JIT) 編譯來編譯的 MSIL 前,執行階段會執行一個檢查程式碼來判斷其類型安全性的驗證階段。 成功通過此驗證的程式碼稱為可驗證類型安全 (verifiably type-safe) 的程式碼。

應用程式網域

CLR 支援應用程式網域的概念,做為主機處理序內的執行區域,其中可以載入與執行 Managed 程式碼組件。 應用程式網域界限提供組件間的隔離。 組件會透過靜態變數與資料成員的可見性,以及動態呼叫程式碼的能力隔離。 應用程式網域也是載入和卸載程式碼的機制。 程式碼只能透過卸載應用程式網域來從記憶體卸載。 如需詳細資訊,請參閱 應用程式域和 CLR 整合安全性

程式碼存取安全性 (CAS)

CLR 安全性系統提供一種方式來控制 Managed 程式碼可以透過指派權限給程式碼來執行的作業種類。 程式碼存取權限是根據程式碼識別指派的 (例如,組件的簽章或程式碼的來源)。

CLR 會提供電腦管理員可以設定的電腦通用原則。 此原則會針對在電腦上執行的任何 Managed 程式碼定義權限授權。 此外,主機可以使用主機層級的安全性原則,例如SQL Server來指定 Managed 程式碼的其他限制。

如果 .NET Framework 中的 Managed API 會公開受程式碼存取權限保護之資源的作業,則 API 將會在存取資源前要求權限。 此要求會使 CLR 安全性系統在呼叫堆疊中觸發每個程式碼單位 (組件) 的完整檢查。 只有在整個呼叫鏈結具有許可權時,才會授與資源的存取權。

請注意,在使用 Reflection.Emit API 動態產生 Managed 程式碼的能力,在 CLR 裝載的環境中不支援SQL Server。 此種程式碼將沒有要執行的 CAS 權限,因此會在執行階段失敗。 如需詳細資訊,請參閱 CLR 整合程式碼存取安全性

主機保護屬性 (HPA)

CLR 會提供一個機制來使用 CLR 主機可能需要的某些屬性,為屬於 .NET Framework 之一部分的 Managed API 加註。 這類屬性的範例包括:

  • SharedState,它可指出 API 是否會公開建立或管理共用狀態 (如靜態類別欄位) 的功能。

  • Synchronization,它可指出 API 是否會公開執行執行緒之間之同步處理的功能。

  • ExternalProcessMgmt,它可指出 API 是否會公開控制主機處理序的方法。

當提供了這些屬性時,主機可以指定 HPA (如 SharedState 屬性) 的清單,在主控環境內應該不允許使用這些 HPA。 在此情況下,CLR 會拒絕使用者程式碼嘗試呼叫 HPA 在禁止清單中加註的 API。 如需詳細資訊,請參閱 主機保護屬性和 CLR 整合程式設計

SQL Server 和 CLR 如何一起運作

本節討論SQL Server如何整合SQL Server和 CLR 的執行緒、排程、同步處理和記憶體管理模型。 特別是,本節會按照延展性、可靠性以及安全性目標來檢查整合效果。 SQL Server基本上會作為 CLR 的作業系統裝載于SQL Server內。 CLR 會呼叫執行緒、排程、同步處理和記憶體管理SQL Server實作的低階常式。 這些常式與其余SQL Server引擎所使用的基本類型相同。 此方法提供數個延展性、可靠性與安全性優勢。

延展性:一般執行緒、排程與同步處理

CLR 會呼叫 SQL Server API 來建立執行緒,同時用於執行使用者程式碼和自己的內部用途。 為了在多個執行緒之間進行同步處理,CLR 會呼叫SQL Server同步處理物件。 這種做法可讓SQL Server排程器線上程等候同步處理物件時排程其他工作。 例如,當 CLR 起始記憶體回收時,其所有執行緒都會等待記憶體回收完成。 由於SQL Server排程器已知正在等候的 CLR 執行緒和同步處理物件,SQL Server可以排程執行未涉及 CLR 之其他資料庫工作的執行緒。 這也可讓SQL Server偵測涉及 CLR 同步處理物件所採取鎖定的死結,並採用傳統技術來移除死結。

Managed 程式碼會在 SQL Server 中預先執行。 SQL Server排程器能夠偵測和停止未產生大量時間的執行緒。 將 CLR 執行緒連結至SQL Server執行緒的能力表示SQL Server排程器可以識別 CLR 中的「失失」執行緒並管理其優先順序。 這種失控的執行緒會暫停,並放回到佇列中。 系統不允許重複識別為失控執行緒的執行緒在給定的期間內執行,讓其他執行中的工作者得以執行。

在某些情況下,長時間執行的 Managed 程式碼會自動產生,有些情況則不會。 在下列情況下,長時間執行的 Managed 程式碼會自動產生:

  • 如果程式碼呼叫 SQL OS (來查詢資料,例如)
  • 如果配置足夠的記憶體來觸發垃圾收集
  • 如果程式碼藉由呼叫 OS 函式進入先占模式

不執行上述任何動作的程式碼,例如只包含計算的緊密迴圈,將不會自動產生排程器,這可能會導致系統中的其他工作負載等候很長。 在這些情況下,開發人員必須藉由呼叫 .NET Framework 的 System.Thread.Sleep () 函式,或使用 System.Thread.BeginThreadAffinity () 明確進入預存模式,以在預期長時間執行的程式碼區段中明確產生。 下列程式碼範例示範如何使用這些方法手動產生。

// Example 1: Manually yield to SOS scheduler.
for (int i = 0; i < Int32.MaxValue; i++)
{
 // *Code that does compute-heavy operation, and does not call into
 // any OS functions.*

 // Manually yield to the scheduler regularly after every few cycles.
 if (i % 1000 == 0)
 {
   Thread.Sleep(0);
 }
}
// Example 2: Use ThreadAffinity to run preemptively.
// Within BeginThreadAffinity/EndThreadAffinity the CLR code runs in preemptive mode.
Thread.BeginThreadAffinity();
for (int i = 0; i < Int32.MaxValue; i++)
{
  // *Code that does compute-heavy operation, and does not call into
  // any OS functions.*
}
Thread.EndThreadAffinity();
延展性:一般記憶體管理

CLR 會呼叫SQL Server基本類型來配置和取消配置其記憶體。 由於 CLR 所使用的記憶體會計入系統的總記憶體使用量,因此SQL Server可以保留在其設定的記憶體限制內,並確保 CLR 和SQL Server不會彼此競爭記憶體。 SQL Server也可以在系統記憶體受限時拒絕 CLR 記憶體要求,並要求 CLR 降低其他工作需要記憶體時使用的記憶體。

可靠性:應用程式網域和無法復原的例外狀況

當 .NET Framework API 中的 Managed 程式碼遇到嚴重的例外狀況 (例如,記憶體不足或堆疊溢位) 時,不一定能夠從這類的失敗復原,也不一定能夠確保其實作的一致且正確的語意。 這些 API 會引發執行緒中止的例外狀況來回應這些失敗。

裝載于 SQL Server 時,這類執行緒中止的處理方式如下:CLR 會偵測發生執行緒中止的應用程式域中的任何共用狀態。 CLR 會藉由檢查同步處理物件是否存在來偵測此情況。 如果在應用程式網域中有共用狀態,則會卸載應用程式網域本身。 卸載應用程式網域時,會停止目前正在該應用程式網域中執行的資料庫交易。 由於共用狀態的存在可能會將這類重大例外狀況的影響擴大至觸發例外狀況的使用者會話,因此SQL Server和 CLR 已採取步驟來降低共用狀態的可能性。 如需詳細資訊,請參閱 .NET Framework 文件集。

安全性:權限集合

SQL Server可讓使用者指定部署至資料庫之程式碼的可靠性和安全性需求。 將元件上傳至資料庫時,元件的作者可以指定該元件的三個許可權集合之一:SAFE、EXTERNAL_ACCESS和 UNSAFE。

功能 SAFE EXTERNAL_ACCESS UNSAFE
程式碼存取安全性 僅限 Execute 對外部資源的 Execute + 存取權 不受限制
程式設計模型限制 沒有限制
可驗證性需求
呼叫機器碼的能力 No

SAFE 是最可靠及安全的模式,其包含了根據允許的程式設計模型的相關限制。 SAFE 組件有被授與足夠的權限來執行、進行運算,以及存取本機資料庫。 SAFE 組件需要是可驗證的類型安全,且不允許呼叫 Unmanaged 程式碼。

UNSAFE 適用於高度信任的程式碼,這類程式碼只能由資料庫管理員建立; 此信任的程式碼沒有程式碼存取安全性的限制,且可以呼叫 Unmanaged 程式碼 (機器碼)。

EXTERNAL_ACCESS 提供了中級安全性選項,可讓程式碼存取在資料庫外部的資源,但是仍然保有 SAFE 的可靠性保證。

SQL Server使用主機層級 CAS 原則層來設定主機原則,根據儲存在SQL Server目錄中的許可權集,授與三組許可權的其中一個。 在資料庫內執行的 Managed 程式碼一定會取得這些程式碼存取權限集合當中的一個。

程式設計模型限制

SQL Server中 Managed 程式碼的程式設計模型牽涉到撰寫函式、程式和類型,通常不需要使用跨多個調用保留的狀態,或跨多個使用者會話共用狀態。 再者,如之前所述,共用狀態的存在可能會造成嚴重的例外狀況,而這些例外狀況會影響應用程式的延展性和可靠性。

基於這些考慮,我們不建議使用靜態變數和SQL Server中使用的類別靜態資料成員。 若為 SAFE 和 EXTERNAL_ACCESS 元件,SQL Server會在 CREATE ASSEMBLY 時間檢查元件的中繼資料,如果找到靜態資料成員和變數的使用,則無法建立這類元件。

SQL Server也不允許呼叫以SharedStateSynchronizationExternalProcessMgmt主機保護屬性標注的.NET Framework API。 這可防止 SAFE 和EXTERNAL_ACCESS元件呼叫任何啟用共用狀態、執行同步處理,以及影響SQL Server程式完整性的 API。 如需詳細資訊,請參閱 CLR 整合程式設計模型限制

另請參閱

CLR 整合安全性
CLR 整合的效能