適用於:SQL Server
Azure SQL 資料庫
Azure SQL 受控執行個體
經記憶體最佳化的資料表是使用 CREATE TABLE (Transact-SQL) 所建立。
記憶體最佳化資料表預設為完全持久。就像 (傳統) 磁碟資料表上的交易一樣,記憶體最佳化資料表上的交易為完全不可部分完成、一致、隔離且持久 (ACID)。 經記憶體最佳化的資料表和原生編譯的預存程序僅支援一部份 Transact-SQL 功能。
自 SQL Server 2016 開始,Azure SQL Database 中不限制針對記憶體內部 OLTP 的 定序或字碼頁 。
記憶體最佳化資料表的主要存放區是主記憶體。 資料表中的資料列可從記憶體讀取,並且可寫入記憶體。 資料表資料的第二個副本保留在磁碟上,但僅做為持久性用途。 如需有關持久資料表的詳細資訊,請參閱 建立及管理記憶體最佳化物件的儲存體 。 記憶體優化數據表中的數據只會在資料庫復原期間從磁碟讀取(例如,在伺服器重新啟動之後)。
為了更進一步提升效能,記憶體中 OLTP 支援延遲交易持久性的持久資料表。 延遲的持久交易會在交易認可並控制權傳回給客戶端之後不久儲存至磁碟。 為了換取提升的效能,在伺服器當機或故障轉移時,未保存至磁碟的認可交易會遺失。
除了預設的持久記憶體優化數據表之外,SQL Server 也支援非持久性記憶體優化數據表,這些數據表不會記錄,而且其數據不會保存在磁碟上。 這表示這些數據表上的交易不需要任何磁碟 IO,但如果發生伺服器當機或故障轉移,數據就會遺失。
記憶體內部 OLTP 與 SQL Server 整合,以便在開發、部署、管理能力及支援能力等各方面提供完美無瑕的體驗。 資料庫可以包含位於記憶體中以及以磁碟為基礎的物件。
記憶體最佳化資料表中的資料列會建立版本。 這表示,資料表的每個資料列可能有多個版本。 所有資料列版本都是在相同資料表資料結構中維護。 只要使用資料列版本設定功能,就能在同一個資料列並行讀取和寫入。 如需並行讀取和寫入同一個資料列的詳細資訊,請參閱 Transactions with Memory-Optimized Tables與記憶體最佳化資料表的交易。
下圖說明多重版本設定。 此圖顯示一個資料表包含三個資料列,每個資料列各有不同的版本。
該資料表具有三個資料列:r1、r2 和 r3。 r1 有三個版本、r2 有兩個版本,而 r3 有四個版本。 相同數據列的不同版本不一定佔用連續的記憶體位置。 不同的資料列版本可能分散在資料表資料結構中。
記憶體最佳化的資料表資料結構可以視為資料列版本的集合。 磁碟基礎的資料表中的資料列是以頁面與範圍方式組織,其中個別資料列使用頁碼和頁面位移來定址,而記憶體最佳化資料表中的資料列版本則是使用 8 位元組記憶體指標來定址。
記憶體最佳化資料表中的資料,可使用下列兩種方式的其中一種進行存取:
透過原生編譯的預存程序。
可透過解譯的 Transact-SQL 在不使用原生編譯的預存程序時操作。 這些 Transact-SQL 陳述式可能在解譯的預存程序之內,或者可能是臨機操作 Transact-SQL 陳述式。
存取記憶體最佳化資料表中的資料
從原生編譯的預存程序可以最有效率地存取記憶體最佳化資料表 (原生編譯預存程序)。 也可以使用 (傳統) 解譯的 Transact-SQL,存取經記憶體最佳化的資料表。 解譯的 Transact-SQL 是指在不使用原生編譯預存程序的情況下,存取經記憶體最佳化的資料表。 解譯的 Transact-SQL 存取範例包括從 DML 觸發程序、臨機操作 Transact-SQL 批次、檢視和資料表值函式,存取經記憶體最佳化的資料表。
下表摘要說明各種物件的原生和解譯 Transact-SQL 存取。
功能 | 使用原生編譯的預存程序存取 | 解譯的 Transact-SQL 存取 | CLR 存取 |
---|---|---|---|
記憶體最佳化資料表 | 是 | 是 | 否1 |
記憶體最佳化資料表類型 | 是 | 是 | 否 |
原生編譯的預存程序 | 現在支援原生編譯預存程序的巢狀項目。 只要參考的預存程序也是原生編譯的,您就可以在預存程序中使用 EXECUTE 語法。 | 是 | 否* |
1您無法從內容連接存取記憶體優化數據表或原生編譯預存程式(執行 CLR 模組時從 SQL Server 連線)。 不過,您可以建立及開啟另一個連接,並從中存取記憶體最佳化資料表和原生編譯預存程序。
記憶體優化數據表中的敏感數據可以使用 Always Encrypted 來保護。 適用下列限制:
- 使用 具有安全記憶體保護區的 Always Encrypted 時,不支援針對記憶體優化數據表中的數據行使用已啟用記憶體保護區的密鑰。 這表示無法使用就地加密,且初始加密是在用戶端上完成。
- 在原生編譯模組中參考數據表時,記憶體優化數據表中的任何數據行都不支援 Always Encrypted。
效能和延展性
下列因素會影響可使用 In-Memory OLTP 達成的效能提升:
通信: 使用許多簡短預存過程呼叫的應用程式,相較於預存過程中實作較少呼叫且更多功能的應用程式,效能提升可能會較小。
「Transact-SQL 執行」:記憶體內部 OLTP 使用原生編譯的預存程序時可達到最佳效能,而不是使用解譯的預存程序或查詢執行。 從這樣的預存程序存取記憶體最佳化資料表頗有助益。
範圍掃描和點查閱的比較:記憶體最佳化的非叢集索引可支援範圍掃描和排序掃描。 對於點查閱而言,記憶體最佳化雜湊索引的效能比記憶體最佳化的非叢集索引更好。 記憶體最佳化非叢集索引的效能比以磁碟為基礎的索引更好。
- 自 SQL Server 2016 起,記憶體最佳化資料表的查詢計畫能以平行方式掃描資料表。 這可改善分析查詢的效能。
- 雜湊索引在 SQL Server 2016 也能以平行方式掃描。
- 非叢集索引在 SQL Server 2016 也能以平行方式掃描。
索引作業: 索引作業不會記錄,而且只存在於記憶體中。
並行:效能受到引擎層級並行 (例如閂鎖競爭或封鎖) 影響的應用程式改用記憶體中 OLTP 之後,將可大幅改善其效能。
下表列出關聯式資料庫中經常發現的效能和延展性問題,以及記憶體中 OLTP 如何改善效能。
問題 | 記憶體中 OLTP 影響 |
---|---|
效能 高資源(CPU、I/O、網路或記憶體)使用量。 |
中央處理器 相較於解譯的預存程式,原生編譯預存程式可能會大幅降低 CPU 使用量,因為它們需要較少的指示來執行 Transact-SQL 語句。 In-Memory OLTP 可協助減少擴展型工作負載的硬體投資,因為一部伺服器可能會提供多部伺服器的吞吐量。 I/O (輸入/輸出) 如果處理資料或索引頁面時遭遇 I/O 瓶頸,記憶體中 OLTP 可減少瓶頸。 此外,In-Memory OLTP 物件的檢查點是連續的,而且不會導致 I/O 作業突然增加。 不過,如果效能關鍵資料表的工作集無法容納於記憶體中,In-Memory OLTP 不適用,因為它需要資料常駐於記憶體。 如果在記錄時遭遇 I/O 瓶頸,記憶體中 OLTP 可減少瓶頸,因為它進行的記錄較少。 如果將一個或多個記憶體最佳化資料表設為非持久資料表,就可以消除記錄資料的作業。 記憶體 In-Memory OLTP 不提供任何效能優點。 此外,記憶體中 OLTP 可能會對記憶體造成額外的壓力,因為物件需駐留在記憶體中。 網路 In-Memory OLTP 不提供任何效能優點。 資料需要從資料層到應用程式層之間的通訊。 |
延展性 SQL Server 應用程式中大多數的縮放問題都是由並行問題所造成,例如鎖定、閂鎖和執行緒同步鎖定中的競爭。 |
閂鎖競爭 典型的案例是以索引鍵順序並行插入資料列時,競爭索引的最後一頁。 由於 In-Memory OLTP 在存取數據時不會使用閂鎖,因此會完全移除與閂鎖爭用相關的擴展性問題。 執行緒同步鎖定競爭 因為 In-Memory OLTP 在存取數據時不需要使用閂鎖,完全消除了與旋轉鎖爭用相關的可擴展性問題。 鎖定相關的競爭 如果資料庫應用程式在讀取和寫入作業之間發生封鎖問題,記憶體中 OLTP 可解決封鎖問題,因為它使用新的開放式並行控制形式來實作所有交易隔離層級。 In-Memory OLTP 不會使用 TempDB 來儲存資料列版本。 如果由於兩項寫入作業之間的衝突導致延展問題發生,例如兩項並行交易嘗試更新同一個資料列,記憶體中 OLTP 會讓其中一項交易成功,而讓另一項交易失敗。 必須明確或隱含地重新提交失敗的交易,才能重試交易。 不論是哪一種情況,您都必須變更應用程式。 如果您的應用程式在兩個寫入作業之間遇到常見的衝突,開放式鎖定的值會減少。 應用程式不適合 In-Memory OLTP。 大多數 OLTP 應用程式不會發生寫入衝突,除非該衝突是鎖升級的結果。 |
記憶體最佳化資料表中的資料列層級安全性
記憶體最佳化資料表支援資料列層級安全性 。 將資料列層級安全性原則套用到記憶體最佳化資料表基本上與磁碟資料表相同,唯一差異在於當作安全性述詞使用的內嵌資料表值函式必須為原生編譯 (使用 WITH NATIVE_COMPILATION 選項建立)。 如需詳細資料,請參閱 資料列層級安全性 主題中的 跨功能的相容性 一節。
記憶體優化資料表可以使用各種不可或缺的內建安全功能,這些功能對於列層級安全性至關重要。 如需詳細資料,請參閱 Built-in Functions in Natively Compiled Modules(原生編譯模組中的內建函數)。
EXECUTE AS CALLER - 所有原生模組現在都支援並使用 EXECUTE AS CALLER,即使未指定提示也一樣。 這是因為預期所有數據列層級安全性述詞函式都使用 EXECUTE AS CALLER,因此函式及其內建函式都會在呼叫使用者的內容中進行評估。
對呼叫端進行權限檢查會造成 EXECUTE AS CALLER 的效能受到輕微 (大約 10%) 影響。 如果模組明確指定 EXECUTE AS OWNER 或 EXECUTE AS SELF,則會避免這些許可權檢查及其相關聯的效能成本。 不過,將這些選項與上述內建函數搭配使用,會因為必要的上下文切換而造成較大的效能下降。
案例
如需記憶體內部 OLTP 可以改善效能之典型案例的簡短討論,請參閱記憶體內部 OLTP。