今回も以下のように、前回と同じSQLのチューニングを行います。

select
    l_orderkey,
    sum(l_extendedprice * (1 - l_discount)) as revenue,
    o_orderdate,
    o_shippriority

from
    customer,
    orders,
    lineitem
where
    c_mktsegment = 'BUILDING'
    and c_custkey = o_custkey
    and l_orderkey = o_orderkey
    and o_orderdate < '1995-03-15'
    and l_shipdate > '1995-03-15'
group by
    l_orderkey,
    o_orderdate,
    o_shippriority
order by
    revenue desc,
    o_orderdate;

前回は、各テーブルに統計情報を取得したほか、HASH分散を行いDMS(ノード間データ移動)の発生を抑制しました。その結果、何もしていなかった時よりも65%高速化できることを確認しました。

このSQLはまだチューニングの余地がありそうなので、今回もこのSQLをベースにチューニングの手法を紹介します。

パーティション分割の検討

SQL Data Warehouseでは「パーティション分割」の機能が用意されています。パーティションの機能はさまざまなRDBMSで実装されており、SQL Data Warehouseでも利用可能で、主に以下の2点の目的で利用します。

  • データのメンテナンス
  • パフォーマンスの向上

パーティションの分割を行えば、データのメンテナンス性能とパフォーマンスの向上につながる可能性がありますが、パーティションの数を多くしすぎると逆にパフォーマンス低下につながってしまいます。特にクラスター化カラムストアインデックスに対してパーティション分割を行う場合は、この「多すぎるパーティション」は要注意で、1パーティション当たり最低でも6000万件以上のデータが必要になります(詳しくは第7回を参照してください)。

それでは、今回作成したlineitemテーブルのl_shipdate列でパーティション化を検討してみましょう。私の作成した環境ではl_shipdate列の年、月ごとに以下の件数が入っていました。

表1 l_shipdate毎のlineitemのデータ件数(縦:月、横:年)

  
1992年 1993年 1994年 1995年 1996年 1997年 1998年
1 9,582,148 77,319,033 77,306,651 77,304,390 77,307,322 77,301,612 77,303,389 473,424,545
2 26,893,471 69,832,618 69,816,217 69,825,985 72,315,505 69,838,587 69,830,218 448,352,601
3 47,917,745 77,302,737 77,316,067 77,306,194 77,292,910 77,310,943 77,323,239 511,769,835
4 65,236,395 74,821,333 74,808,528 74,821,757 74,804,418 74,820,828 74,814,999 514,128,258
5 77,295,226 77,301,237 77,296,790 77,301,141 77,313,822 77,311,481 77,302,598 541,122,295
6 74,798,314 74,818,781 74,819,580 74,821,200 74,812,722 74,801,290 74,806,715 523,678,602
7 77,320,352 77,300,917 77,309,443 77,291,896 77,310,877 77,295,345 77,304,720 541,133,550
8 77,287,819 77,299,704 77,333,931 77,314,714 77,292,832 77,312,581 68,945,856 532,787,437
9 74,816,010 74,805,164 74,810,885 74,818,904 74,816,308 74,816,704 47,921,137 496,805,112
10 77,305,995 77,301,525 77,301,536 77,297,505 77,301,405 77,312,406 30,035,274 493,855,646
11 74,826,553 74,817,419 74,811,820 74,801,185 74,798,002 74,825,366 10,202,979 459,083,324
12 77,314,444 77,300,381 77,312,674 77,303,751 77,296,519 77,299,954 20,781 463,848,504
760,594,472 910,220,849 910,244,122 910,208,622 912,662,642 910,247,097 685,811,905 5,999,989,709

この表からは、年月でパーティション分割を行うと、1998年9月や10月は6000万件を下回っていることを確認できます。

また、本テーブルは前回にHASH分散を行いましたが、各ディストリビューションでデータが偏っている可能性があります。パーティション分割する際の「6000万件を下回らないようにする」という条件は、「各ディストリビューションで100万件を下回らないようにする」ことの絶対条件となります(100万件×60ディストリビューション=6000万件)。

しかし、ディストリビューションでの偏りが多少発生することも想定すると、ぎりぎり6000万件のデータをパーティション分割するよりも、少し余裕を持たせた範囲でパーティション分割するほうが安全です。以上により、今回の例では「年月」でパーティション分割するのではなく、年単位でパーティション分割します。

パーティション分割の方法

SQL Data WarehouseではSQL Serverと高い互換性のあるDatabase Serviceですが、パーティション分割に関してはSQL Serverでのパーティション分割とは作成の方法が少し異なり、定義が簡略化されています。

SQL Serverではパーティション分割する際、パーティション関数やパーティション構成を定義しなければなりませんが、SQL Data Warehouseでは定義する必要はありません。SQL Data Warehouseでのパーティション分割の構文は、パーティション分割を行う列と、パーティション分割の境界点の定義のみで、パーティションの構成が可能です。1つのテーブルに対して、1つの列のみパーティション化を行えます。また、境界点に対して「RANGE RIGHT」または「RANGE LEFT」を指定します。

では、lineitemテーブルのパーティション分割を行ってみましょう。

CREATE TABLE lineitem_PARTITION

WITH
(
    DISTRIBUTION = HASH ( l_orderkey ),
    CLUSTERED COLUMNSTORE INDEX,
    PARTITION (l_shipdate RANGE RIGHT FOR VALUES(
        '1992/01/01',
        '1993/01/01',
        '1994/01/01',
        '1995/01/01',
        '1996/01/01',
        '1997/01/01',
        '1998/01/01'
        )
    )
)
AS SELECT * FROM lineitem;

RENAME OBJECT lineitem to lineite_NON_PARTITION;
RENAME OBJECT lineitem_PARTITION to lineitem;

CREATE STATISTICS Stat_lineitem_l_orderkey ON lineitem(l_orderkey);
CREATE STATISTICS Stat_lineitem_l_shipdate ON lineitem(l_shipdate);

前回もお伝えしたように、統計情報は必ず取得するようにしましょう。また、上記のコマンドのようにクラスター化列ストアインデックスをパーティション分割により再構成をする時は、比較的大量のメモリを必要とします。SQL Data Warehouseで使用できるメモリ量をコントロールするために、リソースクラスをデフォルトのsmallrcから、largercなどに切り替えて実行するようにしてください。

結果

それでは、パーティション分割によりどれくらい高速化できたか結果を確認してみましょう。結果は以下の通りです。

上記の通り、巨大なテーブルをパーティション分割することでクエリは高速化されました。SQL Data Warehouseでは大容量、大量の件数のテーブルとなることも多いと思います。パーティション分割を正しく利用すればクエリは高速化できるので、大量の件数が格納されているテーブルなどはパーティション分割を検討するようにしましょう。

山口 正寛


1984年生まれ。大阪府出身、東京都在住。データベースエンジニア。SQL Server、Oracle、MySQL、PostgreSQLなどのデータベースで、小規模から大規模な案件まで数多く経験。現在ではクラウドの流れに逆らうことなく、「データベース×クラウド」をキーワードに案件対応、セミナー活動、執筆活動など幅広く活動中。株式会社システムサポート所属。