2011年2月28日月曜日

TPC-H blocksizeを 32kに

前回のつづき

前回は blocksizeはデフォルトの 8kで作成しましたが、今回は 32kにしてみました。
DWHは出来るだけ blocksizeを大きくするのが定石ですが、結果は、、、

blocksizeElapsedQphH@32GB
8KB3,436 sec974
32KB3,342 sec986

若干速くなってはいるものの、わずか数%良くなっただけ。

今回は Paralle Degreeを初めから 6にしてテーブルを作成しているため、ディスクへのアクセスは direct readになっています。strace(1)を使って、読み込みサイズを見ると、どちらも 1MBでしたので、処理時間が変わらないことも納得がいきます。

direct readの時の I/Oサイズは db_file_multiblock_read_count=128 から、8k x 128 = 1MBとなります。
blocksize=32kの場合も 1MBなのは _db_file_direct_io_count = 1048576 によって決められているようです。
簡単に試した結果 _db_file_direct_io_countを大きくすると、pread(2)の際のサイズも大きくなることを確認しました。このパラメータに関する検証は、後の課題にしたいと思います。

とりあえず今回の結論は、direct readでアクセスする場合、大事なのは blocksizeではなく I/Oサイズ。

本日の結果: 986 QphH@32GB

次回は Linux I/O Schdulerを、デフォルトの cfqから noopに変えてみます。
実は驚くべき結果が。。。

2011年2月23日水曜日

TPC-H QphHはどうやって計算するのか

TPC-Hでは QphHで速度性能を表現しています。

TPC-H Non-Clusteredの Oracleでの記録を見ると、現在の所の第1位は以下のような記録でした。

  • 1000GBで  140,181 QphH    12.15 $/QphH
  • 3000GBで  198,907 QphH    16.58 $/QphH 
(正確には 140,181QphH@1000GB  などと、ScaleFactor(SF)を @に続けて書きます。
 ちなみに SFが異なる値を比較するのは意味が無い と言われている。)

QphHの名前((Query-per-Hour Performance Metric))から単純に考えると 1時間あたりの何クエリー発行できるか  という値に見えるが、実際の計算はもう少し複雑。

前回、とりあえず 22個の TPC-HのSQLを実行したら
3436秒 = 57分16秒   かかったと書いたが、
では、これは  1時間あたり 約22クエリーなので   22 QphH@32GB  なのか?

実際に計算したら 22ではなく、 974 QphH だった!


詳しく見ていきましょう。

TPC-Hで表示されている QphHは、正確には "TPC-H Composite Query-per-Hour Metric"  と呼ばれており、Compositeという通り、

TPC-H Power (1セッションで、どれだけ速く処理できるか) 
TPC-H Throughput (複数セッションで、どれだけ速く処理できるか)

の2つの値から計算される値となります。
具体的には、それらの値の相乗平均が QphH となります。




では、TPC-H Powerの計算方法は、というと、3600*SF の値を、22個のSQLの処理時間の相乗平均で割った値となります。数式にするとこんな感じ。


QIは Query Intervalで、各SQLの処理時間(秒) が入ります。
分母は相乗平均なので1つでも短いクエリーがあるとかなり値は良くなってきます。
例えば、22個のSQL文が全て10秒で終了して合計220秒の場合は 11,520QphHとなります。
しかし、1個のSQLが199秒かかったが他の21個のSQLが1秒で終了して合計220秒の場合は 90,564QphHとなり約8倍のパフォーマンスとなります!!
なので、きちんと TPC-Hを測定する場合は、いくつかのSQLは捨てる代わりに、思いっきり早いSQLを作るというテクニックを使う場合もあるようです。今回はOracleの機能や構成による違いをみたいので、そこは追求しませんでした。
(本当は22個のSQLの他にインサート、デリートをする2つのSQL(Refresh Functionsと呼ばれています)があるのですが、今回は割愛。大体上記の計算式で合っています。)
で、
今回、自分は1セッションで検証したので TPC-H Powerの値を測っていることになりますが、各Queryの実行時間は
Q1   226.78 sec
Q2     18.17 sec
...
Q22 39.88 sec
で、これにエクセルで (3600 * SF) / (product(Q1~Q22) ^ (1/22) ) なんて計算をして、974 QphH  というのが出てきました。


TPC-H Throughputは、今後の複数セッションの検証の時に説明しますが、いくつかのTPC-Hのレポートを見ると、若干 TPC-H Throughputの方が高い数値となる傾向にあるようですが、概ね同じ程度の値となります。なので、エイヤーッと、 QphH と 自分の TPC-H Powerをそのまま比較することにします。

すると  14万~19万 QphHと 974QphHなので、約140倍から200倍の差  というところになります。

今回検証した環境の値段は HWは 約12万円ですが、Oracle価格と3年保守を入れないといけないので、それだけで数百万。。。    $/QphH  は多分、100 $/QphH 位でしょうか。こちらも 6~8倍とかなり負けてます。



これで一応計測の目安が出来たので、次からはいくつか構成を変えながら、出来るだけパフォーマンスアップ出来るように考えていきます。


本日の結果: 974 QphH@32GB


今回参考にした TPC-Hの仕様書
TPC BENCHMARK H  Standard Specification

リポジトリの作成と とりあえず測ってみた

今回のテストはデータがメモリから充分溢れるように、メモリサイズ16GBの倍の 32GB準備することにしました。

データのロードは外部表を使うのが一番簡単で早い。
また 6coreのCPUなのでデータを6つに分けることと、データを圧縮して速度向上を図ってみました。SSDだと圧縮効果が薄れると思うが、HDDを使う場合は圧縮することによりかなり時間が短くなります。
$ dbgen -f -C6 -S1 -s32 &
$ dbgen -f -C6 -S2 -s32 &
...
$ dbgen -f -C6 -S6 -s32 &

$ gzip *.tbl

以下が、データロードするSQL文。lineitemだけを書いているが、これを他の7つのテーブルにも行う。(元になるSQL文は Create Your Own Oracle TPC-H Playground on Linux  の情報を大いに参考にして作成しました。)

CREATE OR REPLACE DIRECTORY tpch_dir AS '/home/oracle/....'
/
CREATE OR REPLACE DIRECTORY zcat_dir AS '/bin'
/

CREATE TABLE lineitem_ext (
    l_orderkey NUMBER(10)
  , l_partkey NUMBER(10)
  , l_suppkey NUMBER(10)
  , l_linenumber NUMBER(38)
  , l_quantity NUMBER
  , l_extendedprice NUMBER
  , l_discount NUMBER
  , l_tax NUMBER
  , l_returnflag CHAR(1)
  , l_linestatus CHAR(1)
  , l_shipdate VARCHAR2(10)
  , l_commitdate VARCHAR2(10)
  , l_receiptdate VARCHAR2(10)
  , l_shipinstruct VARCHAR2(25)
  , l_shipmode VARCHAR2(10)
  , l_comment VARCHAR2(44)
  )
  ORGANIZATION EXTERNAL (
    TYPE oracle_loader
    DEFAULT DIRECTORY tpch_dir
    ACCESS PARAMETERS ( RECORDS DELIMITED BY NEWLINE
       PREPROCESSOR zcat_dir:'zcat'
       BADFILE 'bad_%a_%p.bad'
       LOGFILE 'log_%a_%p.log'
       FIELDS TERMINATED BY '|'
       MISSING FIELD VALUES ARE NULL
    )
    LOCATION (
        'lineitem.tbl.1.gz'
      , 'lineitem.tbl.2.gz'
      , 'lineitem.tbl.3.gz'
      , 'lineitem.tbl.4.gz'
      , 'lineitem.tbl.5.gz'
      , 'lineitem.tbl.6.gz'
    )
  ) 
  PARALLEL 6 REJECT LIMIT 0 NOMONITORING
/

CREATE TABLE lineitem (
    l_orderkey NUMBER(10) NOT NULL
  , l_partkey NUMBER(10) NOT NULL
  , l_suppkey NUMBER(10) NOT NULL
  , l_linenumber INTEGER NOT NULL
  , l_quantity NUMBER NOT NULL
  , l_extendedprice NUMBER NOT NULL
  , l_discount NUMBER NOT NULL
  , l_tax NUMBER NOT NULL
  , l_returnflag CHAR(1) NOT NULL
  , l_linestatus CHAR(1) NOT NULL
  , l_shipdate DATE NOT NULL
  , l_commitdate DATE NOT NULL
  , l_receiptdate DATE NOT NULL
  , l_shipinstruct VARCHAR2(25) NOT NULL
  , l_shipmode VARCHAR2(10) NOT NULL
  , l_comment VARCHAR2(44) NOT NULL
  )
  PCTFREE 1 PCTUSED 99 PARALLEL 6
/

INSERT /*+append*/ INTO lineitem
SELECT 
   l_orderkey
 , l_partkey
 , l_suppkey
 , l_linenumber
 , l_quantity
 , l_extendedprice
 , l_discount
 , l_tax
 , l_returnflag
 , l_linestatus
 , to_date(l_shipdate, 'yyyy-mm-dd')
 , to_date(l_commitdate, 'yyyy-mm-dd')
 , to_date(l_receiptdate, 'yyyy-mm-dd')
 , l_shipinstruct
 , l_shipmode
 , l_comment
FROM lineitem_ext
/

BEGIN
  dbms_stats.gather_table_stats(ownname => 'TPCH_&TESTID.', tabname => 'LINEITEM', degree => 6, cascade => TRUE) ;
END;
/

また、SSDなので書き込みが発生しないように、テーブルスペースを readonlyにしておくことも忘れない。
ALTER TABLESPACE tpch_&TESTID. READ ONLY;


あと、もう1つ大人な事情があった!
今回はインデックスを利用できない時の処理速度を測定したかったので、TPC-Hを正確に測定する際は必ず作成しなければいけない primary key, foreign key を全て作成しないようにしました。
なので全てのデータに対して FULL Table Scanとなります。例えば lineitemであれば、次のようなインデックスを作成する必要があるのです。

-- PK Constraints
CREATE UNIQUE INDEX lineitem_pk 
  ON lineitem(l_linenumber, l_orderkey) PARALLEL 2
/
ALTER TABLE lineitem ADD CONSTRAINT lineitem_pk 
  PRIMARY KEY(l_linenumber, l_orderkey)
  USING INDEX lineitem_pk
/

-- FK Constraints
ALTER TABLE lineitem ADD CONSTRAINT lineitem_partsupp_fk 
  FOREIGN KEY(l_partkey, l_suppkey) 
  REFERENCES partsupp(ps_partkey, ps_suppkey) NOT DEFERRABLE
/
ALTER TABLE lineitem ADD CONSTRAINT lineitem_orders_fk 
  FOREIGN KEY (l_orderkey) 
  REFERENCES orders(o_orderkey) NOT DEFERRABLE
/

まあ、正確なTPC-Hの測定にはなりませんが、余計なインデックスは邪魔なので良しとします。

で、前回説明したとおり qgenで作成したSQL文 22個を
デフォルトの blocksize=8k で作成したデータで、とりあえず実行してみた。
そしたら 3436秒 = 57分16秒 だった。

果たしてこれは速いのか。

次回は TPC-Hで使う QphHの計算方法について触れ、
上記の処理時間が、どれ位なものなのかを考えてみることにしよう。


※ 2011/4/5 追記: TPC-H用リポジトリを作成するSQL文の元ネタの場所を追記しました。

2011年2月19日土曜日

TPC-H のテスト 開始

贅沢に SSD6枚のマシンを使える機会があったので、こりゃラッキーと DWHマシンとしての検証開始!
小幡さん新久保君の構成よりかなり贅沢なので、そこで出来なかったことが出来るぞ、ということでガッツリ検証してみます。


まずは今回のマシンスペックから。 
Memory: 16GB (4GBx4)
SSD: Crucial CTFDDAC064MAG-1G1(64GB)  x 6 (1枚はOS用、あとはデータ用)




内臓(SSDx6)を引き出したところ。
SATAと電源のケーブルでいっぱい。

SSDはそれぞれSATA3 6Gb/sに接続している。
今回のSSDはカタログスペックで読み込み350MB/secとあるので、これを5枚使って、どれ位まで性能を上げられるか楽しみ。

とりあえずはDiskの性能測定から。
SSD 5枚は BIOSレベルで RAID-0(Stripe Size=64k)を組んだので、理論値では 1.5GB/secを越えるが、このCPU,MBの組み合わせで、どれ位のスループットが出るのだろう。


読み込みはカタログスペック通り350MB近く。(いや 90%しか性能が出ていないが...)
5枚のRAID-0では 読み込み1GB/sec を越えてきている。後はこの性能を出し切れるか。
ちなみにLinux I/O Schedulerは cfqにて測定。

今回のTPC-H測定は大人な事情があって、HammerOraなどのツールを使わず、TPCのサイトからダウンロード出来るクエリージェネレーター qgenで生成した生のTPC-H用SQLを使って、SQLが終了するまでの時間を測定した。

では次回から検証をスタートしてみる。