4

我们有一个存储所有异常(消息、stackTrace 等)的表,该表越来越大,我们希望减少它。

有大量重复的 StackTraces、消息等,但启用压缩会产生适度的大小减少 (10%),而我认为如果 Sql Server 以某种方式将字符串实习在某个每列哈希表中,可能会带来更大的好处。

如果我规范化表并将 StackTraces 提取到另一个表,我可以获得一些好处,但异常消息、异常类型等也会重复。

有没有办法为 Sql Server 中的某些列启用字符串实习?

4

2 回答 2

4

没有内置的方法可以做到这一点。您可以轻松地执行以下操作:

SELECT MessageID = IDENTITY(INT, 1, 1), Message 
  INTO dbo.Messages 
  FROM dbo.HugeTable GROUP BY Message;

ALTER TABLE dbo.HugeTable ADD MessageID INT;

UPDATE h
   SET h.MessageID = m.MessageID
   FROM dbo.HugeTable AS h
   INNER JOIN dbo.Messages AS m
   ON h.Message = m.Message;

ALTER TABLE dbo.HugeTable DROP COLUMN Message;

现在你需要做一些事情:

  • 更改您的日志记录过程以对 Messages 表执行 upsert
  • 向消息表添加适当的索引(不确定消息数据类型)和 PK
  • 将 FK 添加到 MessageID 列
  • 在 HugeTable 上重建索引以回收空间
  • 首先在测试环境中执行此操作!
于 2012-05-04T16:15:51.740 回答
0

Aaron 的帖子回答了向表添加实习的问题,但之后您需要修改应用程序代码和存储过程以使用新模式。

...或者你可能会这么想。您实际上可以创建一个VIEW返回与旧模式匹配的数据的 a,您还可以支持INSERT视图上的操作,这些操作被转换为MessagesHugeTable表上的子操作。为了便于阅读,我将使用名称InternedStringsExceptionLogs表格。

所以如果旧表是这样的:

CREATE TABLE ExceptionLogs (
    LogId         int IDENTITY(1,1) NOT NULL PRIMARY KEY,
    Message       nvarchar(1024) NOT NULL,
    ExceptionType nvarchar(512)  NOT NULL,
    StackTrace    nvarchar(4096) NOT NULL
)

新表是:

CREATE TABLE InternedStrings (
    StringId int IDENTITY(1,1) NOT NULL PRIMARY KEY,
    Value    nvarchar(max)     NOT NULL
)

CREATE TABLE ExceptionLogs2 ( -- note the new name
    LogId         int IDENTITY(1,1) NOT NULL PRIMARY KEY,
    Message       int NOT NULL,
    ExceptionType int NOT NULL,
    StackTrace    int NOT NULL
)

添加索引以InternedStrings使值查找更快:

CREATE UNIQUE NONCLUSTERED INDEX IX_U_InternedStrings_Value ON InternedStrings ( Value ASC )

然后你也会有一个VIEW

CREATE VIEW ExeptionLogs AS

    SELECT
        LogId,
        MessageStrings      .Value AS Message,
        ExceptionTypeStrings.Value AS ExceptionType,
        StackTraceStrings   .Value AS StackTrace
    FROM
        ExceptionLogs2

        INNER JOIN InternedStrings AS MessageStrings       ON
            MessageStrings.StringId = ExceptionLogs2.Message

        INNER JOIN InternedStrings AS ExceptionTypeStrings ON
            ExceptionTypeStrings.StringId = ExceptionLogs2.ExceptionType

        INNER JOIN InternedStrings AS StackTraceStrings    ON
            StackTraceStrings.StringId = ExceptionLogs2.StackTrace

并处理INSERT来自未修改客户端的操作:

CREATE TRIGGER ExceptionLogsInsertHandler
    ON ExceptionLogs INSTEAD OF INSERT AS

    DECLARE @messageId int = SELECT StringId FROM InternedStrings WHERE Value = inserted.Message
    IF @messageId IS NULL
    BEGIN
        INSERT INTO InternedStrings ( Text ) VALUES ( inserted.Message )
        SET @messageId = SCOPE_IDENTITY()
    END

    DECLARE @exceptionTypeId int = SELECT StringId FROM InternedStrings WHERE Value = inserted.ExceptionType
    IF @exceptionTypeId IS NULL
    BEGIN
        INSERT INTO InternedStrings ( Text ) VALUES ( inserted.ExceptionType )
        SET @exceptionTypeId = SCOPE_IDENTITY()
    END

    DECLARE @stackTraceId int = SELECT StringId FROM InternedStrings WHERE Value = inserted.StackTrace
    IF @stackTraceId IS NULL
    BEGIN
        INSERT INTO InternedStrings ( Text ) VALUES ( inserted.StackTrace )
        SET @stackTraceId = SCOPE_IDENTITY()
    END

    INSERT INTO ExceptionLogs2 ( Message, ExceptionType, StackTrace )
    VALUES ( @messageId, @exceptionTypeId, @stackTraceId )

请注意,这TRIGGER可以改进:它仅支持单行插入,并不完全是并发安全的,尽管因为以前的数据不会发生变异,这意味着表中存在数据重复的轻微风险InternedStrings- 并且因为UNIQUE索引插入将失败。有不同的可能方法来处理这个问题,例如使用 aTRANSACTION并将查询更改为使用holdlockand updlock

于 2017-09-03T05:04:17.377 回答