Sql-Server

如何在容器之間移動或重新分配 FILESTREAM 文件?

  • March 19, 2017

我們有一個FILESTREAM包含幾百萬個文件的容器,我們相信這是我們遇到的性能問題(大量超時)的原因。

根據這篇關於最佳實踐的部落格FILESTREAM,每個容器不應超過 300,000 個文件。

根據此處接受的答案FILESTREAM,除了將表重新創建到新位置之外,沒有其他方法可以完成它。

  1. 我的情況是這樣嗎?
  2. 如果是這樣,推薦的方法是確保自動創建足夠的容器來處理這個問題,而無需每 300,000 個文件進行手動干預?

表結構如下:

CREATE TABLE [dbo].[Documents](
   [ContentPath] [uniqueidentifier] ROWGUIDCOL  NOT NULL,
   [FileContent] [varbinary](max) FILESTREAM  NULL,
CONSTRAINT [UQ_IX_Documents_ContentPath] UNIQUE NONCLUSTERED 
(
   [ContentPath] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY]
) ON [PRIMARY] FILESTREAM_ON [FSFileGroup_1]

我們使用的是 SQL Server 2012。不幸的是,不是 Enterprise(我們最近才意識到每個文件組支持多個容器)。

儘管我們從不進行記錄更新,但我們會進行大量寫入,可能與讀取一樣多。模式是:一次只有一個,由 ContentPath 讀取,並且沒有特定的容器或順序寫入。

我想,對你來說一個不錯的選擇是在邏輯上拆分錶,所以每個部分都在自己的文件夾中,並且文件數少於 300k(=records)。

在 SQL Server 中,邏輯表拆分通常使用分區(這是企業功能)來完成。分區本質上將表的部分(分區)映射到物理儲存(文件組)。可以將不同的分區分配給不同的文件組。FILESTREAM本質上是文件組的屬性。

設計

這構成了您的解決方案的支柱:

  • 分區駐留在不同的文件組上
  • 每個文件組都是一個單獨的 FILESTREEAM 文件夾/容器
  • 每個分區的大小低於 300k 記錄

以下是如何使用FILESTREAMcolumn對錶進行分區。

主要優點: 您將能夠FILESTREAM一次管理每個分區的數據,而不是整個表。

你需要:

  1. 允許跟踪分區大小或至少不允許跨分區隨機寫入的 ID
[Id] [uniqueidentifier] ROWGUIDCOL NOT NULL DEFAULT (newsequentialid())
  1. 維護工作,檢查最後一個分區有多滿,提前創建新的文件組和分區,重建每個分區的索引等。
  2. 為了滿足Insert操作,您的分區小於 300k,因此索引一次重建一個分區。

這種設計主要用於只讀表,插入發生在它的末尾。如果您還沒有這樣做,請考慮寫入和更新以及分區對它們的影響。

移民

為了使表分區,您需要建立索引。主要有2種方式:

  1. 為整個表重建索引。它將需要比桌子多 x2-x3 倍的空間。由於多種原因,它不適合大桌子。
  2. 將數據遷移到已經分區的新表中。

我的首選方法:

  • 創建與目標分區表結構相同的表。
  • DELETE... OUTPUT DELETED ... INTO <intermediary table>使用適合 1 個分區的數據插入數據。
  • 如果一個事務的行數太大,則將其包裝到一個循環中
  • 在分區方案上創建索引
  • 將分區切換到目標表
  • 刪除中間表上的索引
  • 循環包裝並重複所有分區/數據

插入物

理想情況下,新插入的行應該進入最後一個分區。但是,關於NEWSEQUENTIOALID()功能有一個問題:

創建一個 GUID,該 GUID 大於自 Windows 啟動以來此函式先前在指定電腦上生成的任何 GUID。重新啟動 Windows 後,GUID 可以從較低的範圍再次啟動,但仍然是全域唯一的。

這意味著只有在伺服器第一次重新啟動之前,新的寫入才會進入最後一個分區。而這最終會發生。新行將插入表格中間。但是還記得我們現在有分區嗎?一次只有一個分區會受到影響。它仍然會在某個時間點超過 300k 行。

這裡的主要選項是拆分分區。現有分區將一分為二,其中右側部分將形成一個新分區。新分區應該進入新的文件組。它不會是僅元數據操作:文件將被物理複製到另一個分區。

解決方法是在同一 PS 上移動(刪除行並將行插入中間表)行;拆分分區並向後移動行。但無論如何,它涉及移動數據一次。如果中間表位於同一 PS 上,則拆分將涉及移動數據。如果沒有,則需要第二次將數據移回主表。

多個 FILESTREAM 容器

多個 FILESTRAM 容器是企業版的一項功能。容器通常被稱為容器中的“文件夾” FILESTREAM。實際上它是類型文件組中的數據庫文件FILESTREAM

將每個 NTFS 文件夾的文件保持在 300k 以下確實會有所幫助,但是很難明確地管理這些文件夾中儲存的文件。

文章Rebalancing data across files in a filegroup from 2011 由 Paul Randall 提供了有關在文件組中使用文件的提示:

$$ SQL Server $$還使用一種稱為比例填充的算法,旨在根據文件相對於文件組中其他文件的可用空間量來分配文件中的數據。 …

這意味著,如果您將新數據文件添加到具有大部分完整數據文件的文件組中,則按比例填充權重將使得新文件將成為分配來源的文件,直到它填充到與舊文件相同的級別文件。新文件實質上成為分配熱點。

SQL Server 將首先填充新添加的文件,直到它們填充到與其他文件相同的 %age 級別。

它可能看起來像分區的更簡單的替代方案,但它不是:

  1. 您無法對已儲存在現有容器中的文件執行任何操作。
  2. 您仍然需要在單個事務中管理一張大表

同時,您可以將多個 FILESTREAM 容器混合到分區解決方案中,以使文件夾更小更快。

標準版 (SE) 更新

SE 沒有分區,也沒有多個FILESTREAM容器。這意味著我們每個表只能有一個分區和一個容器。

一種選擇是擁有多個UNION ALL頂部有視圖的表格。

  • 大部分時間表的數量是固定的。您已經擁有相當多的文件,因此您可以查看 GUID 的分佈,並為更小的間隔創建表格,以便在低端獲得更高的密度。
  • 創建一個包含UNION ALL所有表的視圖。提示:為了避免掃描所有表WHERE,為屬於該特定表的 ID 添加一個子句。它將幫助優化器排除特定不存在的表:
   Select * from Table1 where ID between 1 and 100 
   UNION ALL
   Select * from Table2 where ID between 101 and 10000 
   UNION ALL
   Select * from Table3 where ID between 10001 and 100000000 

確保涵蓋從0000-...-0000到的所有可能的 GUID 值FFFF-...-FFFF

  • 此視圖將可更新,因此您將能夠使用此視圖將數據插入相關表中。對此有模仿:通過視圖修改數據。- 您需要將所有數據從現有表物理複製到新表。它可以通過主視圖來實現。

PS SQL Server 首先是一個 RDMS。管理大量非結構化數據的功能(如VARBINARY(MAX)附加組件)和相對較新的功能。因此,還需要考慮其他限制:

每個表的最大分區數:15 000 每個數據庫的最大文件組或文件數:32 767 每個容器的最大文件數:300 000(在我的範例中 = 文件組,如果每個文件組有多個容器,則 = 數據庫文件)

事務日誌的大小是一個事務的限制,也是一個事務完成所需的時間。您需要保持單個操作的可管理性,並且可以在合理的時間內回滾。

引用自:https://dba.stackexchange.com/questions/138756