将日志信息存储到表中的最佳实践

Tho*_*hor 4 performance sql-server

许多应用程序将日志记录信息存储在日志表中。

这些表很特殊,因为您只会执行插入操作。你永远不会进行更新。如果您确实进行了删除,那么清除旧数据将是一项每晚的工作。

该表有一个日期时间字段,该字段通常严格递增。调用应用程序可能会失去与数据库的连接,并且会堆积插入,并且在它获得连接时,它不能保证它将按照严格的顺序执行插入。但总的来说,如果日期时间字段是聚集索引,我预计在插入时对表进行排序会很便宜。

大多数查询将查询日期时间,但这些将是不等式查询。

有了这些特殊的属性,感觉应该有一种方法来优化它。

最佳实践是什么?

日志表的示例可以是:

CREATE TABLE logMessages (
    logTime datetime2(6) NOT NULL,
    logSeverity varchar(10) NOT NULL,
    logStatus varchar(10) NOT NULL,
    logText varchar(255),
    processID bigint,
    processUser varchar(25)
)
Run Code Online (Sandbox Code Playgroud)

一个典型的查询:

SELECT logTime, logSeverity, logText
FROM logMessages
WHERE logTime >= '2020-10-01'
AND logTime < '2021-11-01'
AND logSeverity IN ('WARNING','ERROR','FATAL')
Run Code Online (Sandbox Code Playgroud)

唯一身份?

日期时间字段不唯一,我们是否需要唯一标识符?

选项 1:添加logId BIGINT IDENTITY

如果我们添加一个唯一的 logId,它将不会在任何其他表中用作外键。

如果我们单独使用它作为聚集索引,并且 SQL Server 认为我们的查询将检索太多行而无法使用非聚集索引,那么它将执行全表扫描。

将聚集索引设置为 (logTime, logId) 是否有意义?我想要这样做的原因是,当查询优化器不会在 logTime 上使用非聚集索引时,因为它需要太多行,那么依靠良好的聚集索引将减少要扫描的行数。

选项 2:不添加 logId

将聚集索引设置为 (logTime)。由于 logTime 不是唯一的,SQL Server 必须通过添加不可见列来使其唯一。这是一个问题吗?至少我得到了一个良好的聚集索引,这将有助于我的大多数查询。

查找表

logSeverity 是一个带有预定义值(DEBUG、INFO、WARNING、ERROR、FATAL)的小 int,将这些值移动到不同的表并用tinyint 表示它们是否会更好?

查询提示:WITH (NOLOCK)

WITH (NOLOCK)专家通常表示,如果不需要准确的数据,则可以使用。是否有任何情况下您可以使用它并仍然获得准确的数据?在这种情况下,我们不会执行 UPDATE 或 DELETE,而只会执行 INSERT。插入通常也位于表的末尾(日期时间字段将增加)。不会查看最后几秒的查询不应受到插入的影响。

把它们加起来:

  • 对于唯一键我们该怎么办?
  • 我们的聚集索引应该是什么样的?
  • 我们应该使用查找表吗?
  • 我们可以使用WITH(NOLOCK)吗?
  • 还有其他最佳实践吗?

谢谢。

J.D*_*.D. 5

直接回答您的每个问题:

问:关于唯一密钥我们该怎么办? 问:我们的聚集索引应该怎样?

答:在大多数情况下,拥有一个很重要,primary key这样您就可以保证唯一性(包括本例)。仅在临时表中,不使用它可能是例外。它甚至可能对以后的即席调试有用,并且使用INTBIGINT自动增量字段作为一个字段很便宜。但是出于性能原因,您仍然可以(并且应该)将 logTime 列放在clustered index表上,因为它将在许多查询中用作谓词,并且从性能角度来看,SQL Server 所做的唯一标识符步骤是超级可以忽略不计的。如果您确实添加 LogId 作为主键,则可以nonclustered index向其添加 a。这样,如果您需要进行任何临时维护(例如删除/更新表中的特定记录,您可以通过主键有效地查找它们)。

问:我们应该使用查找表吗?

答:在这种情况下,我建议使用查找/“枚举”表。从节省空间或性能的角度来看,这并不是什么,但主要是因为如果其中一个值发生变化,您可以通过仅更新查找表来非常有效地维护它,而不是必须更新主表中引用该值的每条记录。从数据完整性角度来看,这是一个改进。它还可以防止锁定较大的主表(因此也有一些性能优势)。

问:我们可以使用WITH(NOLOCK)吗?

答:您不可能在此处使用WITH (NO LOCK) 查询提示并保证始终拥有100% 准确的数据,即使您从与 或 不同的行集中进行INSERTED选择UPDATED。我过去维护过确实使用它的查询,并且很少遇到问题,但我个人的选择(除非不关心数据准确性)是避免它。对于查询调优,有更好的性能改进替代方案。顺便说一句,我曾经对 NOLOCK 提示也有过同样的想法,您可以看到 Microsoft 的 David Browne 对我的问题的回答,以了解为什么无法保证数据准确性的决定性原因。简而言之,您选择的行可能是不断变化的数据结构的一部分,因为其他较远的行正在INSERTEDUPDATED同时也是同一数据结构的一部分,例如特定的 B 树,index两行都是一部分的。

问:还有其他最佳实践吗?

答:也许考虑其他可能需要查询日志的方式,例如特定用户、特定进程或两者的所有日志(甚至无论它们何时发生)?如果是这样,在这些字段上添加一些额外的内容nonclustered indexes也可以在以后对您有所帮助。例如,nonclustered index在 processUser、processID 上,然后单独nonclustered index在 processID 上(第一个index将覆盖仅使用 processUser 的谓词,或者使用 processUser 和 processID,这就是为什么您不需要nonclustered index仅在 processUser 上的第三个)。