【问题标题】:SQL Server Update Indexing DesignSQL Server 更新索引设计
【发布时间】:2017-04-27 01:12:45
【问题描述】:

我有一张表,带有 DML 的 IDMAP:

CREATE TABLE tempdb2.dbo.idmaptemp (
    OldId varchar(20),
    CV_ModStamp datetimeoffset,
    NewId varchar(20),
    RestoreComplete bit,
    RestoreErrorMessage varchar(1000),
    OperationType varchar(20)
)

正如它所定义的,它已经包含了一组大约 (100 万) 的预定义行。还原操作完成后,我必须更新表上的 NewId、RestoreComplete、RestoreErrorMessage。 声明是:

update tempdb2.dbo.IdMaptemp set NewId = 'xxx', RestoreComplete = 'false', RestoreErrorMessage = 'error' where OldId = 'ABC';

Java 应用程序在内存中有大约一百万个值,并且必须使用上述语句更新这些值。数据库设置为关闭自动提交,并随批次(批次大小 500)而变化。

我在使用 OldId 字段进行索引时尝试了两个选项:

  1. 聚集索引 - 执行计划列为聚集索引更新(100% 成本)。发生这种情况是因为叶子是正在更新的行并且会触发索引更新。我在吗?

  2. 非聚集索引 - 执行计划列为更新 (75%) 和查找 (25%)。

在对数据库表进行大规模更新时,是否可以实现其他任何加速?该表无法清除并重新插入,因为还有其他行不受更新影响。每批 500 行样本的聚集索引需要大约 7 个小时来更新。

我应该选择非聚集索引选项吗?

【问题讨论】:

  • 百分比是没有意义的,它们只是说明成本在计划中是如何分配的,在计划之外没有任何价值。
  • @Mark Rotteveel 可以找到执行计划的哪个阶段需要时间的任何信息?
  • 如果您正在对性能进行故障排除,也许您可​​以将更新从 Java 中移出并放入 SQL 脚本中。您是否正在针对此表运行 100 万条单独的更新语句?如果是这样,您可能会发现很大一部分性能问题是由于单个更新语句而不是任何特定索引造成的。如果您可以重构您的流程以在数据库中执行所有这些操作,它会更快。例如,将一百万个值批量加载到另一个表中,然后在一批中执行数据库更新。
  • @Nick.McDermaid 我知道在数据库层做的方法更快。来自应用程序开发人员的观点,如果有几个表在更新字段中有所不同,我应该为每个表编写代码吗?尽管让数据库来做跑腿工作似乎是合适的,但这似乎并不可靠。希望有一个实用程序或驱动程序可以使用临时表方法处理大量更新!
  • 我看不出为 Java 应用程序或数据库中的每个表编写代码之间的区别。也许您的 Java 应用程序中有一些动态代码。在数据库中编写非动态代码可以实现某些性能改进。无论如何,真正的问题是你的 Java 应用程序中的这些百万数据点来自哪里?它们是来自另一个数据库还是可以在数据库中生成?如果您的数据是从 DB>Java>DB 往返,那么在我看来,您有一个提高性能的好机会

标签: sql sql-server indexing


【解决方案1】:

更改大表的聚集索引是一项昂贵的提议。表的聚集索引是为整个表定义的,而不是为行的子集定义的。

如果您将 oldid 保留为聚集索引并且只想提高批处理性能,请考虑允许 db 参与批处理过程,而不是应用程序/java 层。要求数据库一次更新数百万行 1 行是一个昂贵的提议。用批处理值填充临时表,然后让 SQL 一次更新整个批处理可能是提高性能的好方法。

insert #temptable (OldId,NewId)
...

Update
set T1.NewId = T2.NewId
T1
from
T1 join #tempTable T2
on T1.OldId = T2.OldId

如果您可以计算新的 id,请考虑另一种批处理策略。

update tempdb2.dbo.IdMaptemp top 1000 set NewId = 'xxx', RestoreComplete = 'false', 
    RestoreErrorMessage = 'error' where NewId is null;

如果你真的想创建一个以 NewId 作为聚集索引的新表 根据需要创建新表

insert into NewTable()
select top 10000 *
from OldTable O
left join NewTable N
on O.OldId = N.OldId
where N.OldId is null

完成后,删除旧表。

注意:您的 id 是否需要为 20 个字节?通常聚集索引是 int - 4 字节或 bigint - 8 字节。

如果这是一次性的事情,那么更改大型持久表上的聚集索引将是值得的。如果 oldid 始终处于获取 newid 值的过程中,而这只是您拥有的工作流程,那么我不会费心更改持久表的聚集索引。只需将 oldid 保留为聚集索引即可。 NewId 听起来像是一个代理键。

【讨论】:

  • 该表的索引可以在创建时修改,因为这是一个临时表。最终,通过加入新的 id 将状态写入数据源表。这似乎是一个永无止境的循环,需要更新数据库上的大量值。由于更新是在连接上运行的,表是否仍然需要索引?注册。 ids,ids 是唯一生成的 20 个字符,并且是特定于要求的。
  • 再次...经常发生!
猜你喜欢
  • 2011-10-07
  • 2011-10-05
  • 1970-01-01
  • 2011-04-20
  • 2012-03-31
  • 2012-05-26
  • 2012-10-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多