2012-05-04 15 views
4

すべての例外(message、stackTraceなど)が格納されているテーブルがあり、テーブルが大きくなっており、減らしたいと考えています。Sqlサーバーの文字列インターンシップ

StackTraces、Messagesなどはたくさんありますが、圧縮を有効にするとサイズがわずかに縮小されます(10%)が、SQL Serverがカラムごとのハッシュテーブルでintern the stringsになるとはるかに大きな利点が得られると思います。

テーブルを正規化してStackTracesを別のテーブルに抽出すると、いくつかの利点が得られるかもしれませんが、例外メッセージ、例外タイプなども繰り返されます。

Sql Serverの一部の列に対して文字列インターンシップを有効にする方法はありますか?

+0

データを正規化することと、文字列のインターンリング(値を繰り返すのではなく、繰り返し値への参照を使用すること)との概念的な違いはありません。そして、あなたが@Aaronが非常に大きなサイズの違いを知っていることをテストしたなら、私は賭けます。 –

+0

サイズの変更は、メッセージ/タイプなどの選択性によって異なります。 –

+2

概念的な違いはありませんが、実用的なものがたくさんあります。 *スキーマとクエリが変更されます * 、および潜在的に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
    • スペース
    • を取り戻すためにHugeTable上のインデックスを再構築し、カラム
    • をMESSAGEIDするFKを追加
    • まず、テスト環境でこれを行います!
  • +1

    ありがとうAaron、私はこれのようなものを期待していた。 私は間違いなくスタックトレースのためにそうするでしょう、しかし、私は例外の型名が小さすぎて、独自のテーブルと例外メッセージがより多くのバリエーションを持つことができないと思います。ビルドインソリューションがあれば、私は3つすべてでそれを使用します。そうでしょう? – Olmo

    +0

    メッセージ列のサイズ、平均値のサイズ、および私が推測する一意の値の数によって異なります。詳細なしのゴールデンルールはありません。 –

    +2

    私は理解しています。私の意見では、ルックアップ・メッセージ・テーブルを作成するのが面倒に見える場合がありますが、その列にIntern = trueフラグを設定します。 – Olmo

    0

    Aaronさんの記事は、インターンをテーブルに追加する質問に回答しますが、その後、新しいスキーマを使用するためにアプリケーションコードとストアドプロシージャを変更する必要があります。

    ...と思われるかもしれません。実際には、古いスキーマと一致するデータを返すVIEWを作成することができます。INSERT操作もビューでサポートされ、MessagesおよびHugeTableテーブルの子操作に変換されます。わかりやすくするために、テーブルには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操作を処理するために:それは唯一の単一行の挿入をサポートし、完全に同時実行セーフではない、けれども前のデータが勝ったので:このTRIGGERを向上させることができる

    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) 
    

    注意これは、データのわずかなリスクがあることを意味します。InternedStringsテーブルにあります。また、UNIQUEインデックスのため、挿入が失敗します。 TRANSACTIONを使用し、クエリを変更してholdlockupdlockを使用するなど、これを処理するさまざまな方法があります。