適用於:SQL Server
Azure SQL 資料庫
Azure SQL 受控執行個體
在 SQL Server 2016 (13.x) 之前,經記憶體最佳化的資料表的 in-row 資料大小不能超過 8,060 個位元組。 不過,自 SQL Server 2016 (13.x) 開始,可在 Azure SQL 資料庫中建立具有多個大型資料行 (例如,多個 varbinary(8000) 資料行) 和 LOB 資料行 (即 varbinary(max)、varchar(max) 和 nvarchar(max)) 的經記憶體最佳化的資料表,並可使用原生編譯的 Transact-SQL (T-SQL) 模組和資料表類型對它們執行作業。
不符合 8,060 位元組資料列大小限制的資料行,會儲存在資料列外的另一個獨立內部資料表中。 每個資料列外資料行都有一個對應的內部資料表,而該內部資料表則有一個非叢集索引。 如需了解用於列外資料行的這些內部資料表詳細資訊,請參閱 sys.memory_optimized_tables_internal_attributes。
在某些情況中,計算資料列和資料表的大小會很有用:
資料表使用的記憶體數量。
資料表所使用的記憶體數量無法精確計算。 許多因素都會影響使用的記憶體數量。 例如以頁面為單位的記憶體配置、區域性、快取和填充等因素。 此外,還包括與使用中交易相關聯或正等待垃圾回收的多個資料列版本。
資料表中的資料和索引所需的大小下限是由
<table size>的計算提供,本文稍後將對此進行討論。計算記憶體用量充其量只是一種估算,因此建議您在部署計畫中納入容量規劃。
資料列的資料大小,以及它是否符合 8,060 個位元組的資料列大小限制? 若要回答這些問題,請使用本文稍後討論的
<row body size>的計算。
記憶體最佳化資料表由一組資料列和索引所組成,而索引包含指向資料列的指標。 下圖說明包含索引和資料列的資料表,這些索引和資料列各自擁有資料列標頭和主體:
計算資料表大小
資料表的記憶體中大小 (以位元組為單位) 計算如下:
<table size> = <size of index 1> + ... + <size of index n> + (<row size> * <row count>)
雜湊索引的大小會在資料表建立時固定,並取決於實際的雜湊桶數量。 索引定義中指定的 bucket_count 會無條件進位為最接近的 2 次方,以取得 實際貯體數。 例如,如果指定的 bucket_count 是 100000,則索引的實際貯體計數為 131072。
<hash index size> = 8 * <actual bucket count>
非叢集索引的大小順序是 <row count> * <index key size>。
資料列大小是將標頭和內容相加後計算得出:
<row size> = <row header size> + <actual row body size>
<row header size> = 24 + 8 * <number of indexes>
計算資料列內容大小
經記憶體最佳化的資料表中的資料列具有下列元件:
資料列標頭包含實作資料列版本設定所需的時間戳記。 資料列標頭也包含索引指標,用來在雜湊桶中實現資料列串鏈(如先前所述)。
資料列主體包含實際資料行資料,包括一些輔助資訊,如可為 Null 的資料行的 null 陣列以及可變長度資料類型的位移陣列。
下圖說明有兩個索引的資料表之資料列結構:
開始和結束時間戳記表示特定資料列版本有效的期間。 在這個區間內開始的交易可以看到這個資料列版本。 如需更多資訊,請參閱使用記憶體最佳化資料表的交易。
索引指標指向屬於雜湊值區之鏈結中的下一個資料列。 下圖說明有兩個資料行 (姓名、城市) 之資料表的結構,其中包含兩個索引,一個是姓名資料行的索引,另一個是城市資料行的索引。
在此圖中,名稱 John 和 Jane 被雜湊至第一個桶。
Susan 會被雜湊到第二個桶。 城市 Beijing 和 Bogota 被雜湊至第一個桶。
Paris 和 Prague 被雜湊到第二個桶。
因此,名稱雜湊索引的鏈結如下:
- 第一個區段:
(John, Beijing);(John, Paris);(Jane, Prague) - 第二個儲存桶:
(Susan, Bogota)
城市索引的鏈結如下:
- 第一個儲存桶:
(John, Beijing)、(Susan, Bogota) - 第二個儲存貯體:
(John, Paris)、(Jane, Prague)
結束時間戳記 ∞ (無限大) 指出這是資料列的目前有效版本。 自從寫入此資料列版本後,該資料列一直未被更新或刪除。
對於大於 200 的時間,資料表包含下列資料列:
| 名稱 | 城市 |
|---|---|
| John | 北京 |
| 珍 | 布拉格 |
不過,開始時間為 100 的任何使用中交易都會看到下列版本的資料表:
| 名稱 | 城市 |
|---|---|
| John | 巴黎 |
| 珍 | 布拉格 |
| 蘇珊 | 波哥大 |
下表中討論 <row body size> 的計算。
資料列主體大小有兩種不同的計算方式,也就是計算的大小和實際大小:
計算的大小以計算的資料列主體大小為代表,用以判斷是否超過資料列的大小限制 8,060 位元組。
實際大小以實際的資料列主體大小為代表,為記憶體內部及檢查點檔案的實際資料列主體儲存體大小。
計算的資料列主體大小及實際的資料列主體大小計算方式相似。 唯一的差異在於 (n)varchar(i) 和 varbinary(i) 資料行大小的計算,如下列資料表底部所反映。 計算的資料列主體大小使用宣告的大小 i 作為資料行的大小,而實際的資料列主體大小使用實際的資料大小。
下表說明資料列主體大小的計算方式,表示為 <actual row body size> = SUM(<size of shallow types>) + 2 + 2 * <number of deep type columns>。
| 區段 | 大小 | 註解 |
|---|---|---|
| 淺層類型資料行 |
SUM(<size of shallow types>)。 個別類型的大小如下所示 (以位元組為單位):bit: 1tinyint: 1smallint: 2int: 4real: 4smalldatetime: 4smallmoney: 4bigint: 8datetime: 8datetime2: 8float: 8money: 8numeric (精確度 <= 18): 8time: 8numeric(精確度 > 18): 16uniqueidentifier: 16 |
|
| 淺資料行邊距 | 可能的值包括:1 如果有深層類型資料行,且淺層資料行的資料總大小為奇數。0 否則 |
深層類型是指 (var)binary 和 (n)(var)char 類型。 |
| 深層類型資料行的位移陣列 | 可能的值包括:0 如果沒有深層類型資料欄2 + 2 * <number of deep type columns> 否則 |
深層類型是指 (var)binary 和 (n)(var)char 類型。 |
| 空值陣列 |
<number of nullable columns> / 8 進位到完整的位元組。 |
陣列中每個可為 null 的資料行都有 1 個位元。 這會向上取整為整數位元組。 |
| 空值陣列填充 | 可能的值包括:1 如果有深層類型資料行,且 NULL 陣列的大小為奇數個位元組。0 否則 |
深層類型是指 (var)binary 和 (n)(var)char 類型。 |
| 填補 | 如果沒有深層類型資料行:0如果有深層類型資料行,則會根據淺層資料行所需的最大對齊,加入 0 到 7 個位元組的填充。 每個淺層資料行的對齊都需等於其大小 (如先前所記載),除了 GUID 資料行需要 1 個位元組 (非 16) 的對齊,而數值資料行一律需要 8 個位元組 (絕不是 16) 的對齊。 會採用所有淺層資料行中最大的對齊需求。 會加入 0 到 7 個位元組的填充,使截至目前的總大小(不包括深度類型資料行)成為所需對齊值的倍數。 |
深層類型是指 (var)binary 和 (n)(var)char 類型。 |
| 固定長度的深層類型資料行 | SUM(<size of fixed length deep type columns>)每個資料行的大小如下所示: i 適用於 char(i) 和 binary(i)。2 * i 用於 nchar(i) |
固定長度深類型資料行是指類型為 char(i)、nchar(i) 或 binary(i) 的資料行。 |
| 可變長度的深層類型資料行計算的大小 | SUM(<computed size of variable length deep type columns>)每個資料行的計算後大小如下所示: i 適用於 varchar(i) 和 varbinary(i)2 * i 用於 nvarchar(i) |
此列僅適用於 計算後的列主體大小。 可變長度的大型類型資料行是指類型為 varchar(i)、nvarchar(i) 或 varbinary(i) 的資料行。 計算的大小是由資料行的最大長度 ( i) 所決定。 |
| 可變長度的深層類型資料行實際大小 | SUM(<actual size of variable length deep type columns>)每個資料行的實際大小如下所示: n,其中 n 是儲存在資料行中的字元數,對於 varchar(i)。2 * n,其中 n 是儲存在資料行中的字元數,對於 nvarchar(i)。n,其中 n 是儲存在資料行中的位元組數,用於 varbinary(i)。 |
此資料列只適用於 實際資料列主體大小。 實際大小取決於該資料列中各資料行所儲存的資料。 |
範例:資料表和資料列大小計算
對於雜湊索引,實際的桶數會向上取整為最接近的 2 的冪次方。 例如,如果指定的 bucket_count 是 100000,則索引的實際貯體計數為 131072。
假設 Orders 資料表具有下列定義:
CREATE TABLE dbo.Orders (
OrderID INT NOT NULL PRIMARY KEY NONCLUSTERED,
CustomerID INT NOT NULL INDEX IX_CustomerID HASH WITH (BUCKET_COUNT = 10000),
OrderDate DATETIME NOT NULL,
OrderDescription NVARCHAR(1000)
)
WITH (MEMORY_OPTIMIZED = ON);
GO
此資料表具有一個雜湊索引與一個非叢集索引 (主索引鍵)。 它也有三個固定長度的數據行和一個可變長度的數據行,其中一個數據行可為 Null(OrderDescription)。 假設 Orders 資料表有 8,379 個資料列,且 OrderDescription 資料行中值的平均長度為 78 個字元。
若要判斷資料表大小,請先判斷索引的大小。 這兩個索引的 bucket_count 都指定為 10000。 這會進位到最接近的 2 的冪次:16384。 因此,Orders 資料表的索引大小總計為:
8 * 16384 = 131072 bytes
剩下的是資料表資料大小,也就是:
<row size> * <row count> = <row size> * 8379
(範例資料表有 8,379 個資料列)。現在我們有:
<row size> = <row header size> + <actual row body size>
<row header size> = 24 + 8 * <number of indices> = 24 + 8 * 1 = 32 bytes
接下來,我們來計算 <actual row body size>:
淺層類型資料行:
SUM(<size of shallow types>) = 4 <int> + 4 <int> + 8 <datetime> = 16淺層資料行填充為 0,因為淺層資料行的總大小為偶數。
深層型別資料行的偏移陣列:
2 + 2 * <number of deep type columns> = 2 + 2 * 1 = 4NULL陣列 = 1NULL陣列填充 = 1,因為NULL陣列大小為奇數,且有深度類型資料行。填充
- 8 是最大對齊需求
- 目前為止的大小為 16 + 0 + 4 + 1 + 1 = 22
- 最接近的 8 倍數是 24
- 填充總量為 24 - 22 = 2 個位元組
沒有固定長度的深層類型資料行 (固定長度的深層類型資料行:0。)。
深層類型資料行的實際大小為 2 * 78 = 156。 單一深層類型資料行
OrderDescription具有nvarchar類型。
<actual row body size> = 24 + 156 = 180 bytes
若要完成計算:
<row size> = 32 + 180 = 212 bytes
<table size> = 8 * 16384 + 212 * 8379 = 131072 + 1776348 = 1907420
因此,記憶體中的總資料表大小約為 2 MB。 這並未涵蓋記憶體配置可能造成的負擔,以及存取此資料表的交易所需的任何資料列版本設定。
這個資料表及其索引實際配置和使用的記憶體可透過下列查詢取得:
SELECT * FROM sys.dm_db_xtp_table_memory_stats
WHERE object_id = object_id('dbo.Orders');
列外資料欄限制
在記憶體最佳化資料表中使用 off-row 資料行的某些限制與注意事項如下所示:
- 如果記憶體最佳化資料表上有資料行存放區索引,則所有資料行都必須能容納於資料列內。
- 所有索引鍵資料行都必須儲存在資料列內。 如果索引鍵資料行無法容納於列內,新增索引就會失敗。
- 關於變更具有列外資料行的記憶體最佳化資料表時的注意事項。
- 對於 LOB,其大小限制與磁碟型資料表相同(LOB 值上限為 2 GB)。
- 為達最佳效能,建議應將大多數的資料行調整到 8,060 個位元組內。
- Off-row 資料可能會導致記憶體和/或磁碟使用量過高。