sql >> データベース >  >> RDS >> Database

IDENTITY列を広げることによる影響の最小化–パート1

    [パート1|パート2|パート3|パート4]

    最近数回発生する問題は、INTとしてIDENTITY列を作成し、上限に近づいていて、それを大きくする必要があるシナリオです(BIGINT)。テーブルが整数の上限(20億以上)に達するほど大きい場合、これは火曜日の昼食とコーヒーブレイクの間に実行できる操作ではありません。このシリーズでは、このような変更の背後にあるメカニズムと、稼働時間にさまざまな影響を与えてそれを実現するためのさまざまな方法について説明します。最初の部分では、他の変数を使用せずにINTをBIGINTに変更した場合の物理的な影響を詳しく調べたいと思いました。

    INTを広げると実際に何が起こりますか?

    INTとBIGINTは固定サイズのデータ​​型であるため、一方から他方への変換はページに触れる必要があり、これがデータサイズの操作になります。 INTからBIGINTへのデータ型の変更では、ページ上にすぐに(そしてIDENTITY列に対して)追加のスペースを必要とすることはできないように思われるため、これは直感に反します。論理的に考えると、これは、既存のINT値が4バイトを超える値に変更された後まで必要になる可能性がなかったスペースです。しかし、これは今日の仕組みではありません。簡単なテーブルを作成して、以下を見てみましょう。

    CREATE TABLE dbo.FirstTest
    (
      RowID  int         IDENTITY(1,1), 
      Filler char(2500)  NOT NULL DEFAULT 'x'
    );
    GO
     
    INSERT dbo.FirstTest WITH (TABLOCKX) (Filler)
    SELECT TOP (20) 'x' FROM sys.all_columns AS c;
    GO

    簡単なクエリで、このオブジェクトに割り当てられた低ページと高ページ、および合計ページ数を知ることができます。

    SELECT 
      lo_page    = MIN(allocated_page_page_id), 
      hi_page    = MAX(allocated_page_page_id), 
      page_count = COUNT(*)
    FROM sys.dm_db_database_page_allocations
    (
      DB_ID(), OBJECT_ID(N'dbo.FirstTest'), NULL, NULL, NULL
    );

    データ型をINTからBIGINTに変更する前後にそのクエリを実行すると、次のようになります。

    ALTER TABLE dbo.FirstTest ALTER COLUMN RowID bigint;

    これらの結果が表示されます:

    -- before:
     
    lo_page    hi_page    page_count
    -------    -------    ----------
    243        303        17
     
    -- after:
     
    lo_page    hi_page    page_count
    -------    -------    ----------
    243        319        33

    必要な追加スペースのためのスペースを確保するために、16個の新しいページが追加されたことは明らかです(テーブル内のどの値も実際には8バイトを必要としないことがわかっていますが)。しかし、これは実際にはあなたが思うようには達成されませんでした。既存のページの列を広げるのではなく、行を新しいページに移動し、その場所にポインタを残しました。前後の243ページを見る(文書化されていないDBCC PAGE ):

    -- ******** Page 243, before: ********
     
    Slot 0 Offset 0x60 Length 12
     
    Record Type = PRIMARY_RECORD        Record Attributes =  NULL_BITMAP    Record Size = 12
     
    Memory Dump @0x000000E34B9FA060
     
    0000000000000000:   10000900 01000000 78020000                    ..	.....x...
     
    Slot 0 Column 1 Offset 0x4 Length 4 Length (physical) 4
     
    RowID = 1                           
     
    Slot 0 Column 2 Offset 0x8 Length 1 Length (physical) 1
     
    filler = x                          
     
     
    -- ******** Page 243, after: ********
     
    Slot 0 Offset 0x60 Length 9
     
    Record Type = FORWARDING_STUB       Record Attributes =                 Record Size = 9
     
    Memory Dump @0x000000E34B9FA060
     
    0000000000000000:   04280100 00010078 01                          .(.....x.
    Forwarding to  =  file 1 page 296 slot 376

    次に、ポインタのターゲット(296ページのスロット376)を見ると、次のようになります。

    Slot 376 Offset 0x8ca Length 34
     
    Record Type = FORWARDED_RECORD      Record Attributes =  NULL_BITMAP VARIABLE_COLUMNS
    Record Size = 34                    
    Memory Dump @0x000000E33BBFA8CA
     
    0000000000000000:   32001100 01000000 78010000 00000000 00030000  2.......x...........
    0000000000000014:   01002280 0004f300 00000100 0000               .."...ó.......
    Forwarded from  =  file 1 page 243 slot 0                                
     
    Slot 376 Column 67108865 Offset 0x4 Length 0 Length (physical) 4
     
    DROPPED = NULL                      
     
    Slot 376 Column 2 Offset 0x8 Length 1 Length (physical) 1
     
    filler = x                          
     
    Slot 376 Column 1 Offset 0x9 Length 8 Length (physical) 8
     
    RowID = 1

    これは、明らかに、テーブルの構造に対する非常に破壊的な変更です。 (そして興味深い副次的な観察:列の物理的な順序、RowIDとフィラーがページ上で反転されています。)予約済みスペースは136KBから264KBに跳ね上がり、平均断片化は33.3%から40%にわずかに上昇します。このスペースは、オンラインかどうかに関係なく、または再編成によって回復されることはありません。また、後で説明しますが、これはテーブルが小さすぎてメリットがないためではありません。

    注:これは、SQL Server 2016の最新のビルドでも当てはまります。このような操作は、最新バージョンではメタデータのみの操作になるように改善されていますが、明らかに修正されていません。特に、列がIDENTITY列であり、定義によって更新できない場合は、そうなる可能性があります。

    昨年お話しした新しいALTERCOLUMN/ ONLINE構文を使用して操作を実行すると、いくつかの違いが生じます。

    -- drop / re-create here
    ALTER TABLE dbo.FirstTest ALTER COLUMN RowID bigint WITH (ONLINE = ON);

    これで、前後は次のようになります。

    -- before:
     
    lo_page    hi_page    page_count
    -------    -------    ----------
    243        303        17
     
    -- after:
     
    lo_page    hi_page    page_count
    -------    -------    ----------
    307        351        17

    この場合、それはまだデータサイズの操作でしたが、ONLINEオプションにより、既存のページがコピーされて再作成されました。オンライン操作として列サイズを変更したときに、テーブルが同じページ数にさらに多くのデータを詰め込めるのはなぜか疑問に思われるかもしれません。各ページの密度が高くなり(行数は少なくなりますが、ページあたりのデータ数は多くなります)、分散が犠牲になります。断片化は33.3%から66.7%に倍増します。使用されたスペースは、同じ予約済みスペース内のより多くのデータを示します(72 KB /136KBから96KB/ 136 KBまで)。

    そして大規模に?

    テーブルを削除して再作成し、さらに多くのデータを入力してみましょう:

    CREATE TABLE dbo.FirstTest
    (
      RowID INT IDENTITY(1,1), 
      filler CHAR(1) NOT NULL DEFAULT 'x'
    );
    GO
     
    INSERT dbo.FirstTest WITH (TABLOCKX) (filler) 
    SELECT TOP (5000000) 'x' FROM sys.all_columns AS c1
      CROSS JOIN sys.all_columns AS c2;

    当初から、8,657ページ、断片化レベル0.09%、使用スペースは69,208 KB /69,256KBです。

    データ型をbigintに変更すると、25,630ページにジャンプし、断片化が0.06%に減少し、使用されるスペースは205,032 KB /205,064KBになります。オンライン再構築は何も変更せず、再編成も変更しません。再構築を含むプロセス全体は、私のマシンでは約97秒かかります(データの入力には2秒かかりました)。

    ONLINEを使用してデータ型をbigintに変更すると、バンプは11,140ページになり、断片化は85.5%になり、使用されるスペースは89,088 KB /89160KBになります。オンラインでの再構築と再編成は、まだ何も変更しません。今回は、プロセス全体で約1分しかかかりません。したがって、新しい構文により、操作が高速になり、追加のディスクスペースが少なくなりますが、断片化が大きくなります。取ります。

    次へ

    上記の私のテストを見て、いくつかのことについて疑問に思っていると思います。最も重要なのは、なぜテーブルがヒープなのかということです。インデックス、キー、または制約が詳細を曖昧にすることなく、ページ構造とページ数に実際に何が起こるかを調査したかったのです。また、この変更がなぜそれほど簡単だったのか不思議に思うかもしれません。真のIDENTITY列を変更する必要があるシナリオでは、おそらくクラスター化された主キーでもあり、他のテーブルに外部キーの依存関係があります。これは間違いなくプロセスにいくつかの問題をもたらします。これらについては、シリーズの次の投稿で詳しく見ていきます。

    [パート1|パート2|パート3|パート4]


    1. MariaDBでのDATE_FORMAT()のしくみ

    2. synatxエラーの近く(コード1)AndroidSQLiteを挿入

    3. 物理ファイルからのMySQLデータベースの復元

    4. 明示的結合と暗黙的結合?