堆積(不含叢集索引的數據表)

適用於:SQL ServerAzure SQL DatabaseAzure SQL 受控執行個體Microsoft Fabric 中的 SQL 資料庫

堆積是一種沒有叢集索引的資料表。 你可以在以堆積形式儲存的資料表上建立一個或多個非叢集索引。 堆積儲存資料時不會指定順序。 通常,堆積會依照你插入列的順序來儲存資料。 不過,資料庫引擎 可以在堆積中移動數據,以有效率地儲存數據列。 在查詢結果中,你無法預測資料的順序。 若要保證從堆積傳回的數據列順序,請使用 ORDER BY 子句。 要指定一個永久的邏輯順序來儲存列,請在表格上建立一個叢集索引,這樣表格就不會變成堆積。

Note

有時候,有充分理由讓資料表保留為堆積,而不是建立叢集索引。 然而,有效使用堆積物是一項進階技能。 除非有充分理由讓資料表維持為堆積,否則大多數資料表都應具有經過審慎選擇的叢集索引。

何時使用堆

堆積非常適合經常截斷並重新載入的資料表。 資料庫引擎 透過填滿最早可用的空間來優化堆積中的空間。

請考慮下列事項:

  • 在堆積中尋找空閒空間可能成本高昂,尤其是當大量刪除或更新發生時。
  • 叢集索引對於不常截斷的資料表,提供穩定的效能。

對於經常截斷或重建的資料表,例如暫存或暫存資料表,使用堆積通常更有效率。

使用堆積和叢集索引之間的選擇,可能會大幅影響資料庫的效能和效率。

當你將資料表儲存為堆積時,你會透過一個 8 位元組的列識別碼(RID)來識別各個列,該識別碼包含檔案號、資料頁碼和頁面上的插槽(FileID:PageID:SlotID)。 資料列識別碼是一種小型而高效率的結構。

使用堆作為大型無序插入操作的暫存表。 由於堆不強制執行嚴格的插入順序,插入操作通常比等效插入到叢集索引的速度快。 如果你讀取並處理堆積的資料到最終目的地,可以考慮建立一個窄且非叢集的索引,涵蓋查詢所使用的搜尋謂詞。

Note

你從堆疊中擷取資料是依照資料頁的順序,但不一定是你插入資料的順序。

當你總是透過非叢集索引存取資料,且 RID 比叢集索引鍵小時,也可以使用堆積。

如果一個資料表是堆積且沒有任何非叢集索引,那麼你必須讀完整個資料表(用資料表掃描)才能找到任何一列。 SQL Server 無法直接在堆積上尋找 RID。 當資料表很小時,這種行為是可以接受的。

何時不該使用堆

當資料經常以排序順序回傳時,不要使用堆積。 排序欄上的叢集索引可以避免排序操作。

當資料經常被分組時,不要使用堆疊。 資料必須先排序後再分組,而排序欄上的叢集索引可以避免排序操作。

當資料範圍經常被從表格查詢時,不要使用堆積。 範圍資料行上的叢集索引可避免對整個堆進行排序。

當沒有非叢集索引且表格很大時,不要使用堆積。 此設計唯一的應用是回傳整個資料表內容,無需指定順序。 在堆中,資料庫引擎必須讀取所有資料列,才能找到任一資料列。

如果你經常更新資料,就不要用堆疊。 如果你更新了一個記錄,而更新在資料頁中佔用的空間比目前還多,該紀錄就會移到有足夠空間的資料頁。 此移動會產生指向資料新位置的 轉發紀錄 。 轉發指標寫入先前存放資料的頁面,以指示新的物理位置。 此動作會引入堆積碎片化。 當 資料庫引擎 掃描堆積時,它會遵循這些指標。 此動作限制了前讀效能,並可能產生額外的輸入輸出,進而降低掃描效能。

管理堆積

若要建立堆積,請建立不含叢集索引的資料表。 如果資料表中已有叢集索引,則請卸除叢集索引,將資料表還原為堆積。

若要移除堆積,請在堆積上建立叢集索引。

若要重建堆積以回收浪費的空間:

  • 在堆積資料表上建立叢集索引,然後刪除該叢集索引。
  • 使用 ALTER TABLE ... REBUILD 命令重建堆積。

Warning

建立或卸除叢集索引時必須重寫整份資料表。 如果資料表有非叢集索引,每次更改叢集索引時,都必須重新建立所有非叢集索引。 因此,從堆積變更為叢集索引結構或返回堆積結構可能需要大量時間,且需要磁碟空間來重新排序資料。tempdb

識別堆積

下列查詢會從目前資料庫傳回堆積清單。 此清單包括:

  • 資料表名稱
  • 結構描述名稱
  • 列數
  • 資料表大小(KB)
  • 索引大小(KB)
  • 未使用的空間
  • 用來識別堆的欄位
SELECT t.name AS 'Your TableName',
       s.name AS 'Your SchemaName',
       p.rows AS 'Number of Rows in Your Table',
       SUM(a.total_pages) * 8 AS 'Total Space of Your Table (KB)',
       SUM(a.used_pages) * 8 AS 'Used Space of Your Table (KB)',
       (SUM(a.total_pages) - SUM(a.used_pages)) * 8 AS 'Unused Space of Your Table (KB)',
       CASE
           WHEN i.index_id = 0 THEN 'Yes'
           ELSE 'No'
       END AS 'Is Your Table a Heap?'
FROM sys.tables AS t
     INNER JOIN sys.indexes AS i
         ON t.object_id = i.object_id
     INNER JOIN sys.partitions AS p
         ON i.object_id = p.object_id
        AND i.index_id = p.index_id
     INNER JOIN sys.allocation_units AS a
         ON p.partition_id = a.container_id
     LEFT OUTER JOIN sys.schemas AS s
         ON t.schema_id = s.schema_id
WHERE i.index_id <= 1 -- 0 for Heap, 1 for Clustered Index
GROUP BY t.name, s.name, i.index_id, p.rows
ORDER BY 'Your TableName';

堆積結構

堆積是一種沒有叢集索引的資料表。 堆積在 sys.partitions 中有一個資料列,而堆積所使用的每個資料分割各有一個 index_id = 0。 預設情況下,堆只有一個分割區。 當堆有多個分割區時,每個分割區都有一個堆結構,包含該特定分割區的資料。 例如,如果一個堆有四個分割區,則會有四個堆結構;每個分割區各有一個。

根據堆積中的資料類型,每個堆積結構有一個或多個配置單元,用於儲存和管理特定分割區的資料。 至少每個堆區每個分割有一個 IN_ROW_DATA 配置單元。 如果堆積結構包含大型物件(LOB)欄位,則每個分割也只有一個 LOB_DATA 配置單元。 如果每個分割區包含超過8,060位元組列大小限制的可變長度欄位,則每個分割區也有一個 ROW_OVERFLOW_DATA 分配單位。

系統檢視中的sys.system_internals_allocation_units欄位first_iam_page指向管理特定分割區堆積空間的 IAM 頁面鏈中的第一個索引配置圖(IAM)頁面。 SQL Server 使用 IAM 頁面來巡覽堆積。 資料頁面及其內的列沒有特定順序,也沒有連結。 資料頁之間的唯一邏輯連接為 IAM 頁面中所記錄的資訊。

Important

sys.system_internals_allocation_units系統檢視僅保留給內部使用。 不保證未來的相容性。

你可以透過掃描 IAM 頁面來找出堆積中包含頁面的區間,來執行資料表掃描或序列讀取。 由於 IAM 以資料檔案中存在的順序表示區段,這種結構意味著序列堆掃描會依序逐一進行每個檔案的進度。 使用 IAM 頁面設定掃描序列也意味著堆積中的資料列通常不會依插入順序回傳。

下圖顯示 SQL Server Database Engine 如何利用 IAM 頁面擷取單一資料分割堆積中的資料列。

IAM 堆積圖。