MiM*_*iMo 5 performance sql-server-2008 insert
我们产品的数据库包含一个审计跟踪表:
CREATE TABLE gn_AuditTable(
gn_ObjectId int NULL,
gn_Action smallint NULL,
gn_Time datetime NULL,
gn_UserId int NULL,
gn_Login varbinary(16) NULL,
gn_ExtraObjectId int NULL,
gn_ExtraInfo int NULL
) ON [PRIMARY]
Run Code Online (Sandbox Code Playgroud)
我们用一个简单的insert
语句向这个表添加行- 没有存储过程:
insert into gn_AuditTable (
gn_ObjectId,gn_Time,gn_UserId,gn_Login,gn_Action,gn_ExtraObjectId,gn_ExtraInfo
) values (
?,?,?,?,?,?,?
)
Run Code Online (Sandbox Code Playgroud)
将列的值作为参数传递给查询;也没有触发器。
问题是有时这个插入是超慢的——在某些情况下需要超过 30 秒和超时。
我们对同一产品进行了多次安装,并且我们在其中一些(但不是全部)中遇到了问题。
审计表可能非常大——多达几百万行。非常慢的插入发生在具有大表的数据库中(很自然) - 但是我们有其他具有相似行数的其他数据库运行良好,甚至遇到问题的数据库也会间歇性地出现问题,在大多数情况下插入相当快(亚秒)。
该表有 6 个非聚集、非唯一索引:
gn_Login
;
gn_ObjectId
;
gn_Action
和gn_ExtraInfo
;
gn_ExtraObjectId
;
gn_Time
;
gn_UserId
.
拥有所有这些索引可能会导致问题,但同样,我们到处都有相同的索引,而问题只是在某处/有时。
我们在只有一个用户的测试数据库上至少有一次插入超时,因此问题似乎与整体负载无关,而是与插入和表本身有关。
我们曾经在 上有一个聚集索引gn_ObjectId
,但为了解决这个问题,我们去掉了它。gn_ObjectId
不是顺序的,所以我们认为它是聚集索引的一个糟糕选择,迫使 SQL Server 在插入时重新排序行。可能gn_Time
- 这总是在增加 - 将是一个更好的选择。
没有主键,因为没有“自然”唯一的列或列组合——我们必须添加一个 id 列,这似乎只是开销。
有没有人对我们可以看什么来诊断问题有什么建议?什么是insert
需要这么多时间的事情——间歇性的?
gbn*_*gbn 11
为什么没有聚集索引?为什么没有主键?
这很可能是您的问题:您插入到堆中的表没有任何顺序(例如,在 IDENTITY 列的意义上)
看
编辑,更新后。
拥有非唯一聚集索引需要每个重复的 gn_ObjectId 条目的唯一标识符(DBA.se 链接)。这不是一个好的聚类键,最好是
我还假设您还没有尝试过 IDENTITY 列……?试试看,回来汇报。。
编辑 2,正如@JNK 所说,在这种情况下,ID 列不是开销,因为您的写入将如何在磁盘上进行管理
你的数据库够大吗?您确定您的 INSERT 不会触发自动增长吗?如果您的部署没有启用即时文件初始化,那么这正是触发数据库文件增长时所期望的行为:在文件增长和初始化期间随机阻止写入。您也可能正在经历日志文件增长(这至少可以通过查看日志增长性能计数器快速识别)但是要发生日志增长,您需要有一个非简单的恢复模型和一个基本上不存在的备份策略(即不会发生日志截断)。
至于您的表的设计:时间序列通常按时间值聚类,因为大多数查询涵盖时间范围(例如“选择过去 3 天内的所有事件”),因此这将使gn_Time
您可能成为非唯一的聚类关键候选者.
我们发现了问题:
有一个维护过程可以从数据库中删除数据,包括来自的行gn_AuditTable
;
delete gn_AuditTable . .
与其他语句一起在事务中(不好)
可能会发生一次删除相当多的行的情况 - 在这种情况下 SQL Server 会锁定整个表 - 然后insert gn_AuditTable
超时等待事务完成
我们发现这一点是因为我们能够在超时时查看锁,并且注意到表锁。我们正在将其delete gn_AuditTable . . .
移出事务,并可能将其分成更小的批次,而不是触发表锁。
我们正在实现聚集索引gn_Time
和即时文件初始化 - 两者都独立于这个特定问题而受益 - 谢谢!