2012-05-04 61 views
4

我們有一個表,我們存儲所有的異常(消息,stackTrace等),表越來越大,我們希望減少它。Sql Server字符串interning

有很多重複的StackTraces,Messages等等,但是啓用壓縮會產生適度的大小減少(10%),而我認爲如果Sql Server在某些每列哈希表中會有intern the strings會帶來更大的好處。

我可以得到一些好處,如果我規範化表和提取StackTraces到另一個,但異常消息,異常類型等等也重複。

有沒有辦法在Sql Server中爲某列啓用字符串實習?

+0

我看不出規範化數據和字符串實習(使用重複值的引用而不是重複值)之間的任何概念區別,除了一個是自動的,另一個是手動的。我敢打賭,如果你測試@Aaron建議你看到非常大的尺寸差異。 –

+0

尺寸的變化將取決於您對信息/類型等的選擇性。 –

+2

沒有任何概念上的差異,但有很多實際的差異: *模式和查詢更改 *曾經是簡單的插入將成爲N個查詢,並可能有N個插入。 我認爲重複的字符串已經足夠普遍,具有內置功能可能會有用。 – Olmo

回答

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; 

現在,你需要做的幾件事情:

  • 更改記錄程序的執行更新插入到郵件表
  • 加入適當的索引消息表(不確定消息數據類型)和PK
  • 將FK添加到MessageID列
  • 在HugeTable上重建索引以回收空間
  • 先在測試環境中執行此操作!
+1

非常感謝亞倫,我期待着這樣的事情。 我肯定會這樣做的堆棧跟蹤,但我認爲異常類型名稱太小,因爲他們自己的表和異常消息有更多的變化。如果有內置解決方案的話,我會在三種解決方案中使用它。你不是嗎? – Olmo

+0

取決於消息列的大小,平均值大小以及我猜測多少個唯一值。沒有更多的細節沒有黃金法則。 –

+2

我明白了。我的意思是,在我看來,有時候創建一個查詢消息表看起來很麻煩,但是我會在列上設置一個Intern = true標誌。 – Olmo

0

Aaron的帖子回答了向表中添加實習的問題,但之後您需要修改應用程序代碼和存儲過程以使用新的模式。

......你可能會這麼想。實際上,您可以創建一個返回與舊架構匹配的數據的VIEW,您也可以在該視圖上支持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索引,插入將失敗。有幾種可能的方法來處理這種情況,例如使用TRANSACTION並將查詢更改爲使用holdlockupdlock