【问题标题】:How to re-index an AuditLog table? non clustered primary key, clustered covering index, guid如何重新索引 AuditLog 表?非聚集主键、聚集覆盖索引、guid
【发布时间】:2018-08-04 08:51:59
【问题描述】:

使用 SQL Server 2016 标准版。我有一个现有的 AuditLog 表,在 bigint 列(生成的 C# 端)上有一个 PK 和一个附加索引。

CREATE TABLE [dbo].[AuditLog]
(
    [Id] [bigint] NOT NULL,
    [ChangeTime] [datetime] NOT NULL,
    [User] [varchar](100) NOT NULL,
    [RootId] [bigint] NOT NULL,
    [EntityId] [bigint] NOT NULL,
    [EntityName] [varchar](100) NOT NULL,
    [Operation] [varchar](100) NOT NULL,
    [OldValue] [varchar](max) NULL,
    [NewValue] [varchar](max) NULL
)

ALTER TABLE [dbo].[AuditLog] 
    ADD CONSTRAINT [PK_AuditLog] 
        PRIMARY KEY CLUSTERED ([Id] ASC)

CREATE NONCLUSTERED INDEX [IX_AuditLog_RootId] 
    ON [dbo].[AuditLog] ([RootId] ASC)

对于当前的 105,000,000 行,大小为(使用 used_pa​​ge_count * 8K 每页):

  • PK_AuditLog:11,535,112 KB
  • IX_AuditLog_RootId:2,370,480 KB

我现在必须从 SQL 中的存储过程在此表中创建行,而不仅仅是在 c# 中,所以我需要一个可以在 SQL 端(和 C# 仍然)生成的主键。我认为我的选择是int identityguid(默认为NEWSEQUENTIALID)。

由于我的大部分用法包括日期和按日期排序,我正在考虑使用它进行聚类。听起来对吗?

由于我几乎总是按RootIdUser 进行过滤,因此我正在考虑将它们包含在我的索引中。将其他列包含在聚集索引中是个好主意吗?还是应该在单独的覆盖索引中?

每个索引都需要唯一标识行,因此即使我没有指定主键,我的聚集索引也会包含主键。因此,使用Guid 作为 PK 对于存储来说似乎是个坏主意,尤其是在有 1 亿行的情况下。所以我使用的是bigint

由于我的 PK 不是集群的(因此没有按该​​顺序物理存储),SQL Server 如何计算出下一个标识?我怀疑它对 PK 进行排序以找到最大值。在非聚集列上使用标识是个坏主意吗?

另外,我想我可以使用精度为 3(存储 7 个字节)的 datetime2 而不是 datetime(8 个字节)来保持相同的精度但节省一点空间(甚至精度为 4 以提高相同的精度无论如何存储)?

所以我正在考虑这样做:

CREATE TABLE dbo.AuditLog
(
    Id bigint NOT NULL IDENTITY (1, 1),
    ChangeTime datetime2(4) NOT NULL...


ALTER TABLE AuditLog   
    ADD CONSTRAINT [PK_AuditLog] 
        PRIMARY KEY NONCLUSTERED (Id)

CREATE CLUSTERED INDEX CIX_AuditLog_ChangetimeRootUser 
    ON AuditLog(Changetime, RootId, [User])

脚注

这就是表格的使用方式:

  • 此表没有外键。

  • 重插入(用户实体字段的任何添加/编辑/删除都会插入新的 AuditLog 行,在工作时间不断,必须快速)

  • 偶尔读取(用户检查什么或谁更改了某些内容,即,每天几次读取 AuditLog,这样最好不要等待很长时间才能返回查询)

  • AuditLog 行一旦插入就永远不会更新或删除。

典型的过滤器和顺序:

  • 仅按日期过滤
  • 按日期和用户过滤
  • 按日期和 objectId 过滤
  • 按日期、用户和对象 ID 过滤
  • 仅按 objectId 过滤
  • 几乎总是按反向日期排序,以首先显示最近的更改。
  • 通常与分页一起使用,使用“偏移 x 行”和“仅获取下 x 行”
  • 和一个特定用例,相当于使用 where 子句选择 PK 的子集,然后使用 PK 在主表上自联接以检索列值

PS:我很清楚这个过程和需要的时间,创建临时新表,分块复制数据,创建索引等等......

【问题讨论】:

标签: sql-server database indexing


【解决方案1】:

由于我的大部分用法包括日期和按日期排序,我正在考虑使用它进行聚类。听起来对吗?

如果不这样做并评估结果,就无法知道。

将其他列包含在聚集索引中是否是个好主意?

您不能在聚集索引中包含列,因为它没有什么意义。聚集索引最终是表。您在 NC 索引中包含列以避免额外的查找来访问行的其他列。

SQL Server 如何计算出下一个身份?

坦率地说,不用担心。引擎在表级别管理身份 - 它不需要引用任何特定行来确定下一个值。

另外,我想我可以使用精度为 3(存储 7 个字节)的 datetime2 而不是 datetime(8 个字节)来保持相同的精度但节省一点空间(甚至精度为 4 以提高相同存储的精度无论如何)?

不要仅仅为了每行保存一个字节而妨碍您的数据。根据您的要求选择正确的数据类型。存储很便宜。缺乏精确性是永远的。

此外,您的脚注不清楚。您指的是用户实体字段的添加/更新/删除(对于不熟悉您的架构的人来说,这是一个没有意义的术语)以及“从不更新或删除”。这似乎是一个矛盾,可能相关也可能不相关。

还有最后一条评论。变化涉及风险。如果您当前的架构足够,那么 最安全 的方法是简单地重新创建您的表,并使用您的 ID 列作为标识(其他一切都保持不变)。

【讨论】:

  • 在日期列上进行聚类,因为我的大多数用法都包括日期和按日期排序:您说“不做和评估就无法判断”。嗯。我会认为在做之前思考和辩论利弊是一个好主意,突出陷阱,或者这是一个众所周知的坏主意,等等。
  • 聚集索引最终是表,因此向其中添加列没有意义:这是有道理的。不用担心身份列,好吧,一旦我知道它是如何工作的,我就不会担心。这篇文章解释了,我应该先搜索一下。 sql不需要扫描任何行,值存储在syscolpars(dba.stackexchange.com/questions/135950/…)
  • 我不明白您关于妨碍我的数据的评论。据我所知,datetime 有 3 位小数秒,并存储为 8 个字节。精度为 3 或 4 的 datetime2 不会降低我的精度,并且存储为 7 个字节。所以我不会妨碍我的数据或松散的精度,我只会为 1 亿行节省 11GB 中的 100MB。不多,我知道。
  • 至于脚注,谢谢,对不起,我的意思是在数据库中其他地方的用户实体中更新的每个字段都会在 AuditLog 中插入一个新行。永远不会更新或删除的是 AuditLog 行。一旦插入,它们将永远保持不变。已编辑的帖子。
猜你喜欢
  • 1970-01-01
  • 2019-11-01
  • 2011-05-09
  • 2014-07-30
  • 2011-05-15
  • 2021-12-14
  • 2013-08-07
  • 1970-01-01
相关资源
最近更新 更多