本文是微軟的譯文,對應的原文是:https://www.red-gate.com/simple-talk/sql/database-administration/migrating-disk-based-table-memory-optimized-table-sql-server/
以前稱為Hekaton的特性,現在是內存中的OLTP,可以提供非常有用的性能提升,您可以仔細地選擇表來進行內存優化。如何將現有表轉換為內存優化的表呢?這個過程不是很簡單,但是內存中的OLTP表所帶來的好處是值得您付出努力的。Alex Grinberg帶着你的基本知識。
內存中的OLTP,也稱為Hekaton,可以顯著提高OLTP(聯機事務處理)數據庫應用程序的性能。它提高了吞吐量,減少了事務處理的延遲,並且可以幫助改善數據暫態的性能,比如臨時表和ETL期間(提取轉移和加載)。內存中的OLTP是一種內存優化的數據庫引擎,它集成到SQL Server引擎中,並針對事務處理進行了優化。
為了使用內存中的OLTP,您將一個重訪問的表定義為內存優化。內存優化的表是完全事務性的、持久的,並且可以使用與基於磁盤的表相同的方式訪問。一個查詢可以同時引用Hekaton內存優化表和基於磁盤的表。事務可以在Hekaton表和基於磁盤的表中更新數據。只引用內存優化表的存儲過程可以被本機編譯為機器代碼,以便進行進一步的性能改進。內存中的OLTP引擎是為一個非常高的會話而設計的
注意:在SQL Server 2014中引入了內存優化的OLTP表。不幸的是,它有許多限制,因此使用起來很不實際。在SQL Server 2016中,內存優化的表得到了極大的改進,並且大大減少了約束。在SQL Server 2016版本中,只有幾個限制仍然存在。本節提供的所有示例和技術只適用於SQL Server 2016版本。
在開始使用內存優化的表之前,必須使用一個memory最優化數據filegroup來創建一個數據庫。這個filegroup用於存儲SQL Server需要的數據和delta文件對來恢復內存優化的表。盡管創建它們的語法與創建常規filestream filegroup的語法幾乎相同,但它也必須指定包含memory優化數據選項。
創建
CREATE DATABASE [TestDB]
ON PRIMARY
( NAME = N'TestDB_data', FILENAME = N'C:\SQL2016\TestDB_data.mdf'),
FILEGROUP [TestDBSampleDB_mod_fg] CONTAINS MEMORY_OPTIMIZED_DATA DEFAULT
( NAME = N'TestDB_mod_dir', FILENAME = N'C:\SQL2016\TestDB_mod_dir' , MAXSIZE = UNLIMITED)
LOG ON
( NAME = N'TestDBSampleDB_log', FILENAME = N'C:\SQL2016\TestDB_log.ldf' )
如果希望為現有數據庫啟用memory最優化數據選項,則需要使用memory優化數據選項創建一個filegroup,然后將文件添加到filegoup中。

MEMORY_OPTIMIZED property
–當訪問內存優化的表時,不需要從磁盤讀取這些頁面。所有的數據都存儲在內存中。
DURABILITY property
–內存優化的表可以是持久的(schemaanddata),也可以是非持久的(模式)。默認情況下,這些表是持久的(schemaanddata),這些持久表也滿足了所有其他事務需求;它們是原子的、孤立的、一致的。一組檢查點文件(數據和delta文件對),這些文件只用於恢復目的,是使用駐留在內存優化的文件組中的操作系統文件創建的,這些文件組跟蹤對持久表中的數據的更改。這些檢查點文件只是應用程序。非持久的,沒有記錄的,只使用一個選項模式。正如該選項所指出的,表模式將是持久的,即使數據不是。在事務處理過程中,這些表不需要任何IO操作,也不需要對這些表的檢查點文件進行任何操作。只有在SQL
Server運行時,數據才可以在內存中使用。
Indexes
–沒有在內存優化的表上實現集群索引。索引不是作為傳統的b樹存儲的。內存優化的表支持散列索引,存儲為哈希表,其中有鏈表,將散列的所有行連接到相同的值和“范圍”索引,這些索引是使用特殊的bw樹存儲的。使用bw-tree的范圍索引可以用來快速查找范圍謂詞中的符合條件的行,就像傳統的b-樹一樣,但是它是用樂觀的並發控制設計的,沒有鎖定或鎖定。
datetimeoffset
,
geography
,
geometry
,
hierarchyid
,
rowversion
,
xml
,
sql
_
variant
, all User-Defined Types and all legacy LOB data types (including
text
,
ntext
, and
image
)
bit
,
tinyint
,
smallint
,
int
,
bigint
.
Numeric
and
decimal
money
andsmallmoney
float
andreal
- date/time types:
datetime
,smalldatetime
,datetime2
,date
andtime
char
(n),varchar
(n),nchar
(n),nvarchar
(n),sysname
,varchar
(MAX) andnvarchar
(MAX)binary
(n),varbinary
(n) andvarbinary
(MAX)- Uniqueidentifier
創建內存優化的表的語法與創建基於磁盤的表的語法幾乎完全相同,有一些限制,以及一些必要的擴展。其中的一些區別是:
要設置 MEMORY_OPTIMIZED
= ON
The DURABILITY
property is set to SCHEMA_AND_DATA
or SCHEMA_ONLY
(SCHEMA_AND_DATA
is default)
The memory-optimized table must have a PRIMARY KEY
index. If HASH
index is selected for the primary key, then BUCKET_COUNT
must be specified.
在內存優化表中只允許包括主鍵在內的8個索引
IDENTITY
properties have to be set as seed = 1 and increment = 1 only.
在內存優化的表中不允許計算列(Computed Columns)
不加選擇地創建內存優化的表是一個壞主意。同時,對於OS和其他SQL服務器進程也需要內存,將盡可能多的表遷移到內存優化的表中並不是一個好主意。因為內存優化的表是用開放式並發控制設計的,沒有鎖定或被鎖,選擇轉換的最佳表應該是具有“鎖定和鎖定配置文件”(被檢測為會話“攔截器”的表)的表,其中包括最可寫的表(插入、更新和刪除)和最可讀的表,然而,這個列表對於遷移來說還不是很完整。但是,不應該遷移的表是靜態元數據表;違反了內存優化表的限制的表;表的行數更少。
在SQL Server 2016中,可以使用SQL Server
PowerShell生成一個遷移清單。在對象資源管理器中,右鍵單擊一個數據庫,然后單擊Start PowerShell;驗證以下提示出現,執行以下代碼:PS SQLSERVER:\SQL\{Instance Name}\DEFAULT\Databases\{DB Name}>
輸入以下命令(用您的目標文件夾路徑替換C:Temp。如果您喜歡使用更通用的方法$Env:Temp或$Env:報告輸出的路徑,然后驗證這些命令的PowerShell路徑。只需運行$Env:Temp或$Env:PowerShell命令窗口中的路徑,您的本地路徑將被返回)。清單PowerShell命令示例:
Save-SqlMigrationReport –FolderPath “C:\Temp”
注意:如果您需要在單個表上運行遷移報告,那么就展開數據庫節點,展開表節點,右鍵單擊表,然后從彈出菜單中選擇Start PowerShell。
文件夾路徑將被創建,以防它不存在。遷移檢查表報告將為數據庫中的所有表和存儲過程生成,並且報告將出現在FolderPath指定的位置。因此,報告的文件夾路徑將被指定為PowerShell腳本中的FolderPath,以及檢查清單執行的數據庫名稱。在這個例子中,它是C:\Temp\Northwind.
檢查表報告可以指出,已經超過了內存優化表的一個或多個數據類型限制。但是,這並不意味着不能將表遷移到內存優化的表中。報告指出每一列是否滿足了成功的標准,如果沒有,則提示如果表對遷移很重要,那么如何糾正問題。例如,一個數據庫有一個表testdisk。為了演示的目的,我們將在稍后的報告中看到這個表,其中包含了大量的遷移違規行為。
------------------------------------------------------------------------------------------------------------------------------------------------
--以下表跟該篇文章沒有關系,僅僅是說明如何在內存表上面加索引:
CREATE TABLE [dbo].[Bobcat_AccessLog]( id uniqueidentifier not null default NEWSEQUENTIALID(), [SN] [varchar](50) NOT NULL, [StationID] [varchar](60) NOT NULL, [LogTime] [datetime] NOT NULL, CONSTRAINT PK_Bobcat_AccessLog_ID PRIMARY KEY NONCLUSTERED HASH ( ID )WITH (BUCKET_COUNT = 100) , INDEX [IX_AccessLog_LTime] NONCLUSTERED ([LogTime]), INDEX [IX_AccessLog_SN] NONCLUSTERED ([SN],[StationID]) ) WITH ( MEMORY_OPTIMIZED = ON , DURABILITY = SCHEMA_only )
------------------------------------------------------------------------------------------------------------------------------------------------
- XMLData column have XML data type
- SumOrder is Computed Column
- Description column is SPARSE
- ID have IDENTITY seed value 10000
根據這份報告,所有的違規行為都必須得到糾正,或者不能遷移表格。讓我們修正所有這些違規行為:
XMLData列將被轉換為nv(MAX)數據類型;這就是XML的本質。但是,當應用程序或數據庫沒有實現UNICODE時,可以考慮VARCHAR(MAX)數據類型。
SumOrder是計算的列,其中的值通過公式ProductID列進行計算,並使用OrderQty列(公式ProductID+OrderQty僅為演示目的而創建)。ProductID和OrderQty列都有int數據類型。因此,SumOrder列從ProductID和OrderQty列中繼承了int數據類型(如何糾正計算的列問題,將在“修復計算列問題”小節中解釋)。
Description列,為了糾正這個問題,只需刪除稀疏選項
ID
column IDENTITY
:將對應的seed value設置成1
在實現了所有的更正之后,testmemory內存優化表的DDL腳本將會是:
GB的內存;核聚變(固態)驅動器。但是,內存優化的表執行的速度是磁盤表的兩倍多。
The next step is to create an INSTEAD OF
trigger for the vw_TEST_Memory
view, and use this view to insert new rows. For example:
With this option, the SumOrder column will preserve the formula. However, the insert process will lose some speed.
Let’s review more complex scenario: an application code implementing a User-Defined Table Type (UDTT) and the Stored Procedure. To achieve the maximum benefit of an In-Memory table, we need to implement it in the Natively Compiled Stored Procedure. For a disk table, the User-Defined Table Type has following create syntax:
For an In-Memory table, the UDTT option MEMORY_OPTIMIZED
has to be enabled, and the UDTT must have an index. Without those two options, the UDTT can be created, but SQL Server will raise an error when the UDTT is bound to the Natively Compiled Stored Procedure. Here is a sample error massege:
Create UDTT DDL code:
Once the UDTT is created, we can create a Natively Compiled Stored Procedure (link to see details about Natively Compiled Stored Procedure https://msdn.microsoft.com/en-us/library/dn133184.aspx). For example:
To migrate the regular stored procedure to the Natively Compiled Stored Procedure, the following options are required need to be included:
- After parameters list (if it exists) add WITH NATIVE_COMPILATION, SCHEMABINDING options
- The T-SQL code body surrounded with BEGIN ATOMIC WITH (TRANSACTION ISOLATION LEVEL=snapshot, LANGUAGE=N’us_english’) … END . Where the transaction isolation level can be selected from supported levels (https://msdn.microsoft.com/en-us/library/dn133175.aspx):
- SNAPSHOT
- REPEATABLE READ
- SERIALIZABLE
- READ COMMITTED
遷移現有DML觸發器
- The T-SQL code body surrounded with BEGIN ATOMIC WITH (TRANSACTION ISOLATION LEVEL=snapshot, LANGUAGE=N’us_english’) … END
例如,磁盤表觸發器的代碼
沒有DDL觸發器支持內存中的OLTP表。在線書籍向SQL Server dba和開發人員展示了以下信息:
如果數據庫或服務器有一個或多個在createtable上定義的DDL觸發器或任何包含它的事件組,那么您就不能創建內存優化的表。如果數據庫或服務器有一個或多個DDL觸發器定義在droptable或任何包含它的事件組上,那么您就不能刪除一個內存優化的表。
如果在createprocedure、dropprocedure或任何包含這些事件的事件組中存在一個或多個DDL觸發器,就無法創建本地編譯的存儲過程。
結論
內存中的OLTP表是在SQL Server 2014中引入的。但是,在內存OLTP表中使用的大量限制實際上是不可能的。值得慶幸的是,在SQL Server
2016中,許多限制都被消除了,這使得我們可以開始在數據庫中實現內存中的OLTP表。正如您在本文中所讀到的,將磁盤表遷移到內存中的OLTP表所需要的過程並不簡單,並且需要進行分析,才能最終決定遷移。但是,內存中的OLTP表所帶來的好處是值得的