0

我在我们的数据库中存储图像时遇到了一些问题。我们目前有大约 40 万条记录,但我预计这会很快增加到数百万条。目前我已经遇到了性能问题,所以这是一个主要问题。在决定使用 SQL Server 存储图像之前,我做了一些研究,我读到的所有内容都表明它能够做到这一点。

我将表格设计得非常简单,包含 3 列...

  • Id(主键,唯一标识符,不为空)
  • ImageHash(唯一标识符,不为空)
  • BinaryImage (varbinary(max), not null)

逻辑是我在我的应用程序代码中生成 ImageHash。图像哈希用于在插入之前进行查找,以查看二进制图像是否已存在于数据库中。其余时间我只是直接使用 Id 查询表。

我正在使用 .NET Entity Framework 来执行我的数据访问。Id 列是在插入时生成的,不确定这是否是最佳实践。

这是我的表的创建脚本。我为 ImageHash 创建了一个索引,但是我对 SQL Server 索引并不是很了解。

CREATE TABLE [dbo].[ImageContent](
    [Id] [uniqueidentifier] ROWGUIDCOL  NOT NULL,
    [ImageHash] [uniqueidentifier] NOT NULL,
    [BinaryImage] [varbinary](max) NOT NULL,
 CONSTRAINT [PK_ImageData] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = ON, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 80) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

ALTER TABLE [dbo].[ImageContent] ADD  CONSTRAINT [DF_ImageData_Id]  DEFAULT (newid()) FOR [Id]

还有索引......

CREATE NONCLUSTERED INDEX [ImageHash_Index] ON [dbo].[ImageContent]
(
    [ImageHash] ASC
)
INCLUDE (   [Id]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

我已经重建了所有索引,但这并没有解决问题。我一直在玩 SQL Server Profiler,并且我确定了导致问​​题的 SQL 插入(从实体框架生成)。这是带有二进制文件的语句,但我已将其中的大部分内容截断为......这是在 30 秒后超时......

exec sp_executesql N'declare @generated_keys table([Id] uniqueidentifier)
insert [dbo].[ImageContent]([ImageHash], [BinaryImage])
output inserted.[Id] into @generated_keys
values (@0, @1)
select t.[Id]
from @generated_keys as g join [dbo].[ImageContent] as t on g.[Id] = t.[Id]
where @@ROWCOUNT > 0',N'@0 uniqueidentifier,@1 varbinary(max) ',@0='DF76D1FF-5C05-58E0-0933-1ADBCC6345A8',@1=0xFFD8FFE1214545786966000049492A00080000000D00000103...

所以我的问题是...

  • 任何人都可以看到我的设置方式存在一些重大问题吗?
  • 您有什么建议可以帮助我提高性能吗?
  • SQL Server 是否能够以这种方式存储数百万张图像?

在此先感谢您的时间!

4

2 回答 2

2

因为您的索引(包括主键上的聚簇索引)位于唯一标识符上,所以这些索引将非常快速地分段。

  1. 考虑将单调递增的 INT/BIGINT IDENTITY 作为您的 Id,除非您有充分的理由不这样做
  2. 调整 NCI (ImageHash_Index) 上的填充因子并确保您有工作定期重组/重建它
  3. 如果实际图像大于 2 MB,请考虑使用 FILESTREAM 来存储它们。这里有一份白皮书:http: //msdn.microsoft.com/library/hh461480。如果你走这条路线,还有另外两个性能考虑。此处有关它们的信息:http: //msdn.microsoft.com/en-us/library/ee377058 (v=bts.10).aspx 。

禁用短文件名 (8.3) 生成 当使用 Windows NTFS 文件系统创建长文件名时,默认行为是在旧的 8.3 DOS 文件名约定中生成相应的短文件名,以便与旧操作系统兼容。可以通过注册表项禁用此功能,从而提高性能。

fsutil 行为设置 disable8dot3 1

禁用 NTFS 上次访问更新 NTFS 卷上的每个文件和文件夹都包含一个称为上次访问时间的属性。此属性显示上次访问文件或文件夹的时间,例如用户执行文件夹列表、将文件添加到文件夹、读取文件或更改文件的时间。维护此信息会增加文件系统的性能开销,尤其是在短时间内快速访问大量文件和目录的环境中,例如使用 BizTalk 文件适配器时。除了在高度安全的环境中,保留此信息可能会给服务器增加负担,这可以通过更新以下注册表项来避免:

fsutil 行为设置 disablelastaccess 1

于 2013-07-30T02:07:56.127 回答
0

您将需要进行尽职调查并至少收集一些最少的信息,因为任何人都可以猜测问题出在哪里。您需要做的第一件事是阅读等待和队列以熟悉用于解决 SQL Server 性能问题的适当调查技术,并应用该方法来收集相关信息。

现在这是我的意见,这是基于没有任何证据的。您的 INSERT 可能会阻塞,我们不知道为什么。使用活动监视器了解是什么阻止了您的 INSERT。这不是由碎片化(永恒的红鲱鱼)造成的。如果我敢猜测,罪魁祸首是使用默认范围 new System.Transactions 和这带来的可怕的序列化隔离

附带说明:UNIQUEIDENTIFIER对于散列来说是一种可怕的数据类型选择。要生成与图像相关的散列,您必须运行散列算法,例如 MD5 或 SHA。您可能正在使用 MD5 并生成一个 16 字节的密钥,但这绝对没有理由将其存储为 16 字节长度的UNIQEUIDENTIFIER类型。使用BINARY(16). 还要考虑将来是否要移动到 SHA1(20 字节散列)或其他散列。

于 2013-07-30T07:01:04.290 回答