2010/10/26

11gR2からのフルスキャン

従来からフルスキャン(通常 db file scattered read)をバッファーキャッシュを迂回するdirect path readに変更する手法としていくつか手段がありました。

* 今回は、Parallel Queryは除外

1. "_serial_direct_read"を設定する

SQL> alter session set "_serial_direct_read"=true;

2. event 10355を設定

$ oerr ora 10355
10355, 00000, "turn on direct read path for scans"
// *Cause:
// *Action:  enable direct async read for scans

11gR2からは、フルスキャンでdb file scattered readを使うのか、direct path readを使うのか、Oracleが自動で決定しているようです。
勝手に選択されるのも、時と場合によっては問題になる可能性があるので、その仕組みを少し調査してみます。

以下の隠しパラメータが影響して、I/Oの制御を行っているようです。

"_small_table_threshold"
lower threshold level of table size for direct reads

"_very_large_object_threshold"
upper threshold level of object size for direct reads

セグメントサイズ > 5 * "_small_table_threshold" * blocksizeの場合にdirect path readを選択するようになります。

ただし、これは、event 10949で制御が可能です。

$ oerr ora 10949
10949, 00000, "Disable autotune direct path read for full table scan"
// *Cause:
// *Action:  Disable autotune direct path read for serial full table scan.

しかし、event 10949でDisableにしても

セグメントサイズ > "_very_large_object_threshold" (MB)の場合は、Disableにされません。

つまり

どうしてもdirect path readを選択したくない場合は、

1. 10949でdirect path readをdisableに設定する
さらに
2. "_very_large_object_threshold"を大きな値に設定する

* ただし、隠しパラメータですので、ご使用とご用法には注意してください。

2010/10/17

SQLパフォーマンスワークショップ

今回は、SQLパフォーマンスワークショップの話しを少しだけ。

第2回 SQLパフォーマンスワークショップ

エンバカデロ・テクノロジーズさんとインサイトテクノロジー共同開催でワークショップを行うことになりました。

このワークショップで何を話そうかなと今まさに考えているのですが、まずはパフォーマンスチューニングのベーシックな
部分をきちんとお話ししようかなと思っています。

あと、ケーススタディを多めにして、参加者の方が考えながら参加できる内容にしたいと思っています。是非とも参加して
みてください。

1. ディスクI/O系の話

データベースのボトルネックになりやすいのはディスクI/Oです。

データベースのディスクI/Oにはいくつかの種類が存在します。それらを上手く制御することがチューニングとなります。

では、ディスクI/Oを上手く制御できているのか? いないのか? はどうやって判断するのか?

2. コンテンション系の話


多くのデータベースは多数のユーザーが同時利用することを前提に設計されているので、1つのリソースを多数で共有する
ための排他制御が様々な場所に実装されています。

排他制御はデータベース・カーネル側で制御しているラッチや内部ロックとユーザー自身で制御するロック(いわゆる行ロック)などがあります。

この排他制御を上手く行わないと、スループットが上がらないわけです。

3. コンフィギュレーション系の話

さらに、データベースの基本設定(パラメータや物理設計)の良し悪しでも、パフォーマンスに影響があります。


さすがに全部話していると時間がない気がしますが、出来る限りギュッとまとめて話したいと思います。

2010/10/10

パーティション・ビューって..

Oracle EEのオプションで最も使うと思われるものがPartitioning Optionではないかと個人的に思っています。小幡さんのブログ(Storage Serverフィルタリング考察)にてPartition Viewという懐かしいモノが紹介されていました。7.2で鳴り物入りと書かれていましたが、すぐにPartitioning Tableがリリースされて陽の目を見なかったですね。。。

しかし、Partition ViewにはPartitioning Tableにない素晴らしい点があります。それは、EEじゃなくても使える。頑張って作りこめばPartitioning Optionみたいに使える(これは利点なのか...?)ことです。

というわけで、一回、Partition Viewをまとめてみます。

* 個人的には、昔、この機能を使いこなそうとかなり苦労しました。

まず、大前提です。

  1. 基本的には、Partitioning Tableのように論理的に一つのテーブルとして扱えません(あくまでも1つのビューです)
  2. なので、グローバルインデックスや、カラムの追加/削除、パーティションの追加/削除といったことは透過的に実行できません。
  3. さらに、DMLもビューに対して実行できません(union all viewなので)

もう一度書きますが、あくまでもビューです。

では、普通のビューとどう違うのか?
それは、たった1つなのですが、現在のPartitioning TableのようにPartition ViewへのQueryの実行計画をPartitionを考慮して立ててくれること。です。

事前に準備すべき事は以下となります。

  1. 各テーブルのPartition Keyとなるカラムにはチェック制約が必要
  2. 初期化パラメータ(partition_view_enabled)がTRUE(ただし、大昔から、このパラメータは無くなり_partition_view_enabledがデフォルトTRUEとなっていますので、余り気にしない)

1のチェック制約を見て、ナルホドと思いますね。単純というか何と言うか...

では、一応、やってみます。

-- 通常のビュー用
-- 2010 Q1
create table t2010q1 (term date
     , id number
     , text varchar2(4000));
-- 2010 Q2
create table t2010q2 (term date
     , id number
     , text varchar2(4000));
-- 2010 Q3
create table t2010q3 (term date
     , id number
     , text varchar2(4000));
-- 2010 Q4
create table t2010q4 (term date
     , id number
     , text varchar2(4000));

-- パーティションビュー用
-- 2010 Q1
create table p2010q1 (term date
     , id number
     , text varchar2(4000)
     , constraint p2010q1_chk
      check(term >= to_date('2010/01/01 00:00:00', 'yyyy/mm/dd hh24:mi:ss')
      and   term <= to_date('2010/03/31 23:59:59', 'yyyy/mm/dd hh24:mi:ss'))
     );
-- 2010 Q2
create table p2010q2 (term date
     , id number
     , text varchar2(4000)
     , constraint p2010q2_chk
      check(term >= to_date('2010/04/01 00:00:00', 'yyyy/mm/dd hh24:mi:ss')
      and   term <= to_date('2010/06/30 23:59:59', 'yyyy/mm/dd hh24:mi:ss'))
     );
-- 2010 Q3
create table p2010q3 (term date
     , id number
     , text varchar2(4000)
     , constraint p2010q3_chk
      check(term >= to_date('2010/07/01 00:00:00', 'yyyy/mm/dd hh24:mi:ss')
      and   term <= to_date('2010/09/30 23:59:59', 'yyyy/mm/dd hh24:mi:ss'))
     );
-- 2010 Q4
create table p2010q4 (term date
     , id number
     , text varchar2(4000)
     , constraint p2010q4_chk
      check(term >= to_date('2010/10/01 00:00:00', 'yyyy/mm/dd hh24:mi:ss')
      and   term <= to_date('2010/12/31 23:59:59', 'yyyy/mm/dd hh24:mi:ss'))
     );

-- 通常のビューの作成
create or replace view t2010 as
select * from t2010q1
union all
select * from t2010q2
union all
select * from t2010q3
union all
select * from t2010q4;

-- パーティションビューの作成
create or replace view p2010 as
select * from p2010q1
union all
select * from p2010q2
union all
select * from p2010q3
union all
select * from p2010q4;


先程、書きましたが、通常のビューとパーティションビューの違いは、メンバーとなるテーブルにチェック制約がついているか否かの違いです。
では、データを入れますが、ビュー経由で直接データのinsertはできません、やりたいなら、(Partitioning Tableのように扱いたいなら)ビューに対してinstead triggerなどを仕込む必要があります。(今回は、面倒なので、テーブルにinsertしています)
declare
 dt date;
 tbl varchar2(30);
begin
 for dy in 0 .. 364 loop
  dt := to_date('2010/01/01 00:00:00', 'yyyy/mm/dd hh24:mi:ss') + dy;

  if dt >= to_date('2010/01/01 00:00:00', 'yyyy/mm/dd hh24:mi:ss') and
     dt <= to_date('2010/03/31 23:59:59', 'yyyy/mm/dd hh24:mi:ss') then
   tbl := '2010q1';
  elsif dt >= to_date('2010/04/01 00:00:00', 'yyyy/mm/dd hh24:mi:ss') and
     dt <= to_date('2010/06/30 23:59:59', 'yyyy/mm/dd hh24:mi:ss') then
   tbl := '2010q2';
  elsif dt >= to_date('2010/07/01 00:00:00', 'yyyy/mm/dd hh24:mi:ss') and
     dt <= to_date('2010/09/30 23:59:59', 'yyyy/mm/dd hh24:mi:ss') then
   tbl := '2010q3';
  elsif dt >= to_date('2010/10/01 00:00:00', 'yyyy/mm/dd hh24:mi:ss') and
     dt <= to_date('2010/12/31 23:59:59', 'yyyy/mm/dd hh24:mi:ss') then
   tbl := '2010q4';
  else
   tbl := '';
  end if;

  if tbl is not null then
   for h in 0 .. 23 loop
    for m in 0 .. 60 loop
     begin
      execute immediate 'insert all ' ||
           'into ' || 't' || tbl || ' values (:1, :2, :3) ' ||
           'into ' || 'p' || tbl || ' values (:4, :5, :6) ' ||
           'select * from dual'
         using dt + h/24 + m/24/60
          , dy + h/100 + m/10000
          ,'sample'
          , dt + h/24 + m/24/60
          , dy + h/100 + m/10000
          ,'sample';
     exception
      when others then
       null;
     end;
    end loop;
    commit;
   end loop;
   commit;
  end if;
 end loop;
 commit;
end;
/

では、一番大事な、SELECTの実行計画を見てみます。
SQL> select count(*) from t2010 where term = to_date('2010/01/01','yyyy/mm/dd');

  COUNT(*)
----------
         1


実行計画
----------------------------------------------------------
Plan hash value: 709745821

--------------------------------------------------------------------------------
| Id  | Operation            | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |         |     1 |     9 |   548   (1)| 00:00:07 |
|   1 |  SORT AGGREGATE      |         |     1 |     9 |            |          |
|   2 |   VIEW               | T2010   |    20 |   180 |   548   (1)| 00:00:07 |
|   3 |    UNION-ALL         |         |       |       |            |          |
|*  4 |     TABLE ACCESS FULL| T2010Q1 |     5 |    45 |   137   (1)| 00:00:02 |
|*  5 |     TABLE ACCESS FULL| T2010Q2 |     5 |    45 |   137   (1)| 00:00:02 |
|*  6 |     TABLE ACCESS FULL| T2010Q3 |     5 |    45 |   137   (1)| 00:00:02 |
|*  7 |     TABLE ACCESS FULL| T2010Q4 |     5 |    45 |   137   (1)| 00:00:02 |
--------------------------------------------------------------------------------

全てのパーティション(あえてパーティションと呼びます)に適切なインデックスは作成していないので、T2010Q1パーティションのフルスキャンは許せますが、全パーティションにフルスキャンが発生しています。何とかしたいですよね。
続いてパーティションビューの実行計画を見てみます。
SQL> select count(*) from p2010 where term = to_date('2010/01/01','yyyy/mm/dd');

  COUNT(*)
----------
         1


実行計画
----------------------------------------------------------
Plan hash value: 3622780762

---------------------------------------------------------------------------------

| Id  | Operation             | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |         |     1 |     9 |   137   (1)| 00:00:02 |
|   1 |  SORT AGGREGATE       |         |     1 |     9 |            |          |
|   2 |   VIEW                | P2010   |     8 |    72 |   137   (1)| 00:00:02 |
|   3 |    UNION-ALL          |         |       |       |            |          |
|*  4 |     TABLE ACCESS FULL | P2010Q1 |     5 |    45 |   137   (1)| 00:00:02 |
|*  5 |     FILTER            |         |       |       |            |          |
|*  6 |      TABLE ACCESS FULL| P2010Q2 |     5 |    45 |   137   (1)| 00:00:02 |
|*  7 |     FILTER            |         |       |       |            |          |
|*  8 |      TABLE ACCESS FULL| P2010Q3 |     5 |    45 |   137   (1)| 00:00:02 |
|*  9 |     FILTER            |         |       |       |            |          |
|* 10 |      TABLE ACCESS FULL| P2010Q4 |     5 |    45 |   137   (1)| 00:00:02 |
---------------------------------------------------------------------------------

先程と違いFILTERオペレーションが追加されました。これは、FILTERの結果FALSEなら、後続のオペレーションを実行する。という意味なので。
P2010Q1パーティションは必ずフルスキャンを実行しますが、その他のパーティションには、FILTERがかかり、フルスキャンを実行しない(というかテーブルへのアクセスもしない)ということになります。

かなり、かなり限定的(もしくは、相当いじり倒せば)Partition Viewも活躍できる場がありそうです。皆様も困った時に思い出してみてください。

2010/10/07

I/OスケジューラでSSDのパフォーマンスは変わるのか?

以前、Unbreakable Enterprise KernelのI/O schedulerがdeadlineに変更されたとブログに書きました。
また、SSDであればnoopの方が合っているかもしれないとコメントに書きました。

実際のところ、どうなのでしょうか? 検証してみます。

まずは、I/O schedulerを変更してみます。変更するには3通りあるのですが、

1. bootパラメータ(elevator)を変更する
2. /sys/block/<device>/queue/schedulerを直接変更する
3. udevのルールを変更する

今回は、検証なので、2の直接書き換え方式を使っていますが、通常はは、SSDのみをnoopとしたい、かつ、リブートで元に戻って欲しくない等になるので、3のudevルールで対応すると思います。

以下のudevのルールを/etc/udev/rule.dに作成します。

SUBSYSTEM=="block", SYSFS{queue/rotational}=="0", RUN+="/bin/sh -c 'echo noop > /sys$devpath/queue/scheduler'"

* queue/rotationalは磁気ディスクであれば1が設定され、SSDであれば0が設定されますが、USBのような安価なフラッシュ
  ディスクの場合は1が設定される場合があるようです


今回は、15セッションでTPC-Cのベンチマークを実施してみました。

1. deadline
$ su - root -c "echo deadline > /sys/block/sdc/queue/scheduler"
$ su - root -c "echo 3 > /proc/sys/vm/drop_caches"



Unbreakable Enterprise KernelではデフォルトとなっているdeadlineI/Oスケジューラのパフォーマンスをチェックしておきます。 今回のテストの結果では、平均のレスポンスタイムが22msとなっていました。

2. noop
$ su - root -c "echo noop > /sys/block/sdc/queue/scheduler"
$ su - root -c "echo 3 > /proc/sys/vm/drop_caches"



I/Oスケジューラを今回のテスト目的であるnoopに変更してのパフォーマンスをチェックしてみます。 今回のテストの結果では、平均のレスポンスタイムはdeadlineと同じ22msとなっていました。

3. cfq
$ su - root -c "echo cfq > /sys/block/sdc/queue/scheduler"
$ su - root -c "echo 3 > /proc/sys/vm/drop_caches"



更に、以前のカーネルでデフォルトであったcfqのパフォーマンスも一応見てみます。 平均のレスポンスタイムは23msとなり、他のI/Oスケジューラと遜色ないパフォーマンスでした。

ということ、今回のテストでは、I/Oスケジューラでのパフォーマンスにおける変化はみられませんでした。もう少し、I/Oが厳しい環境でテストすると状況が変わってくるかも知れませんが。。。

2010/10/04

続 filesystemio_optionsと非同期I/O

前回(filesystemio_optionsと非同期I/O)でfilesystemio_options=asynchの動作が非同期I/Oになっていないのではないか?と書きましたが、もう少し、状況証拠をとるためにTPCのベンチマークを取得しました。

filesystemio_options=noneの場合とasynchの場合でほぼ同一の結果となっており、ますます、filesystemio_options=asynchの動作の怪しさが増しています。

- filesystemio_options=none
swingbenchの結果

ASH Viewerの結果

commitクラスの待機イベント

- filesystemio_options=asynch
swingbenchの結果
ASH Viewerの結果

commitクラスの待機イベント

- filesystemio_options=setall
swingbenchの結果
ASH Viewerの結果

基本的に全てのケースでUser I/Oの待機イベントで待機しているのは変わりません。しかし、特徴的な待機イベントとしてCommitクラス(内容はlog file sync)があります。none と asynchの場合にのみ発生しています。

LGRWが log bufferからREDOログへ書き込んでいるのを待機しているわけですが、

OLTP系の処理であれば、commitはそれなりに頻繁に発行される + それなりに多くのセッションが同様の処理をする。という特徴を考えると、LGWRのアクティビティは高くなります。LGWRが同期I/Oをしている場合、commit I/Oの衝突により各セッションが待機するであろうことは想像に難くありません。

それが、none の場合(同期I/O)の場合とasynch(非同期I/Oのつもり)で、ほぼ同一のレスポンス時間、同一TPMかつ、待機イベントの傾向である。また、setall(DIRECTかつ非同期I/O)の場合で、レスポンス時間およびTPMが改善され、待機イベントにlog file syncが無いこと。

上記を考えると、やはり、filesystemio_options=asynchは、非同期I/Oでない気がしますね。。。

2010/10/02

filesystemio_optionsと非同期I/O

先日、filesystemio_options=asynchの場合のopenモードが不思議と書いたのですが、一応、サンプルソースを使って試してみました。
サンプルソースはLinux Foundationにあるaiocpを拝借してテストしました。

[oracle@kshinkub aio]$ wget http://devresources.linuxfoundation.org/daniel/AIO/aiocp.c
--2010-10-02 21:45:34--  http://devresources.linuxfoundation.org/daniel/AIO/aiocp.c
devresources.linuxfoundation.org をDNSに問いあわせています... 140.211.169.81
devresources.linuxfoundation.org|140.211.169.81|:80 に接続しています... 接続しました。
HTTP による接続要求を送信しました、応答を待っています... 200 OK
長さ: 11896 (12K) [text/x-csrc]
`aiocp.c' に保存中

100%[==================================================================>] 11,896      37.3K/s 時間 0.3s

2010-10-02 18:45:35 (37.3 KB/s) - `aiocp.c' へ保存完了 [11896/11896]

[oracle@kshinkub aio]$ gcc -laio -o aiocp aiocp.c


処理内容はざっと以下の通りです。
1. コピー元ファイルのopen
2. コピー先ファイルのopen
3. 1に対してreadを非同期I/Oで要求し、その完了をコールバック関数で待ち受ける
4. 3のread要求が完了した場合、コールバック関数が呼ばれ、さらに2に対して非同期でwrite要求が発行される
5. write要求も完了時にはコールバック関数が呼ばれる
6. 全てのread/writeの要求が発行された場合、その完了を待って終了

つまり、処理の重いwriteの間に処理の軽いreadを多く発行させて、全体のスループットが上がることを期待したサンプルと言えます。以下、本当にそうなっているか確認してみます。
* 今回は、10MBのファイルを別ファイルにコピーするテストを実施しています。その際のブロックサイズはOracleのブロックサイズと合わせ8KBとしました。(別に合わす必要もないですが...)

1. 初期ファイルの作成(10MB)
[oracle@kshinkub aio]$ dd if=/dev/zero of=srcfile bs=1M count=10
10+0 records in
10+0 records out
10485760 bytes (10 MB) copied, 0.0171325 seconds, 612 MB/s

2. ファイルキャシュクリア
[ora112d@RH5-64-112-node1-p ~]$ su - root -c "echo 3 > /proc/sys/vm/drop_caches"

3. O_SYNCの場合のAIO
[ora112d@RH5-64-112-node1-p ~]$ time ./aiocp -d -b 8K -f O_SYNC -f O_CREAT srcfile dstfile
rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
(省略)
rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
real    0m1.360s
user    0m0.002s
sys     0m0.123s

4. ファイルキャシュクリア
[ora112d@RH5-64-112-node1-p ~]$ su - root -c "echo 3 > /proc/sys/vm/drop_caches"

5. O_SYNC|O_DIRECTの場合のAIO
[ora112d@RH5-64-112-node1-p ~]$ time ./aiocp -d -b 8K -f O_SYNC -f O_DIRECT -f O_CREAT srcfile dstfile
rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrwwwwwwwwwwwwwwwwwwwwwwwwwrrrrrrr
rrrrrrrrrrrrrrrwwwwwwwwwwwwwrrrrrrwwrrrrrwwwwwwwwwwwwwwwrrrrrrrr
rrrrrwwwrrrrrwwwwrrrwwwrrrrwwwwwwwrrrrwwwwwwwwwwwwwwwwwwrrrrwwww
wwwwrrrrrrrrrrrrrrwwwwrrrwwwwwwwwwwwrrrrrrrrrrrrrwwwwwwwwwwwwwww
(省略)
rrrrrrrrrrrrrrrrrrrrrrrrwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwrrrrwwww
real    0m0.403s
user    0m0.000s
sys     0m0.061s

"r"が出力(readが完了した時点)される条件は以下の通りです。

static void rd_done(io_context_t ctx, struct iocb *iocb, long res, long res2)
{
(省略)  
        /* turn read into write */
        if (no_write) {
            --tocopy;
            --busy;
            free_iocb(iocb);
        } else {
            int fd;
            if (iocb->aio_fildes == srcfd)
                fd = dstfd;
            else
                fd = dstfd2;
            io_prep_pwrite(iocb, fd, buf, iosize, offset);
            io_set_callback(iocb, wr_done);
            if (1 != (res = io_submit(ctx, 1, &iocb)))
                io_error("io_submit write", res);
        }
        if (debug)
            write(2, "r", 1);
        if (debug > 1)
            printf("%d", iosize);
(省略)
}

つまり、io_submit(2)でread要求を発行し、そのコールバック関数rd_doneの中で、write用のio_submit(2)を発行し、そのwrite要求が成功した場合に"r"を出力しています。
O_DIRECTオプションを付けずにopenした場合は、"r"が続き、"w"が同数続いています。つまり、read完了後、write要求を出しているが 同期I/Oのため、次のread要求が出せないことを示しています。(非同期I/Oになっていない)
しかし、O_DIRECTオプション付きでopenした場合は、"r"が続き、更に"r"が続き、"w"が出てきて、途中で"r"が続き...のようになっています。これは、write要求のコールバック(完了)を待たずに、続けざまにreadを要求していることを示してます。
つまり、filesystemio_options=asynchとしても、openのモードにO_DIRECTが含まれていないので実際には同期I/Oとなっていると思われます。

仮にこの擬似コードの結果が正確なら、

非同期I/Oを使うには、filesystemio_optionsにsetallが必要ということになります。また、そうなるとファイルキャッシュは効き目なしになるので、それ相応のSGAの調整が必要になるということになります。(そもそもファイルキャッシュは当てにしていないですかね。。。)

2010/09/30

Unbreakable Enterprise KernelのI/Oスケジューラ

Unbreakable Enterprise KernelにUpgradeして気づいたのですがkernelのI/Oスケジューラが変更されていますね。
Upgrade前(OEL5.5)ではないのですが、多分5.3? 5.4?の時のI/Oスケジューラは以下でした。

[oracle@kshinkub ~]# uname -a
Linux kshinkub 2.6.18-164.el5 #1 SMP Fri Sep 17 15:51:48 EDT 2009 x86_64 x86_64 x86_64 GNU/Linux

[oracle@kshinkub ~]# cat /sys/block/sda/queue/scheduler
noop anticipatory deadline [cfq]



Upgrade後は以下のようになっていました。
[oracle@kshinkub ~]$ uname -a
Linux kshinkub 2.6.32-100.0.19.el5 #1 SMP Fri Sep 17 17:51:41 EDT 2010 x86_64 x86_64 x86_64 GNU/Linux

[oracle@kshinkub ~]$ cat /sys/block/sda/queue/scheduler
noop anticipatory [deadline] cfq


確か、deadlineはDBMSなどに向くと聞いたような気がするが。ちょっと時間のある時に調べることにする

Unbreakable Enterprise Kernelのチューニングは非同期I/Oか?

小幡さん、yohei-aさん、wmo6hashさんなど、いろいろご意見がありましたが、一度、Oracleの非同期I/Oの実装はどうだったのか再確認が必要だと思いちょっと調べてみます。

手当たり次第やってもしょうがないので、以下の3つの観点で調査してみます。多分、何回かに分けて調査すると思うのですが、今回は初期化パラメータfilesystemio_options = [none|directio|asynch|setall]でどのようにI/Oに関するシステムコールが変化するか見てみます。

1. データファイルへの書き込み
    これは、DBWRが行っていますので、filesystemio_optionsの各モードでDBWRがopen|writeを
    どのように実行しているか?

2.REDOへの書き込み
    これは、LGWRが行っていますので、filesystemio_optionsの各モードでLGWRがopen|writeを
    どのように実行しているか?

3. データファイルの読み込み
    これは、shadowプロセスが行っていますので、filesystemio_optionsの各モードでshadowプロセス
    がopen|writeをどのように実行しているか?

1、2のトレースを取るには以下のような感じです。
# openのシステムコールを確認
strace -f -o /tmp/oracle.trc -e open sqlplus / as sysdba << EOF
startup
EOF

# 別ターミナルで
strace -o /tmp/oracle_dbwr.trc -p 
strace -o /tmp/oracle_lgw.trc -p 

# 別ターミナルで
sqlplus xxx/yyyy
SQL> insert into xxxx values (xxxx);
SQL> -- lgwに書き出させる
SQL> commit;
SQL> -- dbwの書き出させる
SQL> alter system flush buffer_cache;

3のトレースを取るには以下のような感じです。
strace -f -o /tmp/shadown.trc sqlplus xxx/yyy
SQL> -- direct path readとなるように
SQL> select /*+ full */ count(*) from big_table;

では、結果です。

1. DBWRの動作
   filesystemio_options=none
   オープンモード
       open("/oracle_ssd/oradata/soe_ssd.dbf", O_RDWR|O_SYNC) = 25

   書き込み
       pwrite(25, " \242\0\0\370\360\261\1\236\235\277\0\0\0\1\4\233\376\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 65536, 26812022784) = 65536

   filesystemio_options=directio
   オープンモード
       open("/oracle_ssd/oradata/soe_ssd.dbf", O_RDWR|O_SYNC|O_DIRECT) = 25


   書き込み
       pwrite(25, "\6\242\0\0\377\360\261\1\207\355\277\0\0\0\1\6\317\352\0\0\1\0\0\0\204\37\1\0{\355\277\0"..., 8192, 26812080128) = 8192

   filesystemio_options=asynch
   オープンモード
       open("/oracle_ssd/oradata/soe_ssd.dbf", O_RDWR|O_SYNC) = 25


   書き込み
       io_submit(140371209195520, 1, {{0x7faab86dfc00, 0, 1, 0, 20}}) = 1
       io_getevents(140371209195520, 1, 128, {{0x7faab86dfc00, 0x7faab86dfc00, 8192, 0}}, {600, 0}) = 1

    filesystemio_options=setall
   オープンモード
       open("/oracle_ssd/oradata/soe_ssd.dbf", O_RDWR|O_SYNC|O_DIRECT) = 25


   書き込み
       io_submit(139687490887680, 58, {{0x7f0b87a667c0, 0, 1, 0, 20}, ... {0x7f0b87a5caf8, 0, 1, 0, 25}}) = 58
       io_getevents(139687490887680, 4, 128, {{0x7f0b87a5caf8, 0x7f0b87a5caf8, 8192, 0}, ... {0x7f0b87a63718, 0x7f0b87a63718, 8192, 0}}, {600, 0}) = 4

2. LGWRの動作
   filesystemio_options=none
   オープンモード
       open("/oracle_local/oradata/redo06.log", O_RDWR|O_SYNC) = 22


   書き込み
       pwrite(22, "\1\"\0\0\246W\2\0K\0\0\0\20\2007\250\220\1\0\0\r\0\0\0\312\235\277\0\1\0\0\0"..., 512, 78597120) = 512

   filesystemio_options=directio
   オープンモード
       open("/oracle_local/oradata/redo06.log", O_RDWR|O_SYNC|O_DIRECT) = 22


   書き込み
       pwrite(22, "\1\"\0\0\312\0\0\0L\0\0\0\20\200Es\360\1\0\0\r\0\0\0\252\355\277\0\1\0\0\0"..., 512, 103424) = 512

   filesystemio_options=asynch
   オープンモード
       open("/oracle_local/oradata/redo06.log", O_RDWR|O_SYNC) = 22


   書き込み
       io_submit(140134974480384, 1, {{0x7f73b5153c50, 0, 1, 0, 21}}) = 1
       io_getevents(140134974480384, 1, 128, {{0x7f73b5153c50, 0x7f73b5153c50, 512, 0}}, {600, 0}) = 1

   filesystemio_options=setall
   オープンモード
       open("/oracle_local/oradata/redo06.log", O_RDWR|O_SYNC|O_DIRECT) = 22


   書き込み
       io_submit(139658147393536, 1, {{0x7f04aff94c50, 0, 1, 0, 22}}) = 1
       io_getevents(139658147393536, 1, 128, {{0x7f04aff94c50, 0x7f04aff94c50, 512, 0}}, {600, 0}) = 1

3. shadowプロセスの動作
   filesystemio_options=none
   オープンモード
       open("/oracle_ssd/oradata/soe_ssd.dbf", O_RDWR|O_SYNC) = 12

   読み込み
       pread(12, "#\242\0\0\203\36\240\1\353\37\276\0\0\0\1\4\n\257\0\0\0\0\0\0\0\0\0\0\353\331\240\1"..., 8192, 17243856896) = 8192

   filesystemio_options=directio
   オープンモード
       open("/oracle_ssd/oradata/soe_ssd.dbf", O_RDWR|O_SYNC|O_DIRECT) = 12


   読み込み
       pread(12, "#\242\0\0\203\36\240\1\353\37\276\0\0\0\1\4\n\257\0\0\0\0\0\0\0\0\0\0\353\331\240\1"..., 8192, 17243856896) = 8192

   filesystemio_options=asynch
   オープンモード
       open("/oracle_ssd/oradata/soe_ssd.dbf", O_RDWR|O_SYNC) = 12


   読み込み
       pread(12, "#\242\0\0\203\36\240\1\353\37\276\0\0\0\1\4\n\257\0\0\0\0\0\0\0\0\0\0\353\331\240\1"..., 8192, 17243856896) = 8192

    filesystemio_options=setall
   オープンモード
       open("/oracle_ssd/oradata/soe_ssd.dbf", O_RDWR|O_SYNC|O_DIRECT) = 12


   読み込み
       pread(12, "#\242\0\0\203\36\240\1\353\37\276\0\0\0\1\4\n\257\0\0\0\0\0\0\0\0\0\0\353\331\240\1"..., 8192, 17243856896) = 8192

ちょっと見づらいかもしれませんが。。。ただ、基本的に想定通りの動きです。

ただ1点、不思議な点があります。
私の認識ではio_submit(2)はO_DIRECTをつけてopenしないと同期I/Oとなると思っていたのですが、上記結果からfilesystemio_options=asynchの場合は、O_DIRECTフラグが付いていません。それにも関わらずio_submit(2)で非同期I/Oを行う(こと意図していると思う)動作になっています。

本当に非同期I/Oになっているのだろうか?

2010/09/26

続 Smart Flash Cacheの謎

先日、Smart Flash Cacheを実行した際に2回目が必ず遅いと書きましたが、あれから、ちょっと調べてみました。原因は単純なのですが。一応、ご報告です。

先日のテストを再度実行し、その時のv$sysstatのflash cache 周りの統計情報を見てみます。

flash cache insert skip: DBWR overloaded physical read flash cache hits flash cache inserts flash cache insert skip: exists
1回目 147593 2 206803 398
2回目 0 207038 147880 206680
3回目 0 354529 90 354406


正直、上記のイベントに関してキチンとしたドキュメントが存在しないので、想像の域を超えませんが、前回、x$bhから、対象セグメントのサイズの全てが、flash cache上に乗っていないと言いました。その原因が示されているようです。

それは、flash cache insert skip: DBWR overloadedだったから。というわけでしょう。DBWRが忙しすぎると、flash cacheに乗せる(というよりもきっとflash cache上に存在するのか?flash cache上に空きがあるのか?といった管理)作業をスキップするようです。結果として、flash cache上に全データが乗っていなかったと(ある意味)納得がいきました。

もう少し、見てみます。
1回目は、基本的にデータファイルから、buffer cache(このテストでは小さくしてあるので、ほぼ、flash cache)に乗せる作業となります。なので、flash cacheのデータにヒット(physical read flash cache hits)することは無く、また、flash cacheにはデータが存在しないので、既存データとしてスキップする(flash cache insert skip: exists)ことなく、全て、flash cacheにデータを乗せています(flash cache inserts)。ただし、DBWRが忙しかったので、一部のデータはflash cacheの乗せることをスキップしています(flash cache insert skip: DBWR overloaded)

2回目となると様相が変わります。
それは、かいつまんで言うと、physical read flash cache hitsと、flash cache insert skip: existsが増加し、さらに、flash cache insertsもソコソコ存在するということです。これは、簡単にいうとflash cache上でread / writeが、相当競合したであろうことを示しています。

3回目になると。
言わずもがな、ですが、flash cache insertsが激減(全部、flash cacheに乗ってしまった)し、ほぼreadのみの状態となりました。

ここまでで、お分かりのように、Smart Flash Cacheにおける、ready boost的なフラッシュキャッシュは、buffer cacheと同様に扱われます。が、SSDは物理メモリではありません。なので、物理メモリよりは随分遅いのです(普通のディスクよりはかなり高速なのですが)なので、物理メモリの補助として使用することを忘れると大変なことになります。
このテストの場合、buffer cacheを小さく設定しているので、ほとんどをflash cacheに頼っています。flash cacheでは自分のI/O性能を超えるもの要求されて、ニッチもサッチも行かない状況なのがうかがえます。特にこれは2回目のread / writeの競合で顕著にみえたということでしょう。

一応、その際のiostatの状況をまとめてみます。

Chart build on http://charts.hohli.com

Chart build on http://charts.hohli.com

Chart build on http://charts.hohli.com

2010/09/25

Smart Flash Cacheの謎

そろそろ、Smart Flash Cacheのテストに戻ります。

基本的に、Smart Flash CacheはOLTP系で使うものだ。と先日書きました。しかし、テストでOLTP系のSQL(要はindex scan)を実行したら、なかなかbuffer cacheも汚れませんし、当然、flash cacheもキレイなままです。なので、通常はdirect path readを使ってしまうfull scanをscattered readに変える裏技を使って、一気に、buffer cacheを使いまくってみます。

そうすると、一つ、不思議な現象が起こっています。

"2回目のアクセスが、遅い"

ORDER_ITEMS表をfull scan(db file scattered read)させた際の経過時間を示してみます。

Chart build on http://charts.hohli.com

当然、1回目のアクセスはディスクから物理読み込みが発生しているので、遅いのは当たり前です。しかし、2回目のアクセスはbuffer cacheもしくは、flash cacheに乗っているはずなので、速い。ということを期待していたのですが。。。1回目のアクセスよりも遅くなってしまっています。

ちなみに3回目以降は、想定通りのスピードです。

何故なのだ!?

理由を考える前に、そもそも、flash cacheにちゃんとデータが乗っているのか?を見てみる。

ORDER_ITEMSのセグメントサイズは3840MBでした。これが、全部buffer cache(+flash cache)に乗っているのかを確認してみます。

以下のSQLでx$bhからサイズを確認します。

/* block sizeは8KB */
select  decode(state, 0, 'free', 1, 'xcur', 2, 'scur', 3, 'cr', 4, 'read', 5,
  'mrec', 6, 'irec', 7, 'write', 8, 'pi', 9, 'memory', 10, 'mwrite', 11,
  'donated', 12, 'protected', 13, 'securefile', 14, 'siop', 15,
  'recckpt', 16, 'flashfree', 17, 'flashcur', 18, 'flashna',state) status
,count(*)*8 / 1024 MB 
from x$bh
group by state

Chart build on http://charts.hohli.com

1: 起動直後
2: 1回目のアクセス
3: 2回目のアクセス
4: 3回目のアクセス
5: 4回目のアクセス

注目は、1回目のアクセスです。
セグメントサイズが3840MBにも関わらず、flashcur(これが、flash cahceにカレントモードで乗っているステータス)のブロックは2500MBにも満たない状況です。つまり、全部乗り切らなかった。2回、3回とアクセスすると、やっと、全ブロックがflash cacheに乗ったことが分かります。 

flash cacheに乗り切らなかった原因も謎なのですが、なぜ、ディスクからの物理読み込みが発生している1回目のアクセスより、2回目のアクセスが遅くなっているのか?

今後、もう少しちゃんと見てみる必要がありそうです。

2010/09/23

続 Unbreakable Enterprise Kernel

昨日に続き、もう少しUnbreakable Enterprise Kernelをテストしてみます。kernelのupdateでちょっとビックリしたのは以下の問題です。
kernel アップデート後、再起動を行うと、起動はしているようだがコンソールが表示されない(udevサービス起動で固まっている)

回避策) 
/etc/grub.confの起動オプションにnomodesetを追加する。 
kernel /vmlinuz-2.6.32-100.0.19.el5 ro root=LABEL=/ rhgb quiet nomodeset

で、昨日のSSDでのOLTPベンチマークをUnbreakable Enterprise Kernelで実行してみます。

結果は以下。

Redhat Compatible Kernel)
平均のTPMで17822、最高のTPMで20062、レスポンスタイムは12ms

Unbreakable Enterprise Kernel)
平均のTPMで20411、最高のTPMで22966、レスポンスタイムは8ms

一応、ASH Viewerの結果)


TPMはDBのリソース(AAS)が余っているので、参考程度なのですが、レスポンスタイムが12(ms) -> 8(ms)と約33.3%のパフォーマンスアップとなっています。

レスポンスタイムは(当然ですが)I/Oレイテンシーだけで決定するわけではないので、33.3%のパフォーマンスアップという結果は、Oracleが言っている75%のI/Oパフォーマンスアップという謳い文句が大きくハズレていないことを示しているようです。

* ただし、これは再検証をしっかり行ったものではありません。参考程度にしてください...

参考ついでに、HDDも同様にベンチマーク結果を示しておきます。

ベンチマーク結果)

ASH Viewerの結果)


Redhat Compatible Kernel)
平均のTPMが4692で、最高のTPMでも6696、レスポンスタイムは159

Unbreakable Enterprise Kernel)
平均のTPMが5363で、最高のTPMでも6843、レスポンスタイムは138

これは、もしかすると誤差の範囲かもしれませんが、平均TPM、レスポンスタイムで、13%~14%程度のパフォーマンスアップとなっていました。


2010/09/22

Unbreakable Enterprise Kernel

海の向こうでOOWが賑やかに開催されていますが、Oracle LinuxでSSDを使った検証している自分にちょっとタイムリーな情報が入ってきました。Oracleが、良くも悪くもOracleデータベースに向けてカリカリにチューニングしたLinux Kernelを発表しました。それがUnbreakable Enterprise Kernelです。

Delivers Better than 75 Percent Performance Gain Over Red Hat Enterprise Linux 5 Compatible Kernel

これは、ちょっと聞き捨てなりませんよね。詳細は、Oracleの発表資料を見て欲しいのですが、SSDに対するチューニングも施されているようです。

このkernelは、Oracleのサポート契約無しで、public yum リポジトリからインストールできます。詳細は、こちらで詳しく説明されています。

時間のある時に、きちんと検証してみたいと思います。

* カーネルを一気に現在のメイン ラインに近いところまで持ってくる思い切りの良さは、個人的には嫌いではないです。


SSDでのOLTP性能

前回、HDDを使ったOLTPとHDD + SSD(Smart Flash Cache)のパフォーマンス比較を行ないましたが、せっかくなので、SSDにデータを配置した場合のパフォーマンスも計測してみます。

結果は、当然ながら最速となりました。

ベンチマークの結果)
平均のTPMで17822、最高のTPMで20062を記録しました。


ASH Viewerで確認)
まず目を引くのはAASの値の低さです。まだまだ、余裕です。今回は、前回と条件を合わすため、15セッションでベンチマークを行っていますが、全く余裕でした。


一応、前回同様にUser I/Oの待機イベントの出しておきます。


ここまでのパフォーマンス結果をまとめてみます。ただ、SSDを使用した場合はパフォーマンス限界まで達していないので、ベンチマークでの平均レスポンスタイムを示しておきます。


2010/09/21

Smart Flash Cache の使い道

Smart Flash Cache自体、現在のBuffer Cacheの代替キャッシュ(L2 キャッシュ)という位置づけでしかないが、使い道はそれなりに多い。メインメモリ(DDR3)4GBが10000円前後であるのに比べ、SSD 64GBは15000円前後です。SSDは、Buffer Cacheの不足を補うには、もってこいのデバイスと言えるでしょう。

ただし、これは、"あくまでもOLTP系の処理"に限定したものです。OLTP系の処理では、インデックスを使用したアクセスが前提です。またインデックスアクセスは、基本的にバッファ上に存在する(してほしい)事も前提です。DWHのようなシステムでは、バッファ上に乗り切らないので、バッファを迂回したdirect path readを使用します。(が、これもin memory parallel Queryの登場で変わってくるかも知れません)

あと、OLTP系のシステムで、SQL文がそれなりにチューニングされている場合、全体のスループットを上げる方法として、メモリを大容量にする。か、ちょろちょろ発生するディスクアクセスを超高速にする。しかありません。実際、現在のディスクで最速なものはSSDとなるわけですが、SSDへデータを移動するには通常のデータ移行にみられるような、システムの停止を含む業務への影響や、データ移行に伴う様々のリスクが存在します。

Smart Flash Cacheは、WindowsのReady Boostなみに簡単にパフォーマンスアップが望めます。それもリスクなしで。SSD 64GB程度を15000円程度で購入し、デバイスごとOracleに認識させるだけです。本当にそれだけです。(といっても、街の電気屋さんで買って、すぐに商用環境にくっつけるには勇気がいるでしょうが...)

では、再度、Smart Flash Cacheのパフォーマンス比較をしてみましょう。

環境)
memory_target = 4.5GB
swingbenchにてTPC-Cベースの負荷を掛けます(swingbench用スキーマは約30GB程度です)
セッション数は15

1) 普通にベンチマークを実施

ベンチマークの結果)
平均のTPMが4692で、最高のTPMでも6696に留まっています

ASH Viewerの結果)
15セッションのほとんどがUser I/Oクラスの待機イベントで待たされていることが分かります。


またUser I/Oクラスの内訳はdb file sequential read(index scan)であることが分かります。


以前、「まず初めに」で述べたようにswingbenchはかなりチューニングされているベンチマークソフトですが、index scanでかなりの待機が発生しています。この答えを先にいってしまうと、TPC-Cはorder - entryのシステムをシミュレーションしているので、多くの「発注」と「確認」が発生します。「発注」は主にinsertとなるのですが、重複発注を防ぐようにUniqueチェックが入ります。ここで、index scanが多く発生し、結果、待たされる。ことになります。
これは、小幡さんのブログにもありましたね。


2) Smart Flash Cacheを使用してベンチマーク実施

- Smart Flash Cacheを50GB分確保
alter system set db_flash_cache_size=50G scope=both; 

- いくつか大事なオブジェクトをFlash Cache上にキープさせる(前に50GBで大丈夫か確認)
select segment_name, sum(bytes)/1024/1024 segment_size_MB
from user_segments
where  segment_name in ('CUSTOMERS', 'CUSTOMERS_PK', 'ORDER_ITEMS'
                                    ,'ORDER_ITEMS_PK', 'ORDERS', 'ORDER_PK')
group by segment_name;

SEGMENT_NAME                    SEGMENT_SIZE_MB
------------------------------ ----------------
CUSTOMERS                                  3072
ORDER_ITEMS                                3776
ORDER_PK                                    790
CUSTOMERS_PK                                703
ORDERS                                     2752
ORDER_ITEMS_PK                             2808

- いくつか大事なオブジェクトをFlash Cache上にキープさせる
/* これは、Flash Cacheに事前に乗せる際にdirect path readでバッファーを迂回しないおまじない */
alter session set events '10949 trace name context forever ,level 1';
alter session set "_very_large_object_threshold"=1000000;

/* 以下は大事なオブジェクトをFlash Cache上に乗せる */
alter table customers storage(flash_cache keep);
select /*+ no_parallel index(c customers_pk) */ count(*) 
from customers c where customer_id>=0;
alter index customers_pk storage(flash_cache keep);
select /*+ no_parallel no_index(c) */ count(*) 
from customers c;

alter table order_items storage(flash_cache keep);
select /*+ no_parallel no_index(c) */ count(*) 
from order_items c;
alter index order_items_pk storage(flash_cache keep);
select /*+ no_parallel index(c order_items_pk) */ count(*) 
from order_items c where line_item_id >=0 and order_id>=0;

alter table orders storage(flash_cache keep);
select /*+ no_parallel no_index(c) */ count(*) 
from orders c;
alter index order_pk storage(flash_cache keep);
select /*+ no_parallel index(c order_pk) */ count(*) 
from orders c where order_id >= 0;

ベンチマークの結果)
平均のTPMが9726で、最高のTPMで15045と、平均のTPMで約2倍のパフォーマンスアップとなっています。

ASH Viewerの結果)
User I/Oの待機が減って、その分、多少CPUを使えるようになりました。

User I/Oの待機は以前、db file sequential readですが、全体的に減少しています。

ただ、もう少し、チューニングの余地はありそうです。

2010/09/19

Smart Flash Cache 簡単なパフォーマンス比較

SSDも新しくなったことで、快調に検証が進みそうです。
まず、Smart Flash Cache単体のパフォーマンスを比較するため、以下の3パターンのパフォーマンスを測定してみます。

1) 大きなテーブルをdirect path readで検索する
2) 大きなテーブルをdb file scattered readで検索する
3) 大きなテーブルをdb flash cache multiblock physical readで検索する

3)は見慣れないイベントですが、基本的には、全て同じテーブル(大きなテーブル)に対して

1) バッファーキャッシュを迂回してディスクを直接読み込む
2) バッファーキャッシュを経由してディスクを間接的に読み込む
3) バッファーキャッシュ+フラッシュキャッシュを経由して、ディスクを間接的に読み込む

ただし、今回のテストは、Smart Flash Cacheのパフォーマンスチェックなので、以下の条件を付けました。

* ご存知の方も多いと思いますが、11gR2から、通常のフルスキャン
   (今ままでのdb file scattered read)は、ある条件によりdirect path readに置き換えられます。
    これは、バッファーの再利用性が低いSQLだと(ある意味Oracleが勝手に)判断できる場合は、
    バッファー経由のI/Oを諦めてディスクと直接I/Oすることを意味します
* 今回のテストでは、db file scattered read と direct path readを使い分けたかったので、
   非公開のパラメータ"_very_large_object_threshold"を変えています。
* "_very_large_object_threshold"の単位は多分MBだと思いますが、このサイズを超える場合、
   大きいオブジェクトだと判断してdirect path readを選択するようです。なので、この値を非常に
   大きくすると、今ままで通り、db file scattered readを選択する可能性が高いということです。

1) テーブルのサイズは、必ずbuffer cacheのサイズを超える
2) テーブルのサイズは、flash cacheのサイズを超えない

つまり、buffer cacheを超える読み込みが発生するので、2)はパフォーマンス的に不利になるはずで、1)が多少有利になるはず。さらにSmart Flash Cacheの実力が本物なら3)のパフォーマンスが最高になるはずです。
まずは、結果を示します。



Smart Flash Cacheを使用したパフォーマンスが最高となっています。

では、いつものようにASH Viewerでその時の様子を見てみます。
* 1)のテストが図中のAに該当
* 2)のテストが図中のBに該当
* 3)のテストが図中のDに該当




その時の待機イベントも見てみましょう。(上記グラフの青で示されているUser I/Oを見てみます)

一応、思ったとおりの結果となっているようです。パターンAでは、待機クラスUser I/Oのdirect path readで待機しています。またパターンBでは、db file scattered read、パターンDでは、db flash cache multiblock physical readで待機しています。
以前の検証にてDiskとSSDの基本性能の違いをチェックしましたが、1MBのsequential readの性能で約3倍のパフォーマンスが違います。今回は(まだ、きちんと検証できていませんが)multiblock read count分のsequential readだとして16 * 8K = 128Kくらいのsequential readじゃないかと推察できます。1MBほど大きなサイズではないことが言いたいのですが、それにしてもDiskと比較して3倍のパフォーマンスアップにはなっていません。SSDをL2キャッシュとして使うSmart Flash Cacheはそれなりのオーバーヘッドがあるのかも知れません。

Flash Cacheのデバイスが壊れたら

先日、Smart Flash Cacheのテストを行うようテスト環境をセットアップしたのですが、その前からテストで使用するSSDの調子が悪いことも書いていました。とりあえず、そのままの状態で、Smart Flash Cacheをテストしてみます。まず、テストを始める前に、Smart Flash Cacheが有効になっているか、軽く確認してみることにします。

簡単なSQLを実行して、パフォーマンスを比較、(そもそもエラーにならないか確認)してみます。

set timing on

select * from soe.order_items;
細かいテストは次回以降で見ていくとして、ざっくりエラーは発生しません。しかし、パフォーマンスが尋常ではないくらい悪い。何かあるはずだと、アラートログを確認してみます。

ORA-27072: File I/O error
Linux-x86_64 Error: 5: Input/output error
Additional information: 4
Additional information: 1287652
Additional information: -1

flash cacheへのアクセスに失敗したので、Smart Flash Cache機能を無効にして、通常のBuffer Cacheのみの動作をしているようです。なので、かなり、パフォーマンスが悪化していたのだと思います。

このままでは、どうにもならないので、新たにSSDを調達しました。

今回、調達したSSDはCFDのCSSD-S6M64NMQにしました。
前回、使用していたreal SSDと同じものですが、128GBを64GBにして構成しています。

とりあえず、これで、テストに支障はないようです。次からしっかりテストします。

2010/09/15

OTNのチューニングセミナー資料

1年くらい前に、オラクルOTNでセミナーを行う機会があり、そのセミナーの内容がOTNに公開されています。その時に使用した資料をブログにも公開しておきたいと思います。
(1年前の古い資料をいまさら公開するな。と言われそうですが。。。)

確か、当時は、いろいろなセミナー(Oracleだけではなく)で話しをさせてもらう機会も多く、久しぶりのOracleに関するセミナーだったので話していて(個人的には)楽しかった気がします。チューニングの話しになると、とかく細かい話しになりがち(しがち)ですが、そうではない概念的な話しも何かのお役に立つかも知れません。

どうもIEだと上手くPDFが見えないことがあるみたいです。firefoxならちゃんと見えています。(どうなっているの??)

2010/09/14

Smart Flash Cache

小幡さんよりSSDを使ったSmart Flash Cacheのテストについてコメントをもらったので、テストをしてみる予定です。

OEL(Oracle Enterprise Linux)でSmart Flash Cacheを使用できるようにするには、ひとつパッチが必要になります。

Patch 8974084
META BUG FOR FLASH CACHE 11.2PL BUGS TO BACKPORT TO 11.2.0.1 OEL

では軽く手順を示しておきます。

1. 上記のOracleの個別パッチをopatchで適用します
2. 初期化パラメータ db_flash_cache_fileを設定します
3. 初期化パラメータ db_flash_cache_sizeを設定します
4. DBの再起動

ここで、ひとつ注意が必要です。通常、SSDを使用する場合、/dev/sd*のようなデバイスをdb_flash_cache_fileに設定すると思いますが、このようなデバイスファイルには、ORACLEインストールユーザーへ直接、読み書き権限がついていません。

今回、私の検証環境では、SSDは/dev/sdcでした。

$ ls -l /dev/sdc
brw-r----- 1 root disk 8, 32 9月 14 01:58 /dev/sdc

この状態で、Oracleを起動すると

Errors in file /home/oracle/app/oracle/diag/rdbms/ora1120/ora1120/trace/ora1120_dbw0_23097.trc:
ORA-27041: unable to open file
Linux-x86_64 Error: 13: Permission denied
Additional information: 1
DBW0 (ospid: 23097): terminating the instance due to error 27041
Instance terminated by DBW0, pid = 23097

のように、Permission deniedでDBWRが停止して、インスタンスも停止します。
そこで、/dev/sdcに権限をつけてあげるわけですが、これではOSの再起動とともに権限が落ちます。

ということで、適切に/etc/udev/rules.d/*.rulesを設定します。

今回、私は検証環境ということもあり
/etc/udev/rules.d/99-flash-cache.rulesを作成し

KERNEL=="sdc", MODE="0777"

の設定をしておきました。

これで、めでたくOELでSmart Flash Cacheのテストが可能になりました。

SQL> show parameter db_flash_cache
NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
db_flash_cache_file                  string      /dev/sdc
db_flash_cache_size                  big integer 100G

これから、しばらくSmart Flash Cacheについてテストしていきます。

2010/09/12

Linuxのファイルシステムで最適なものは?

Linuxでは多くのファイルシステムがあり、現在も様々なものが開発中です。

メジャーなものとしてext2、ext3がありますが、現在ext4が主流になろうとしています。また、oracleが開発したBtrfs(B-tree file system)などのような、SSDへの最適化やCOW(Copy on Write)など次世代のファイルシステムが沢山あります。

そこで、あまり最新のものは今後やるとして、昔からあるファイルシステムと今のファイルシステムでどの程度パフォーマンスに差があるのか検証してみます。

* 本当はBtrfsも検証したかったのですが、SSDが不調なのとDIRECT I/Oが未サポートだったので断念しました
Linux 2.6.35 released, with Btrfs Direct I/O support and -ENOSPC handling of volume management operations, completing the -ENOSPC support.

ext2からext3がLinuxの主流になったとき、ext3のジャーナリング機能を嫌い、Oracle構築でxfsを使う方も多かったのではないでしょうか?何せ、xfsはメタデータのみジャーナリングを行い、実データに関しては基本知らんぷりという仕様だからです。そんな中途半端?なジャーナリングを備えたファイルシステムがなぜ、好まれるのか?

答えは簡単です。Oracleにとって実データのジャーナルは必要ないからです。OracleはデータベースのファイルをO_SYNCオプションを付けて、オープンします。それは、OSのファイルキャッシュが危ないことを知っているからです。Oracleが「書いた!」と思っても、実はファイルキャッシュにしか書かれておらず、実際のファイルには「書かれていない!」こともあり得るわけです。これを確実に「書いた!」もしくは「失敗した!」とするため(白黒はっきりさせるため)にO_SYNCオプションを付けてオープンしているわけです。

とりあえず、上記のように、ファイルシステムは沢山あって、ジャーナリングなど高機能を提供するものも多い。ただ、そのような高機能が不必要な場合もあるわけで、システムによって、特性を見極めてファイルシステムを選択する必要もあるかも知れない。というわけで、今回はファイルシステムの性能を測定してみました。

1. ext4(writeback)
2. ext4(ordered)
3. ext3(writeback)
4. ext3(ordered)
5. xfs
6. ext2

6パターンのパフォーマンスを比較してみます。前回と同じ測定方法です。

結果は、xfsが大健闘、甲乙つけがたい感じで、ext4のパフォーマンスが素晴らしい。逆にext3は、かなり苦戦を強いられている。xfsやext4はエクステントでブロックを確保するが、これが、読み込み時に有利に働くと思われます。

ただ、これを書いていて思ったのですが、今時みんなASMですかね。。。












SSD自体が不安定

先日、SSDで非同期I/Oって大丈夫か?と書きましたが、非同期I/Oが問題なのでなく、ディスク自体が問題になっているようでした。

最初は、ファイルシステムがオカシイのか?(実は、OEL(kernel:2.6.18)では、experimentalレベルのext4を使っていたので)と思ったのですが、そんなレベルではないようでした。

smartctlで、SSDの状態を見てみるとエラーを示す値が大きくなっていました。これでは、かなり不安定になるのもうなづけます。。。(実際には、OSをインストールしていたディスクも非常に不安定になっていました。こちらは、ディスクを買い替えました)



ということで、SSDを使った検証は後回しにして、HDDベースで、検証を進めることにします。が、最初にファイルシステムを疑ったついでに、少しファイルシステムについても検証してみることにします。

2010/09/06

SSDで非同期I/Oって大丈夫か?

TPC-Cベースのベンチマークを以下の条件の下、4回実施した

クライアントPCから100セッションでswingbenchを実行

A: HDDにベンチマーク用データを格納し、非同期I/Oをオフ(Oracleのデフォルト値)
B: SSDにベンチマーク用データを格納し、非同期I/Oをオフ(Oracleのデフォルト値)
C: HDDにベンチマーク用データを格納し、非同期I/Oをオン
D: SSDにベンチマーク用データを格納し、非同期I/Oをオン

その際の、swingbenchの結果を示してみる

HDDを使用したベンチマークでは、TPMの結果が非常に不安定であることが一目瞭然(A、C)。SSDを使用したベンチマークでは、それなりに安定しているものの(B、D)、非同期I/Oをオンにしたベンチマークが群を抜いてスコアが高く、安定性が高いことがわかる(D)。
 この、秘密は何なのか?

このベンチマークの際に取得しておいたASH viewerの結果も合わせて見てみようと思う。





まず、非同期I/Oを使用しないベンチマーク(A、B)では、断続的にcommit(詳細はlog file sync)の待機が発生している。また、同様にconfigration(詳細はfree buffer waitsとlog file switch(checkpoint incomplete))で待機している。log file syncはREDOへの負荷が高まっている。つまり、REDOへ書き出しているLGWRがスムーズに書き込めていないことを示してる。一般的にはREDOログが配置されているディスクが遅い場合や、頻繁なコミットによるshort I/Oが引き金になることが多い。

また、free buffer waitsは、必要なブロックをBuffer Cache上から検索するが、存在しない場合、ディスクから読み込んでBuffer Cache上に乗せる必要がでてくる。その際、Buffer上がダーティブロックで一杯だった場合、DBWRに一回、ディスクに書き込んでBuffer Cacheをキレイにしてくれ。とお願いする必要が出てくる。それを待っているのがfree buffer waitsで、log file switch(checkpoint incomplete)は読んで字の如しだが、REDOログをスイッチしようとしたが、次のREDOログのチェックポイントが完了していないので待機しているということ。両者に共通することは、free buffer waitsもlog file switch(checkpoint incomplete)もDBWRがスムーズなI/Oができずに、詰まっているために引き起こされている現象だということ。

ちょっと整理してみると、

1. どうも、LGWRはスムーズにI/Oが行えていない(log file syncで待機中)
2. どうも、DBWRもスムーズにI/Oが行えていない(free buffer waitsとlog file switch(checkpoint incomplete)で待機中)

ということで、書き込み要求をスムーズに処理させる(可能性のある)filesystemへの非同期I/Oを有効にしてみることにする。(Oracleのデフォルトでは使用されない)

SQL> alter system set filesytemio_options=asynch scope=spfile;

結果は、一目瞭然(C、D)、commit(log file sync)は無くなった。これにより、TPCベンチマークのスコアも高くなっている。




しかし、HDDを使用したベンチマークでは、LGWRがスムーズにI/Oできるようになった分、自分自身のI/O要求が高まり(User I/O(db file sequential read))更に、HDDの限界値を超えるI/Oは当然できないので、log switchの発生と共に、DBWRのI/Oが飽和状態になり、TPCのベンチマークも下降線を示している。

一方、SSDを使用したベンチマーク(D)では、I/Oに余裕があるため、LGWRとDBWRのI/Oスループット向上が、そのままTPCのスコア向上に寄与している。

しかし、このベンチマークを走らせるようにしてから、OS kernelがI/O関連のエラーを出力するようになっている。原因はSSDへの非同期I/Oのようだが。。。更には、filesystem全体が破壊されてしまっている。。。

確かにパフォーマンス向上が驚くほどだが、二度と起動しないようでは。。。

ということで、もう少し詳細に調べることにする。