我们有一个存储所有异常(消息、stackTrace 等)的表,该表越来越大,我们希望减少它。
有大量重复的 StackTraces、消息等,但启用压缩会产生适度的大小减少 (10%),而我认为如果 Sql Server 以某种方式将字符串实习在某个每列哈希表中,可能会带来更大的好处。
如果我规范化表并将 StackTraces 提取到另一个表,我可以获得一些好处,但异常消息、异常类型等也会重复。
有没有办法为 Sql Server 中的某些列启用字符串实习?
我们有一个存储所有异常(消息、stackTrace 等)的表,该表越来越大,我们希望减少它。
有大量重复的 StackTraces、消息等,但启用压缩会产生适度的大小减少 (10%),而我认为如果 Sql Server 以某种方式将字符串实习在某个每列哈希表中,可能会带来更大的好处。
如果我规范化表并将 StackTraces 提取到另一个表,我可以获得一些好处,但异常消息、异常类型等也会重复。
有没有办法为 Sql Server 中的某些列启用字符串实习?
没有内置的方法可以做到这一点。您可以轻松地执行以下操作:
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;
现在你需要做一些事情:
Aaron 的帖子回答了向表添加实习的问题,但之后您需要修改应用程序代码和存储过程以使用新模式。
...或者你可能会这么想。您实际上可以创建一个VIEW
返回与旧模式匹配的数据的 a,您还可以支持INSERT
视图上的操作,这些操作被转换为Messages
和HugeTable
表上的子操作。为了便于阅读,我将使用名称InternedStrings
和ExceptionLogs
表格。
所以如果旧表是这样的:
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
并将查询更改为使用holdlock
and updlock
。