如何在容器之间移动或重新分发 FILESTREAM 文件?

Dan*_*aar 6 sql-server sql-server-2012 filestream

我们有一个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]
Run Code Online (Sandbox Code Playgroud)

我们使用的是 SQL Server 2012。不幸的是,不是 Enterprise(我们最近才意识到它支持每个文件组多个容器)。

尽管我们从不进行记录更新,但我们进行了大量写入,可能与读取一样多。模式是:一次一个,由 ContentPath 读取,并且没有特定的容器或顺序写入。

Sto*_*leg 5

我想,对您来说,一个不错的选择是在逻辑上拆分表,因此每个部分都将位于自己的文件夹中,并且文件数低于 300k(=记录)。

在 SQL Server 中,逻辑表拆分通常是通过分区完成的(这是一项企业功能)。分区本质上将表的部分(分区)映射到物理存储(文件组)。不同的分区可以分配给不同的文件组。FILESTREAM本质上是文件组的一个属性。

设计

这构成了您的解决方案的主干:

  • 分区驻留在不同的文件组
  • 每个文件组都是一个单独的 FILESTREEAM 文件夹/容器
  • 30万条记录下每个分区的大小

以下是如何使用FILESTREAMcolumn对表进行分区

主要优势: 您将能够一次管理FILESTREAM每个分区的数据,而不是整个表。

你需要:

  1. 允许跟踪分区大小或至少不允许跨分区随机写入的 ID

    [Id] [uniqueidentifier] ROWGUIDCOL NOT NULL DEFAULT (newsequentialid())
    
    Run Code Online (Sandbox Code Playgroud)
  2. 维护工作,检查最后一个分区的完整程度,提前创建新的文件组和分区,重建每个分区的索引等。

  3. 为了满足Insert操作让你的分区小于300k,所以索引一次重建一个分区。

这种设计主要用于只读表,在它的末尾进行插入。如果您还没有这样做,请务必考虑写入和更新以及分区对它们的影响。

移民

为了对表进行分区,您需要建立一个索引。主要有2种方式:

  1. 重建整个表的索引。它将需要比表多 x2-x3 倍的空间。由于多种原因,它不适合大桌子。

  2. 将数据迁移到已经分区的新表中。

我对此的首选方法:

  • 创建一个与目标分区表结构相同的表。
  • 插入数据DELETE... OUTPUT DELETED ... INTO <intermediary table>用于适合 1 个分区的数据。
  • 如果一个事务的行数太大,则将其包装成一个循环
  • 在分区方案上创建索引
  • 将分区切换到目标表
  • 删除中间表上的索引
  • 循环并重复所有分区/数据

插入

理想情况下,新插入的行应该进入最后一个分区。但是,有一个关于NEWSEQUENTIOALID()函数的问题:

创建一个 GUID,该 GUID 大于自 Windows 启动以来此函数在指定计算机上先前生成的任何 GUID。重新启动 Windows 后,GUID 可以从较低的范围再次启动,但仍然是全局唯一的。

这意味着新的写入只会进入最后一个分区,直到第一次重新启动服务器。而这最终会发生。新行将插入表格中间。但还记得我们现在有分区吗?一次只有一个分区会受到影响。它仍然会在某个时间点超过 30 万行。

这里的主要选项是拆分分区。现有分区将一分为二,其中右侧部分将形成一个新分区。新分区应该进入新文件组。这将不仅仅是元数据操作:文件将被物理复制到另一个分区。

解决方法是在同一个 PS 上移动(删除行并将行插入中间表)行;拆分分区并将行移回。但无论如何它都涉及一次移动数据。如果中间表在同一个 PS 上,那么拆分将涉及移动数据。如果没有,则需要第二次将数据移回主表。

多个 FILESTREAM 容器

多个 FILESTRAM 容器是企业版的一个特性。容器通常被称为容器中的“文件夹” FILESTREAM。实际上它是FILESTREAM类型文件组中的数据库文件。

将每个 NTFS 文件夹中的文件数控制在 300k 以下确实有帮助,但要明确管理这些文件夹中存储的文件是非常困难的。

文章重新平衡跨文件中的数据文件组由保罗·兰德尔从2011年给出了使用文件组文件的提示:

[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 
Run Code Online (Sandbox Code Playgroud)

请确保您从覆盖GUID的所有可能的值0000-...-0000FFFF-...-FFFF -这种观点将可更新的,所以你就可以使用此视图将数据插入相关表格。有一些模仿:通过视图修改数据。- 您需要将所有数据从现有表物理复制到新表。它可以通过主视图来实现。

PS SQL Server 首先是一个 RDMS。管理大量非结构化数据的功能,如VARBINARY(MAX)附加组件和相对较新的。因此,还需要考虑其他限制:

每个表的最大分区数:15 000 最大文件组或每个数据库的文件:32 767 每个容器的最大文件:300 000(在我的示例中=文件组,=每个文件组有多个容器的情况下的数据库文件)

事务日志的大小是对一个事务以及完成事务所需时间的限制。您需要保持单个操作的可管理性,并且可以在合理的时间内回滚。