SQL Server: MERGE性能

4

我有一个拥有500万行的数据库表。聚集索引是自增长标识列。主键是一个256字节的VARCHAR代码,该代码是URL的SHA256哈希值,这是表上的非聚集索引。

表格如下:

CREATE TABLE [dbo].[store_image](
    [imageSHAID] [nvarchar](256) NOT NULL,
    [imageGUID] [uniqueidentifier] NOT NULL,
    [imageURL] [nvarchar](2000) NOT NULL,
    [showCount] [bigint] NOT NULL,
    [imageURLIndex]  AS (CONVERT([nvarchar](450),[imageURL],(0))),
    [autoIncID] [bigint] IDENTITY(1,1) NOT NULL,
 CONSTRAINT [PK_imageSHAID] PRIMARY KEY NONCLUSTERED 
(
    [imageSHAID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

CREATE CLUSTERED INDEX [autoIncPK] ON [dbo].[store_image] 
(
    [autoIncID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
GO
  • imageSHAID 是一个图像URL的SHA256哈希,例如 "http://blah.com/image1.jpg",它被哈希成长度为256的varchar。

  • imageGUID 是一个生成的代码guid,用于标识图像(稍后将用作索引,但目前我已将此列省略为索引)。

  • imageURL 是图像的完整URL(最多2000个字符)。

  • showCount 是显示图像的次数,每次显示此特定图像时都会增加此计数器。

  • imageURLIndex 是一个由450个字符限制的计算列,这样我可以对imageURL进行文本搜索(如果我选择),它是可索引的(索引也因简洁而省略)。

  • autoIncID 是聚集索引,应该允许更快地插入数据。

定期地,我会从临时表合并到store_image表中。 临时表结构如下(与store_image表非常相似):

CREATE TABLE [dbo].[store_image_temp](
    [imageSHAID] [nvarchar](256) NULL,
    [imageURL] [nvarchar](2000) NULL,
    [showCount] [bigint] NULL,
) ON [PRIMARY]

GO

当合并过程运行时,我使用以下代码将DataTable写入临时表:
using (SqlBulkCopy bulk = new SqlBulkCopy(storeConn, SqlBulkCopyOptions.KeepIdentity | SqlBulkCopyOptions.KeepNulls, null))
{
    bulk.DestinationTableName = "[dbo].[store_image_temp]";
    bulk.WriteToServer(imageTableUpsetDataTable);
}

然后我运行合并命令,根据imageSHAID从临时表中合并更新store_image表中的showCount。如果该图像当前不存在于store_image表中,我会创建它:

merge into store_image as Target using [dbo].[store_image_temp] as Source
on Target.imageSHAID=Source.imageSHAID 
when matched then update set 
Target.showCount=Target.showCount+Source.showCount 
when not matched then insert values (Source.imageSHAID,NEWID(), Source.imageURL, Source.showCount);

我通常尝试将临时表中的2k-5k行合并到store_image表中的任何一个合并过程中。

我曾经在SSD上运行这个数据库(只连接了SATA 1),速度非常快(低于200毫秒)。由于SSD上的空间不足,我将数据库换成了1TB的7200缓存旋转硬盘,自那时以来完成时间超过了6-100秒(6000-100000毫秒)。当批量插入正在运行时,我可以看到大约1MB-2MB/sec的磁盘活动,CPU使用率很低。

这是这个数据量的典型写入时间吗?对我来说似乎有点慢,是什么导致了性能下降?毕竟,由于imageSHAID被索引,我们应该期望比这更快的搜索时间,对吗?

如果有任何帮助,将不胜感激。

谢谢你的时间。

1个回答

6
您在MERGE中的UPDATE子句更新了showCount。这需要在聚集索引上进行关键字查找。
然而,聚集索引也被声明为非唯一的。尽管基础列是唯一的,但这会向优化器提供信息。
因此,我建议进行以下更改:
  • 将聚集主键更改为autoIncID
  • imageSHAID上的当前PK更改为独立的唯一索引(而不是约束),并添加一个INCLUDE以包含showCount。唯一约束不能有INCLUDE
更多观察:
  • 您不需要在哈希或URL列中使用nvarchar。这些不是Unicode。
  • 哈希也是固定长度的,因此可以使用char(64)(用于SHA2-512)。
  • 列的长度定义了要为查询分配多少内存。有关更多信息,请参见varchar(500)与varchar(8000)相比有什么优势?

你好,这似乎极大地改善了事情,我运行了一些测试,合并时间似乎更短了:3.7k @ 24秒、2.6k @ 2.4秒、2.8k @ 11秒、1.3k @ 1.1秒、2.6k @ 2.5秒、2.1k @ 2.6秒,这些合并时间是否适当?纠正“其他观察”会进一步改善吗?我认为2-3k行的合并总是需要少于500毫秒。 - user989056
其他的观察结果会减少磁盘和内存的使用,减少需要移动的字节数(例如事务日志)。并且有助于优化器有效地分配内存。 - gbn
即使按照您的建议进行了修改,我仍然遇到性能问题。我的某些合并操作持续时间超过70秒。鉴于我已经对索引/PK进行了更改,您能否解释一下在调用合并操作时发生了什么,它肯定会在imageSHAID独立唯一索引上使用索引搜索,这应该很快,不是吗? - user989056
聚类索引放在自增ID上确实有助于性能。接受作为答案。 - user989056

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接