Quantcast
Channel: 技術情報 – Insight Technology, Inc.
Viewing all 48 articles
Browse latest View live

Full Scanを速くしちゃう~Oracle Text編~その3

$
0
0

<Full Scanを速くしちゃう~Oracle Text編~その3>
ペンネーム: グリーンぺぺ

全文検索エンジンであるOracle Textは応用的な検索も可能である。
今回は曖昧検索とシソーラス検索を紹介する。

■環境
Oracle 11.2.0.2 64bit on RHEL5.4

■その1:曖昧検索とは?
fuzzy演算子を使用すると、問合せ入力がかな(カナ)である場合に
曖昧検索ができる。たとえば「バイオリン」と「ヴァイオリン」や
「四谷」と「四ッ谷」を検索する場合に類似検索が可能となる。
早速試してみよう。
[sql]
–lyric列に格納されている語句どおりで検索
SQL> select TITLE,ARTIST from ranking where contains(lyric,’デジャヴをリプレイ’)>0;

TITLE ARTIST
——————– ——————–
おやすみパラドックス やくしまるえつこ

※普通に検索ができます。

–lyric列に格納されている語句の類似語句で検索
SQL> select TITLE,ARTIST from ranking where contains(lyric,’デヂャブをリプレイ’)>0;

レコードが選択されませんでした。

–fuzzy演算子を使用して類似検索
SQL> select TITLE,ARTIST from ranking where contains(lyric,’fuzzy(デヂャブをリプレイ,,,n)’)>0;

TITLE ARTIST
——————– ——————–
おやすみパラドックス やくしまるえつこ
[/sql]
文法など使い方は↓を参照頂きたい。
http://otndnld.oracle.co.jp/document/products/oracle10g/102/doc_cd/text.102/B19214-01/cqoper.htm#i997330

■その2:シソーラス検索とは?
synonym演算子を使用するとシソーラス(類語)検索が可能となる。
シソーラス辞書をユーザで作成し、指定すると検索可能となる。

–任意に作成したシソーラス辞書”my_thes.txt”を登録[bash]
$ cat my_thes.txt
本当
SYN 実際
SYN ホント

$ ctxload -user scott/tiger -thes -name my_thes -file my_thes.txt
Connecting…
Creating thesaurus my_thes…
Thesaurus my_thes created…
Processing…
3 lines processed successfully
Beginning insert…3 lines inserted successfully
Disconnected[/bash]
[sql]
–シソーラス辞書を利用して検索
SQL> select TITLE,ARTIST from ranking where contains(lyric,’syn(実際,my_thes)’)>0;

TITLE ARTIST
————————- ——————–
VOICE Perfume
Re:birth Acid Black Cherry
本当は怖い愛とロマンス 桑田佳祐[/sql]

シソーラス検索はユーザ登録した任意の類語にまで検索範囲を広げて検索が可能となる。
よって上記の検索をlike演算子を利用した検索にしてもヒットしない。
[sql]
SQL> select TITLE,ARTIST from ranking where lyric like ‘%実際%';

レコードが選択されませんでした。
[/sql]
登録したシソーラス辞書はctxloadコマンドを利用してエクスポートできる
–シソーラス辞書のエクスポート[bash]
$ ctxload -user scott/tiger -thesdump -name my_thes -file my_thes.out
Connecting…
Writing thesaurus my_thes to file my_thes.out
10 lines processed successfully
Disconnected

$ cat my_thes.out
ホント
UF 本当
UF 実際
実際
UF 本当
UF ホント
本当
UF 実際
UF ホント
[/bash]
登録したシソーラス辞書の直積結果が格納されていることが確認できた。
シソーラス辞書を充実させていけば以下のような検索も可能になる。
□外国語の等価語句が含まれるように問合せを拡張する
–シソーラス定義の確認[bash]
cat my_thes.txt
本当
ENGLISH: truth
SYN 実際
SYN ホント
[/bash]
–任意に作成したシソーラス辞書”my_thes.txt”を登録[bash]
$ ctxload -user scott/tiger -thes -name my_thes -file my_thes.txt[/bash]

–“本当”と同意の”truth”が含まれるlyric列を検索
SQL> select TITLE,ARTIST,lyric from ranking where contains(lyric,’tr(本当,english,my_thes)’)>0;

TITLE ARTIST lyric
————————- ————————- ————————-
Do You Crash? Bonnie Pink …Tell me the truth,…
VOICE Perfume …本当のキミが…
本当は怖い愛とロマンス 桑田佳祐 …本当は怖い愛と…

–“本当”と同意の”truth”と類語の”実際”,”ホント”が含まれるlyric列を検索
SQL> select TITLE,ARTIST,lyric from ranking where contains(lyric,’trsyn(本当,english,my_thes)’)>0;

TITLE ARTIST lyric
————————- ————————- ————————-
Do You Crash? Bonnie Pink …Tell me the truth,…
モノノケダンス 電気グルーヴ …何も実際…
VOICE Perfume …本当のキミが…
Re:birth Acid Black Cherry …ホントはね…
本当は怖い愛とロマンス 桑田佳祐 …本当は怖い愛と…
[/sql]

■まとめ
Oracle Textを使うならシソーラス検索まで視野に入れた要件定義の
必要があるケースが多いのではないか。全文検索をするとなると言葉に
はゆらぎがあるので(例:”にんじん”と”人参”とか)検索アプリにて吸収
する必要がある。Oracle Textは全文検索エンジンとしてなんの問題
もなく利用できる。一度試して頂きたい。


Full Scanを速くしちゃう~Oracle Text編~その4

$
0
0

<Full Scanを速くしちゃう~Oracle Text編~その4>
ペンネーム: グリーンぺぺ

Oracle Textを利用する際に検索にヒットしない語句が出現する場合がある。
Oracle Textは日本語の語彙用語集(レキシコン)の組合せに存在するものを
索引として作成している。日本語レキシコンは以下のディレクトリに存在し
以下のコマンドで可読化することができる。

■$ORACLE_HOME/ctx/data/jalxにあるレキシコンを可読化する[bash]
$ ls -n $ORACLE_HOME/ctx/data/jalx
-rw-r–r– 1 781 781 7641661 10月 20 17:23 droldJA.dat
-rw-r–r– 1 781 781 1110128 10月 20 17:23 droliJA.dat
-rw-r–r– 1 781 781 76894 10月 20 17:23 drolkJA.dat
-rw-r–r– 1 781 781 8 9月 21 16:26 drolsJA.dat

$ ctxlc -ja -ocs JA16EUC > output.txt

$ head output.txt
あいあい
あいうえお
あいさつ
あいそ
あいだ
あいつ
あいにく
あいまい
あいまって
あうん
[/bash]

ctxlcコマンドを使用すればレキシコンに語彙追加することもできる。

■語彙追加コマンド例[bash]
$ ctxlc -ja -ics JA16EUC -i 追加する語彙ファイル[/bash]
詳細は↓を参照して頂きたい。
http://otndnld.oracle.co.jp/document/products/oracle10g/102/doc_cd/text.102/B19214-01/cexec.htm#i1006575

更にOracle Textにはユーザからどのような全文検索が実行されているのか
ログを取得するモジュールも用意されている。

■Oracle Text問合せログの取得
–ログ取得開始(ログ名:query.log)[sql]
exec ctx_output.start_query_log(‘query.log’);
–問合せ実行
select TITLE,ARTIST from scott.ranking where contains(lyric,’思いこんだら’)>0;
select TITLE,ARTIST from ranking where contains(lyric,’デジャヴをリプレイ’)>0;
select TITLE,ARTIST from ranking where contains(lyric,’デヂャブをリプレイ’)>0;
select TITLE,ARTIST from ranking where contains(lyric,’fuzzy(デヂャブをリプレイ,,,n)’)>0;
select TITLE,ARTIST from ranking where contains(lyric,’syn(実際,my_thes)’)>0;
select TITLE,ARTIST from ranking where contains(lyric,’tr(本当,english,my_thes)’)>0;
select TITLE,ARTIST from ranking where contains(lyric,’trsyn(本当,english,my_thes)’)>0;
select TITLE,ARTIST from ranking where contains(lyric,’バイバイ バイヨン’)>0;
select TITLE,ARTIST from ranking where contains(lyric,’不安が騒いで眠れなくなる’)>0;
select TITLE,ARTIST from ranking where contains(lyric,’バイ バイヨン’)>0;[/sql]
–ログ取得終了[sql]
exec ctx_output.end_query_log;[/sql]
–ログ確認
※デフォルトのログ出力先は以下ディレクトリになる[bash]
$ cat $ORACLE_HOME/ctx/log/query.log
14:35:22 10/22/10 IX_LYRIC 思いこんだら Yes
14:35:22 10/22/10 IX_LYRIC デジャヴをリプレイ Yes
14:35:22 10/22/10 IX_LYRIC デヂャブをリプレイ No
14:35:22 10/22/10 IX_LYRIC fuzzy(デヂャブをリプレイ,,,n) Yes
14:35:22 10/22/10 IX_LYRIC syn(実際,my_thes) Yes
14:35:24 10/22/10 IX_LYRIC tr(本当,english,my_thes) Yes
14:35:25 10/22/10 IX_LYRIC trsyn(本当,english,my_thes) Yes
14:35:25 10/22/10 IX_LYRIC バイバイ バイヨン Yes
14:35:25 10/22/10 IX_LYRIC 不安が騒いで眠れなくなる Yes
14:35:25 10/22/10 IX_LYRIC バイ バイヨン No [/bash]

xml形式のログファイルだと見づらいので見やすくレポートしてくれるパッケージが用意されている。

–ctxsysユーザでログイン[sql]
sqlplus ctxsys/???[/sql]
–Oracle Text問合せログの出力[sql]
set serveroutput on;
declare
the_queries ctx_report.query_table;
begin
ctx_report.query_log_summary(‘query.log’, null, the_queries,
row_num=>10, most_freq=>TRUE, has_hit=>TRUE);
dbms_output.put_line(‘The 10 most frequent queries returning hits’);
dbms_output.put_line(‘number of times query string’);
for i in 1..the_queries.count loop
dbms_output.put_line(the_queries(i).times||’ ‘||the_queries(i).query);
end loop;
end;
/

The 10 most frequent queries returning hits
number of times query string
1 デジャヴをリプレイ
1 バイバイ バイヨン
1 syn(実際,my_thes)
1 trsyn(本当,english,my_thes)
1 fuzzy(デヂャブをリプレイ,,,n)
1 tr(本当,english,my_thes)
1 不安が騒いで眠れなくなる
1 思いこんだら

PL/SQLプロシージャが正常に完了しました。
[/sql]

■まとめ
Oracle Textは新たにオプションの購入の必要はなく、使い勝手のよい全文検索
エンジンだといえる。語彙字句についてもデフォルトのままで利用しても何ら
不足に感じる場合はないのではないだろうか。仮に不足があったとしても
問合せのログ出力にまで対応しているので問合せ精度を高めるようメンテナンス
をすることも可能となっている。積極的に取り込んでいきたい機能のひとつ
であるといえる。

非同期I/Oの謎

$
0
0

<非同期I/Oの謎>
ペンネーム: ベロ

明けましておめでとうございます。

今回は、以前のメルマガの補足検証となります。(どうしてもメルマガにて公開しておき
たかったので、無理を言って公開させてもらいました)

以前、弊社メルマガ(ASMLibに関する検証 その6 *1)にてASMLibの非同期I/Oに関する検証
を行っていました。そこで、各プロセスがファイルをオープンする際のモードおよび
ファイルの読み込み、書き込みのファンクションをトレースしています。また、検証用の
簡単なCのコードも掲載していました。

*1) http://www.insight-tec.com/mailmagazine/ora3/vol421.html

一部、読者の方から「この検証結果では分かりづらい」「そもそも非同期I/Oとは?」など
ご意見を頂きましたので、振り返って、Oracleの非同期I/Oの動作について再検証を行い
ました。

検証環境は以下の通りです。

OS: Oracle Enterprise Linux 5.5 (64bit)
Oracle: Oracle 11.2.0.1 Enterprise Edition (64bit)

* 今回の検証はLinux限定の検証です

まず、OS(libaioライブラリを含む)による非同期I/Oがどのように動作しているか簡単な
コードで確認します。

今回確認用に使用したソースはLinux Foundationにあるaiocp(*2)を使用しました。

*2) http://devresources.linuxfoundation.org/daniel/AIO/aiocp.c

aiocpの処理内容はざっと以下の通りです。

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

* libaio経由のI/Oはio_submit(2)とio_getevents(2)となります

つまり、処理の重いwriteの間に処理の軽いreadを多く発行させて、全体のスループット
を上げることを期待したサンプルと言えます。以下、本当にそうなっているか確認して
みます。

1. aiocpのソースを取得してコンパイルします。
[bash]
$ wget http://devresources.linuxfoundation.org/daniel/AIO/aiocp.c
$ gcc -laio -o aiocp aiocp.c
[/bash]
2. コピー元ファイルの作成
[bash]
$ dd if=/dev/zero of=srcfile bs=1M count=10
[/bash]

* 今回は、10MBのファイルを別ファイルにコピーするテストを実施します。その際の
  ブロックサイズはOracleのブロックサイズと合わせ8KBとしました。

3. ファイルのオープンモードにO_SYNCとO_CREATを付与して実行
[bash]
$ time ./aiocp -d -b 8K -f O_SYNC -f O_CREAT srcfile dstfile
real 0m1.360s
user 0m0.002s
sys 0m0.123
[/bash]
4. ファイルのオープンモードにO_SYNC、O_DIRECTとO_CREATを付与して実行
[bash]
$ time ./aiocp -d -b 8K -f O_SYNC -f O_DIRECT -f O_CREAT srcfile dstfile
real 0m0.403s
user 0m0.000s
sys 0m0.061s
[/bash]
なぜ、2種類のテストを実施したかというと、libaioによる非同期I/OはO_DIRECTが付与
された場合のみ実行されます。なので、O_DIRECTオプションの有無でaiocpの実行速度
が異なっているわけです。

では、Oracleでは、このファイルのオープンモード(ファイルシステムに対して)とI/O
の制御はどこで行われているか?

これは、filesystemio_optionsで制御されています。(デフォルトはnoneとなっていま
す) filesystemio_optionsはnone|directio|asynch|setallから選択します。

none:     ファイルのオープンモードは変更しない(かつlibaio経由のシステムコール
          を発行しない)
directio: ファイルのオープンモードはO_DIRECTが付与される。libaio経由のシステム
          コールは発行しない
asynch:   ファイルのオープンモードはnoneと同一。libaio経由のシステムコールが
          発行される
setall:   ファイルのオープンモードはO_DIRECTが付与される。かつlibaio経由のシス
          テムコールが発行される

* 各オプションによるファイルのオープンモードやシステムコールの差異はstrace
  コマンドをdbwrプロセス等にかけてみることで確認できます。

以下のブログに詳細を記載しています。
http://kojishinkubo.blogspot.com/2010/09/unbreakable-enterprise-kernelio.html

ここで、最初にテストしたaiocpコマンドの結果を思い出してみましょう。

非同期I/Oが実行されるのはlibaio経由のシステムコール(io_submit(2)とio_getevents(2))
を使用する。かつファイルのオープンモードがO_DIRECTとなっていること。でした。

ということで、ファイルシステムを利用中で、dbwr/lgwrの書き込みスループットを上げ
たい場合には、filesystemio_optionsをsetallにしないと効果なしと言えそうです。

昨年からのもやもやが解消! 恵比寿より

Oracle 11g リファレンス・パーティションに関する検証 その1

$
0
0

<Oracle 11g リファレンス・パーティションに関する検証 その1>
ペンネーム: パンダおやじ

 今回から11gの新機能であるリファレンス(参照)パーティションについて
 検証します。

 リファレンス・パーティション(マニュアルでは参照パーティション)は、
 親子の参照制約(外部キー制約)を使用して子表に親のパーティションの
 スキームを継承することができる機能です。

 リファレンス・パーティションはどのように作成すればよいのか?
 リファレンス・パーティションを使うと性能はどうなるのか?
 必ず速くなるのか、遅くなる場合は無いのかを検証していきます。

▼ 想定する検索要件
 全国の顧客分布を確認したい。
 特定の都道府県の顧客数や売上データを確認したい。
 製品種別毎の売上を確認したい。
 製品・地域毎の売上を確認したい。

では、リファレンス・パーティションを使って下記表を作ってみましょう。
ER図はこんな感じです。

+-----------+     +-----------+
|JIS住所表(ADDRESS)  | +-< |顧客表(CUSTOMER)    |
+-----------+     +-----------+
                       +
                       |
                       ^
                  +-----------+
                  |売上表(SALES)       |
                  +-----------+
                       +
                       |
                       ^
+-----------+     +-------------+
|製品表(PRODUCT)     | +-< |売上明細表(SALES_DETAIL)|
+-----------+     +-------------+

 顧客表はJIS住所表の子表で、県コードは持っていません。
 売上表は顧客表の子表で、住所に関する情報は持っていません。
 売上明細表は、製品表と売上表の子表で、製品種別は持っていません。

<検証環境>
 OS:Red Hat Enterprise Linux Server release 5.4(64Bit)
 Oracle:Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 – 64bit

▼リファレンス・パーティションでテーブルを作成

JIS住所表は顧客表の親表です。
この表はリスト・パーティションで作成します。
[sql]
create table ADDRESS
(
ADR_CD number primary key,
KEN_CD number,
CITY_CD number,
TOWN_CD number,
POST_ID varchar2(8),
KEN_NAME varchar2(8),
CITY_NAME varchar2(256),
TOWN_NAME varchar2(256),
STR_NAME varchar2(256)
)
partition by list (KEN_CD)
(
partition HOKKAIDO values (1),
~~
partition OKINAWA values (47)
);
[/sql]
顧客表を住所表の子表として作成します。
[sql]
create table CUSTOMER
(
CUST_ID number primary key,
CUST_NAME varchar2(200),
ADR_CD number not null,
constraint FK_CUSTOMER_01
foreign key (ADR_CD)
references ADDRESS
)
partition by reference (FK_CUSTOMER_01);
[/sql]
売上表を顧客表の子表として作成します。
[sql]
create table SALES
(
SALES_ID number primary key,
CUST_ID number not null,
SALES_DATE date,
constraint FK_SALES_01
foreign key (CUST_ID)
references CUSTOMER
)
partition by reference (FK_SALES_01);
[/sql]
製品は売上明細表の親表です。
この表はリスト・パーティションで作成します。
[sql]
create table PRODUCT
(
PROD_CD number primary key,
SYUBETU_CD number,
PROD_NAME varchar2(256),
SYUBETU_NAME varchar2(256),
PROD_AMT number
)
partition by list (SYUBETU_CD)
(
partition P100 values (100),
partition P101 values (101),
partition P102 values (102),
partition P103 values (103),
partition P104 values (104),
partition P105 values (105),
partition P106 values (106),
partition P107 values (107),
partition P108 values (108),
partition P109 values (109)
);
[/sql]
売上明細表を製品表の子表として作成します。
[sql]
create table SALES_DETAIL
(
SALES_ID number not null,
DETAIL_ID number not null,
PROD_CD number not null,
SALES_CNT number,
primary key( SALES_ID,DETAIL_ID,PROD_CD ),
constraint FK_SALES_DETAIL_01
foreign key (PROD_CD)
references PRODUCT
)
partition by reference (FK_SALES_DETAIL_01);
[/sql]

※リファレンス・パーティションを作成するためには制約句に使用する
 カラムにNOT NULL制約をつけることが必要です。

▼リファレンス・パーティションの確認

さて、リファレンス・パーティションがどのように作成されたか確認しま
しょう。
[sql]
select table_name, partition_name, high_value
from user_tab_partitions
order by table_name,partition_name

TABLE_NAME PARTITION_NAME HIGH_VALUE
——————– ——————– ———-
ADDRESS HOKKAIDO 1
~~~
ADDRESS OKINAWA 47

CUSTOMER HOKKAIDO
~~
CUSTOMER OKINAWA          子表にも作成されて
                        います。

PRODUCT P100 100
~~
PRODUCT P109 109

SALES HOKKAIDO
~~
SALES OKINAWA          JIS住所表の孫表にも
                        パーティションが作成
                        されています。

SALES_DETAIL P100
~~
SALES_DETAIL P109            製品の子表にも作成
                        されています。

[/sql]

上記結果より各表のパーティション境界が確認できます。

HIGH_VALUEがリファレンス・パーティションではNULLとなっています。
これは、パーティション境界が親表から導かれていることを示しています。
さらに、パーティション名が、親表のものと同じになっています。

では、パーティションのタイプを確認してみましょう。
[sql]
select table_name, partitioning_type, ref_ptn_constraint_name
from user_part_tables

TABLE_NAME PARTITION REF_PTN_CONSTRAINT_NAME
——————– ——— ——————————
ADDRESS LIST
CUSTOMER REFERENCE FK_CUSTOMER_01
PRODUCT LIST
SALES REFERENCE FK_SALES_01
SALES_DETAIL REFERENCE FK_SALES_DETAIL_01
[/sql]
REF_PTN_CONSTRAINT_NAME列に外部キーの制約名が示されています。

▼性能比較用にパーティションなしのテーブル作成

性能を比較するためにリファレンス・パーティションなしの各表を作成しま
す。
[sql]
create table ADDRESS_NOPER
(
ADR_CD number primary key,
KEN_CD number,
CITY_CD number,
TOWN_CD number,
POST_ID varchar2(8),
KEN_NAME varchar2(8),
CITY_NAME varchar2(256),
TOWN_NAME varchar2(256),
STR_NAME varchar2(256)
);

create table CUSTOMER_NOREF
(
CUST_ID number primary key,
CUST_NAME varchar2(200),
ADR_CD number not null,
constraint FK_CUSTOMER_NOREF_01
foreign key (ADR_CD)
references ADDRESS
);

create table SALES_NOREF
(
SALES_ID number primary key,
CUST_ID number not null,
SALES_DATE date,
constraint FK_SALES_NOREF_01
foreign key (CUST_ID)
references CUSTOMER
);

create table PRODUCT_NOPER
(
PROD_CD number primary key,
SYUBETU_CD number,
PROD_NAME varchar2(256),
SYUBETU_NAME varchar2(256),
PROD_AMT number
);

create table SALES_DETAIL_NOREF
(
SALES_ID number not null,
DETAIL_ID number not null,
PROD_CD number not null,
SALES_CNT number,
primary key( SALES_ID,DETAIL_ID,PROD_CD ),
constraint FK_SALES_DETAIL_NOREF_01
foreign key (PROD_CD)
references PRODUCT
);
[/sql]
やっと性能を検証するための準備ができました。

▼全国の県別顧客数を調べてみましょう。
— パーティションを使用
[sql]
alter system flush shared_pool;
alter system flush buffer_cache;

alter session set events=’10046 trace name context forever, level 12′;

select A.KEN_CD,A.KEN_NAME,count(B.CUST_ID)
from ADDRESS A , CUSTOMER B
where A.ADR_CD = B.ADR_CD
group by A.KEN_CD,A.KEN_NAME;

KEN_CD KEN_NAME COUNT(B.CUST_ID)
———- ——– —————-
1 北海道 17994
2 青森県 5070
~~~
46 鹿児島県 2910
47 沖縄県 1584

トレースの結果
call count cpu elapsed disk query current rows
——- —— —— ——– ——- ——- ——– ——
Parse 1 0.01 0.01 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 5 0.27 0.28 2615 2805 0 47
——- —— —— ——– ——- ——- ——– ——
total 7 0.29 0.29 2615 2805 0 47

実行計画
Rows Row Source Operation
——- —————————————————
47 PARTITION LIST ALL PARTITION: 1 47 (cr=2805 pr=2615 pw=0 time=0 us cost=565 size=35926 card=1562)
47 HASH GROUP BY (cr=2805 pr=2615 pw=0 time=0 us cost=565 size=35926 card=1562)
250738 HASH JOIN (cr=2805 pr=2615 pw=0 time=225671 us cost=561 size=2883487 card=125369)
250738 TABLE ACCESS FULL CUSTOMER PARTITION: 1 47 (cr=1435 pr=1340 pw=0 time=45159 us cost=201 size=752214 card=125369)
125369 TABLE ACCESS FULL ADDRESS PARTITION: 1 47 (cr=1370 pr=1275 pw=0 time=53307 us cost=335 size=2131273 card=125369)
[/sql]

— パーティションを使用しない
[sql]
alter system flush shared_pool;
alter system flush buffer_cache;

alter session set events=’10046 trace name context forever, level 12′;

select A.KEN_CD,A.KEN_NAME,count(B.CUST_ID)
from ADDRESS_NOPER A , CUSTOMER_NOREF B
where A.ADR_CD = B.ADR_CD
group by A.KEN_CD,A.KEN_NAME;

トレースの結果
call count cpu elapsed disk query current rows
——- —— —— ——– ——- ——- ——– ——
Parse 1 0.02 0.02 22 120 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 5 0.17 0.24 2569 3309 0 47
——- —— —— ——– ——- ——- ——– ——
total 7 0.20 0.26 2591 3429 0 47

実行計画
Rows Row Source Operation
——- —————————————————
47 HASH GROUP BY (cr=3309 pr=2569 pw=372 time=0 us cost=764 size=35926 card=1562)
250738 HASH JOIN (cr=3309 pr=2569 pw=372 time=2375537 us cost=760 size=2883487 card=125369)
250738 TABLE ACCESS FULL CUSTOMER_NOREF (cr=2238 pr=1128 pw=0 time=26878 us cost=171 size=752214 card=125369)
125369 TABLE ACCESS FULL ADDRESS_NOPER (cr=1071 pr=1069 pw=0 time=32877 us cost=308 size=2131273 card=125369)
[/sql]
リファレンス・パーティションを使用した場合COSTが低くなっていますが、実行時
間が長くなっています。
これは、I/O量の違いで発生していることが原因のようです。

— 北海道の顧客数を調べる
— パーティションを使用
[sql]
connect system/insight
alter system flush shared_pool;
alter system flush buffer_cache;

connect scott/tiger
alter session set events=’10046 trace name context forever, level 12′;

select A.KEN_CD,A.KEN_NAME,count(B.CUST_ID)
from ADDRESS A , CUSTOMER B
where A.ADR_CD = B.ADR_CD
and A.KEN_NAME = ‘北海道’
group by A.KEN_CD,A.KEN_NAME

トレースの結果
call count cpu elapsed disk query current rows
——- —— —— ——– ——- ——- ——– ——
Parse 1 0.01 0.01 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 2 0.05 0.05 1356 1454 0 1
——- —— —— ——– ——- ——- ——– ——
total 4 0.06 0.06 1356 1454 0 1

実行計画
Rows Row Source Operation
——- —————————————————
1 PARTITION LIST ALL PARTITION: 1 47 (cr=1454 pr=1356 pw=0 time=0 us cost=561 size=782 card=34)
1 HASH GROUP BY (cr=1454 pr=1356 pw=0 time=0 us cost=561 size=782 card=34)
17994 HASH JOIN (cr=1454 pr=1356 pw=0 time=17610 us cost=560 size=62445 card=2715)
8997 TABLE ACCESS FULL ADDRESS PARTITION: 1 47 (cr=1370 pr=1275 pw=0 time=19639 us cost=335 size=45339 card=2667)
17994 TABLE ACCESS FULL CUSTOMER PARTITION: 1 47 (cr=84 pr=81 pw=0 time=2552 us cost=201 size=752214 card=125369)
[/sql]
— パーティションを使用しない
[sql]
connect system/insight
alter system flush shared_pool;
alter system flush buffer_cache;

connect scott/tiger
alter session set events=’10046 trace name context forever, level 12′;
select A.KEN_CD,A.KEN_NAME,count(B.CUST_ID)
from ADDRESS_NOPER A , CUSTOMER_NOREF B
where A.ADR_CD = B.ADR_CD
and A.KEN_NAME = ‘北海道’
group by A.KEN_CD,A.KEN_NAME;

トレースの結果
call count cpu elapsed disk query current rows
——- —— —— ——– ——- ——- ——– ——
Parse 1 0.03 0.03 22 120 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 2 0.11 0.11 2197 3309 0 1
——- —— —— ——– ——- ——- ——– ——
total 4 0.14 0.14 2219 3429 0 1

実行計画
Rows Row Source Operation
——- —————————————————
1 HASH GROUP BY (cr=3309 pr=2197 pw=0 time=0 us cost=481 size=782 card=34)
17994 HASH JOIN (cr=3309 pr=2197 pw=0 time=17099 us cost=480 size=62445 card=2715)
8997 TABLE ACCESS FULL ADDRESS_NOPER (cr=1071 pr=1069 pw=0 time=16851 us cost=308 size=45339 card=2667)
250738 TABLE ACCESS FULL CUSTOMER_NOREF (cr=2238 pr=1128 pw=0 time=46845 us cost=171 size=752214 card=125369)
[/sql]
おや、今度は逆転しています。
リファレンス・パーティションを使用した場合コストが高いのに実行時間が
短くなっています。
なぜ、実行時間が短くなったのでしょう。
トレースの結果を比較してみると
リファレンス・パーティションを使用した場合ディスクアクセスブロック数が
1356に比べ、使用しない場合は2219と大きく異なります。
この違いが実行時間の違いに表れていますね。

実行時間が半分程度ですので大きな効果がありました。

さらに効果を期待できるのは、親表とジョインしないと速くなるでしょう。

— CUSTOMERだけを参照し北海道の顧客数を求める
[sql]
connect system/insight
alter system flush shared_pool;
alter system flush buffer_cache;

connect scott/tiger
alter session set events=’10046 trace name context forever, level 12′;

select count(*) from CUSTOMER partition(HOKKAIDO);

トレースの結果
call count cpu elapsed disk query current rows
——- —— —— ——– ——- ——- ——– ——
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 2 0.00 0.00 81 84 0 1
——- —— —— ——– ——- ——- ——– ——
total 4 0.01 0.01 81 84 0 1

実行計画
Rows Row Source Operation
——- —————————————————
1 SORT AGGREGATE (cr=84 pr=81 pw=0 time=0 us)
17994 PARTITION REFERENCE SINGLE PARTITION: 1 1 (cr=84 pr=81 pw=0 time=9315 us cost=13 size=0 card=8997)
17994 TABLE ACCESS FULL CUSTOMER PARTITION: 1 1 (cr=84 pr=81 pw=0 time=3445 us cost=13 size=0 card=8997)
[/sql]
やはり速くなっています。なんと、実行時間が10分の1に短縮されています。
顧客表を親表のJIS住所表とJOINしなくて検索できてしまうため、ディスク
アクセスブロック数が81となり当然の結果ですね。

前提となるのは、
・パーティション名が「HOKKAIDO」であることを知っていることが必要です。

今回は、リファレンス・パーティションの基本的な作成方法と性能について
検証を行いました。

▼ここまでにわかったこと

・リファレンス・パーティションを使用しても性能が向上しない場合が
 あります。
・パーティションを全て参照しなくてよいクエリーの場合、性能向上が
 大きく期待できます。
・子表ごとに長いパーティション化句を明示的に宣言する必要が無いので
 管理が容易です。

次回は、一般的なパーティションを作成した場合や、親子関係が複雑な
場合にどのようにリファレンス・パーティションを作成すればよいのか、
その時、性能差はあるのかについて検証します。

最近、肩が痛く年寄りになったのかなと実感している爺でした。

Oracle 11g リファレンス・パーティションに関する検証 その2

$
0
0

<Oracle 11g リファレンス・パーティションに関する検証 その2>
ペンネーム: パンダおやじ

 前回の検証では、以下のことがわかりました。

・リファレンス・パーティションを使用しても性能が向上しない場合が
 あります。
・パーティションを全て参照しなくてよいクエリーの場合、性能向上が
 大きく期待できます。
・子表ごとに長いパーティション化句を明示的に宣言する必要が無いので
 管理が容易です。

今回は、、一般的なパーティションを作成した場合や、親子関係が複雑な
場合にどのようにリファレンス・パーティションを作成すればよいのか、
その時、性能差はあるのかについて検証します。

▼リファレンス・パーティションの制約

CREATE TABLE構文を確認すると以下のことがわかりました。
・リファレンス・パーティションをコンポジット・パーティション化できない
 つまり、単一のパーティションしか定義できません。
・親表のインターバル・パーティションをリファレンスすることができない

前回の例であげた、売上明細表は、2つの親を持っているため(売上表と
製品表)のどちらか片方のリファレンス・パーティションしか作成でき
ません。

つまり、売上明細をどのように検索する場合が多いかによって、作成すべき
リファレンス・パーティションを決定する必要があります。
たとえば、製品種別ごとに検索するケースが多い場合は、製品表を親表にし、
県別に検索するケースが多い場合は、売上表を親表にします。

▼前回提示したER図

+-----------+     +-----------+
|JIS住所表(ADDRESS)  | +-< |顧客表(CUSTOMER)    |
+-----------+     +-----------+
                       +
                       |
                       ^
                  +-----------+
                  |売上表(SALES)       |
                  +-----------+
                       +
                       |
                       ^
+-----------+     +-------------+
|製品表(PRODUCT)     | +-< |売上明細表(SALES_DETAIL)|
+-----------+     +-------------+

 顧客表はJIS住所表の子表で、県コードは持っていません。
 売上表は顧客表の子表で、住所に関する情報は持っていません。
 売上明細表は、製品表と売上表の子表で、製品種別は持っていません。

▼リファレンス・パーティション以外でパーティションを作成
・顧客表に県コードを追加します。
・売上表にも県コードを追加します。
・売上明細表には、県コードと、製品種別を追加します。

JIS住所表はリファレンス用に作成したものと同じです。
この表はリスト・パーティションで作成します。
[sql]
create table ADDRESS_LIST
(
ADR_CD number primary key,
KEN_CD number,
CITY_CD number,
TOWN_CD number,
POST_ID varchar2(8),
KEN_NAME varchar2(8),
CITY_NAME varchar2(256),
TOWN_NAME varchar2(256),
STR_NAME varchar2(256)
)
partition by list (KEN_CD)
(
partition HOKKAIDO values (1),
~~
partition OKINAWA values (47)
);
[/sql]
顧客表を作成します。
[sql]
create table CUSTOMER_LIST
(
CUST_ID number primary key,
CUST_NAME varchar2(200),
ADR_CD number not null,
KEN_CD number,
constraint FK_CUSTOMER_LIST_01
foreign key (ADR_CD)
references ADDRESS_LIST
)
partition by list (KEN_CD)
(
partition HOKKAIDO values (1),
~~
partition OKINAWA values (47)
)
[/sql]
売上表を作成します。
[sql]
create table SALES_LIST
(
SALES_ID number primary key,
CUST_ID number not null,
KEN_CD number,
SALES_DATE date,
constraint FK_SALES_LIST_01
foreign key (CUST_ID)
references CUSTOMER_LIST
)
partition by list (KEN_CD)
(
partition HOKKAIDO values (1),
~~
partition OKINAWA values (47)
);
[/sql]
製品表はリファレンス用に作成したものと同じです。
この表はリスト・パーティションで作成します。
[sql]
create table PRODUCT_LIST
(
PROD_CD number primary key,
SYUBETU_CD number,
PROD_NAME varchar2(256),
SYUBETU_NAME varchar2(256),
PROD_AMT number
)
partition by list (SYUBETU_CD)
(
partition P100 values (100),
partition P101 values (101),
partition P102 values (102),
partition P103 values (103),
partition P104 values (104),
partition P105 values (105),
partition P106 values (106),
partition P107 values (107),
partition P108 values (108),
partition P109 values (109)
);
[/sql]
売上明細表を作成します。
[sql]
create table SALES_DETAIL_LIST
(
SALES_ID number not null,
DETAIL_ID number not null,
PROD_CD number not null,
SYUBETU_CD number,
SALES_CNT number,
primary key( SALES_ID,DETAIL_ID,PROD_CD ),
constraint FK_SALES_DETAIL_LIST_01
foreign key (PROD_CD)
references PRODUCT_LIST
)
partition by list (SYUBETU_CD)
(
partition P100 values (100),
partition P101 values (101),
partition P102 values (102),
partition P103 values (103),
partition P104 values (104),
partition P105 values (105),
partition P106 values (106),
partition P107 values (107),
partition P108 values (108),
partition P109 values (109)
);
[/sql]

前回の検証結果、パーティション化によって検索性能が向上するのは、単一の
パーティションを使用する検索でしたので、全国の顧客数を調べる性能を検証
しません。

▼北海道の顧客数を調べる
— リファレンスパーティションを使用
[sql]
select A.KEN_CD,A.KEN_NAME,count(B.CUST_ID)
from ADDRESS A , CUSTOMER B
where A.ADR_CD = B.ADR_CD
and A.KEN_NAME = ‘北海道’
group by A.KEN_CD,A.KEN_NAME

トレースの結果
call count cpu elapsed disk query current rows
——- —— —— ——– ——- ——- ——– ——
Parse 1 0.01 0.01 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 2 0.05 0.05 1356 1454 0 1
——- —— —— ——– ——- ——- ——– ——
total 4 0.06 0.06 1356 1454 0 1

実行計画
Rows Row Source Operation
——- —————————————————
1 PARTITION LIST ALL PARTITION: 1 47 (cr=1454 pr=1356 pw=0 time=0 us cost=561 size=782 card=34)
1 HASH GROUP BY (cr=1454 pr=1356 pw=0 time=0 us cost=561 size=782 card=34)
17994 HASH JOIN (cr=1454 pr=1356 pw=0 time=17610 us cost=560 size=62445 card=2715)
8997 TABLE ACCESS FULL ADDRESS PARTITION: 1 47 (cr=1370 pr=1275 pw=0 time=19639 us cost=335 size=45339 card=2667)
17994 TABLE ACCESS FULL CUSTOMER PARTITION: 1 47 (cr=84 pr=81 pw=0 time=2552 us cost=201 size=752214 card=125369)
[/sql]
— リスト・パーティションを使用する
[sql]
select A.KEN_CD,A.KEN_NAME,count(B.CUST_ID)
from ADDRESS_LIST A , CUSTOMER_LIST B
where A.ADR_CD = B.ADR_CD
and A.KEN_NAME = ‘北海道’
group by A.KEN_CD,A.KEN_NAME;

トレースの結果
call count cpu elapsed disk query current rows
——- —— —— ——– ——- ——- ——– ——
Parse 1 0.02 0.01 95 285 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 2 0.22 0.22 2631 3388 0 1
——- —— —— ——– ——- ——- ——– ——
total 4 0.24 0.24 2726 3673 0 1

実行計画
Rows Row Source Operation
——- —————————————————
1 HASH GROUP BY (cr=3388 pr=2631 pw=0 time=0 us cost=719 size=396585 card=8813)
17994 HASH JOIN (cr=3388 pr=2631 pw=0 time=29988 us cost=718 size=396585 card=8813)
8997 PARTITION LIST ALL PARTITION: 1 47 (cr=1610 pr=1209 pw=0 time=8996 us cost=336 size=282016 card=8813)
8997 TABLE ACCESS FULL ADDRESS_LIST PARTITION: 1 47 (cr=1610 pr=1209 pw=0 time=6461 us cost=336 size=282016 card=8813)
250738 PARTITION LIST ALL PARTITION: 1 47 (cr=1778 pr=1422 pw=0 time=159606 us cost=380 size=2434744 card=187288)
250738 TABLE ACCESS FULL CUSTOMER_LIST PARTITION: 1 47 (cr=1778 pr=1422 pw=0 time=78184 us cost=380 size=2434744 card=187288)
[/sql]
トレースの結果を比較してみると、リファレンス・パーティションを使用
した場合のほうが早いことがわかります。

詳しく見てみるとJIS住所表と、顧客表ともにリストパーティションを
持っていても顧客表のパーティションをすべて検索してしまっていますね。

その結果、ディスクアクセスブロック数が多くなり、実行時間に差がでて
います。

実行時間が4分の1程度ですのでリファレンス・パーティションの勝利です。

▼顧客表だけを検索して性能差を比較
前回大きな効果があった、顧客表だけを参照した場合の差はどうでしょう

— リファレンス・パーティションを使用
[sql]
select count(*) from CUSTOMER partition(HOKKAIDO);

トレースの結果
call count cpu elapsed disk query current rows
——- —— —— ——– ——- ——- ——– ——
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 2 0.00 0.00 81 84 0 1
——- —— —— ——– ——- ——- ——– ——
total 4 0.01 0.01 81 84 0 1

実行計画
Rows Row Source Operation
——- —————————————————
1 SORT AGGREGATE (cr=84 pr=81 pw=0 time=0 us)
17994 PARTITION REFERENCE SINGLE PARTITION: 1 1 (cr=84 pr=81 pw=0 time=9315 us cost=13 size=0 card=8997)
17994 TABLE ACCESS FULL CUSTOMER PARTITION: 1 1 (cr=84 pr=81 pw=0 time=3445 us cost=13 size=0 card=8997)
[/sql]
— リスト・パーティションを使用
[sql]
select count(*) from CUSTOMER_LIST partition(HOKKAIDO);

トレースの結果
call count cpu elapsed disk query current rows
——- —— —— ——– ——- ——- ——– ——
Parse 1 0.00 0.00 2 2 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 2 0.00 0.00 18 92 0 1
——- —— —— ——– ——- ——- ——– ——
total 4 0.01 0.01 20 94 0 1

実行計画
Rows Row Source Operation
——- —————————————————
1 SORT AGGREGATE (cr=92 pr=18 pw=0 time=0 us)
17994 PARTITION LIST SINGLE PARTITION: 1 1 (cr=92 pr=18 pw=0 time=10208 us cost=25 size=0 card=19509)
17994 TABLE ACCESS FULL CUSTOMER_LIST PARTITION: 1 1 (cr=92 pr=18 pw=0 time=3828 us cost=25 size=0 card=19509)
[/sql]
結果は、ほぼ同じでした。
しかし、リスト・パーティションを独自に作成したCUSTOMER_LISTを検索した
場合は、ディスクアクセスブロック数が少なくなっています。
もっと大きな表の場合は、個別にリストパーティションを作成したほうに
軍配が上がるかもしれません。

▼売上明細表より製品種別コードが109の県別の個数を調べる
— リファレンス・パーティションを使用する
[sql]
select C.KEN_NAME,count(*)
from SALES_DETAIL A,PRODUCT B,ADDRESS C,SALES D,CUSTOMER E
where B.PROD_CD = A.PROD_CD
and B.SYUBETU_CD = 109
and A.SALES_ID = D.SALES_ID
and D.CUST_ID = E.CUST_ID
and E.ADR_CD = C.ADR_CD
group by C.KEN_NAME;

トレースの結果
call count cpu elapsed disk query current rows
——- —— —— ——– ——- ——- ——– ——
Parse 1 0.03 0.03 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 5 0.25 0.25 3997 4300 0 47
——- —— —— ——– ——- ——- ——– ——
total 7 0.28 0.29 3997 4300 0 47

実行計画
Rows Row Source Operation
——- —————————————————
47 HASH GROUP BY (cr=4300 pr=3997 pw=0 time=0 us cost=1281 size=2538 card=47)
23243 HASH JOIN (cr=4300 pr=3997 pw=0 time=56317 us cost=1280 size=1285416 card=23804)
23243 HASH JOIN (cr=2931 pr=2722 pw=0 time=69087 us cost=729 size=952160 card=23804)
23243 HASH JOIN (cr=1497 pr=1382 pw=0 time=43546 us cost=374 size=690316 card=23804)
23243 PARTITION LIST SINGLE PARTITION: 10 10 (cr=767 pr=737 pw=0 time=31542 us cost=208 size=452276 card=23804)
23243 NESTED LOOPS (cr=767 pr=737 pw=0 time=27967 us cost=208 size=452276 card=23804)
10 TABLE ACCESS FULL PRODUCT PARTITION: 10 10 (cr=7 pr=6 pw=0 time=45 us cost=3 size=90 card=10)
23243 TABLE ACCESS FULL SALES_DETAIL PARTITION: 10 10 (cr=760 pr=731 pw=0 time=23719 us cost=21 size=25060 card=2506)
125369 PARTITION REFERENCE ALL PARTITION: 1 47 (cr=730 pr=645 pw=0 time=76244 us cost=165 size=1253690 card=125369)
125369 TABLE ACCESS FULL SALES PARTITION: 1 47 (cr=730 pr=645 pw=0 time=43159 us cost=165 size=1253690 card=125369)
250738 PARTITION REFERENCE ALL PARTITION: 1 47 (cr=1434 pr=1340 pw=0 time=103673 us cost=354 size=2758118 card=250738)
250738 TABLE ACCESS FULL CUSTOMER PARTITION: 1 47 (cr=1434 pr=1340 pw=0 time=50288 us cost=354 size=2758118 card=250738)
125369 PARTITION LIST ALL PARTITION: 1 47 (cr=1369 pr=1275 pw=0 time=45158 us cost=335 size=1755166 card=125369)
125369 TABLE ACCESS FULL ADDRESS PARTITION: 1 47 (cr=1369 pr=1275 pw=0 time=27142 us cost=335 size=1755166 card=125369)
[/sql]

— リスト・パーティションを使用する
[sql]
select C.KEN_NAME,count(*)
from SALES_DETAIL_LIST A,PRODUCT_LIST B,ADDRESS_LIST C,SALES_LIST D,CUSTOMER_LIST E
where B.PROD_CD = A.PROD_CD
and B.SYUBETU_CD = 109
and A.SALES_ID = D.SALES_ID
and D.CUST_ID = E.CUST_ID
and E.ADR_CD = C.ADR_CD
group by C.KEN_NAME;

トレースの結果
call count cpu elapsed disk query current rows
——- —— —— ——– ——- ——- ——– ——
Parse 1 0.05 0.05 157 1872 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 5 0.32 0.85 4639 6797 0 47
——- —— —— ——– ——- ——- ——– ——
total 7 0.38 0.91 4796 8669 0 47

実行計画
Rows Row Source Operation
——- —————————————————
47 HASH GROUP BY (cr=6797 pr=4639 pw=0 time=0 us cost=2401 size=3080658 card=25046)
23243 HASH JOIN (cr=6797 pr=4639 pw=0 time=59509 us cost=2399 size=3080658 card=25046)
23243 HASH JOIN (cr=5187 pr=3363 pw=0 time=59892 us cost=1642 size=2604784 card=25046)
23243 HASH JOIN (cr=3409 pr=1959 pw=0 time=45462 us cost=815 size=1953588 card=25046)
23243 HASH JOIN (cr=1716 pr=872 pw=0 time=8428 us cost=244 size=1302392 card=25046)
10 PARTITION LIST SINGLE PARTITION: 10 10 (cr=7 pr=0 pw=0 time=0 us cost=3 size=260 card=10)
10 TABLE ACCESS FULL PRODUCT_LIST PARTITION: 10 10 (cr=7 pr=0 pw=0 time=0 us cost=3 size=260 card=10)
250568 PARTITION LIST ALL PARTITION: 1 10 (cr=1709 pr=872 pw=0 time=88939 us cost=240 size=6511908 card=250458)
250568 TABLE ACCESS FULL SALES_DETAIL_LIST PARTITION: 1 10 (cr=1709 pr=872 pw=0 time=54264 us cost=240 size=6511908 card=250458)
125369 PARTITION LIST ALL PARTITION: 1 47 (cr=1693 pr=1087 pw=0 time=53729 us cost=299 size=2816086 card=108311)
125369 TABLE ACCESS FULL SALES_LIST PARTITION: 1 47 (cr=1693 pr=1087 pw=0 time=16872 us cost=299 size=2816086 card=108311)
250738 PARTITION LIST ALL PARTITION: 1 47 (cr=1778 pr=1404 pw=0 time=86010 us cost=380 size=4869488 card=187288)
250738 TABLE ACCESS FULL CUSTOMER_LIST PARTITION: 1 47 (cr=1778 pr=1404 pw=0 time=42532 us cost=380 size=4869488 card=187288)
125369 PARTITION LIST ALL PARTITION: 1 47 (cr=1610 pr=1276 pw=0 time=47716 us cost=336 size=3653510 card=192290)
125369 TABLE ACCESS FULL ADDRESS_LIST PARTITION: 1 47 (cr=1610 pr=1276 pw=0 time=30688 us cost=336 size=3653510 card=192290)
[/sql]
リファレンス・パーティションを使用した場合ディスクアクセスブロック数が
少なくなって、結果として早いことがわかりました。

なぜ、ディスクアクセスブロック数に差があるのでしょう。
これは、リファレンス・パーティションでパーティション用のカラムの定義が
必要ないからですね。

▼今回わかったこと
・リファレンス・パーティションは親子関係が正しく定義できれば、性能もよい
・単一のパーティションを検索する場合、リスト・パーティションが有利
・リファレンス・パーティションは複雑な親子関係には向かない

Oracleさんにリファレンス・パーティションとインターバル・パーティションが
コンポジット・パーティション化できる機能の追加を期待して、リファレンス・
パーティションについて検証を終わります。

肩が痛い原因は、運動不足ですと医者に言われてリハビリを始めた爺でした。

Direct NFS Client の検証 その1(設定編)

$
0
0

大容量のNASを使っている案件が多くなってきたなーと感じる今日この頃。
SASストレージに比べて安価なNASをOracleで上手く活用できないかなー??
と考えていたところ「Direct NFS Client」という機能があると知りました。

恥ずかしながら、つい最近知りました・・・
11gR1からの新機能なので、もう大分以前からの機能なんですね。
とは言え、それほど普及している機能ではないと思います。

この機能を一言で説明すると
「NFSサーバー上のOracleファイルとのI/Oを最適化する」
というようなものらしいです。

Oracleファイルって何を指すんだろうか?
データファイルはもちろんのことですが、
・アーカイブログファイルは?
・RMANバックアップファイルは?
・DataPumpのダンプファイルは?
・外部表、SQL*Loarderの元ファイル(CSVフラットファイル等)は?
etc…

データファイルをNASに置くというのは敷居が高いような気がしますが、アーカイブやバックアップの出力先としては十分に活用できるケースはありそうです。
個人的には、他システムで生成され外部NASに配置されたCSVファイルを外部表ロードするという処理を行っている案件があるので、この処理が高速化されるのであれば使ってみたいところです。

前置きはこの辺にして「Direct NFS Client」を試してみたいと思います。

■前提環境

前提として、NFSマウントの設定がされているものとします。
今回は、以下の構成でNFSの設定を行いました。

serv1
 NFS Client
 Redhat EL5
 Oracle 11.2.0.2
 NFSマウントポイント(serv2:/oradata/ora112)

serv2
 NFS Server
 Redhat EL5 

■設定手順

設定はいたって簡単です。
$ORACLE_HOME/lib/libodm11.so のシンボリックリンクのリンクの向き先を libnfsodm11.so に変更してインスタンスを再起動するだけです。
※オプションの設定ファイル「$ORACLE_HOME/dbs/oranfstab」というファイルを使うことも出来ますが、このファイルの設定は任意です。
今回は使っていません。下記のシンボリックリンク変更だけです。

[perl light=”true”]
$ cd $ORACLE_HOME/lib
$ ls -l libodm11.so
lrwxrwxrwx 1 oracle oinstall 12 9月 21 2010 libodm11.so -> libodmd11.so
$
$
$ mv libodm11.so libodm11.so.bk
$ ln -s libnfsodm11.so libodm11.so
$ ls -l libodm11.so
lrwxrwxrwx 1 oracle oinstall 14 3月 31 14:12 libodm11.so -> libnfsodm11.so
$
$
$ sqlplus / as sysdba

SQL*Plus: Release 11.2.0.2.0 Production on Thu Mar 31 14:16:34 2011

Copyright (c) 1982, 2010, Oracle. All rights reserved.

Connected to:
Oracle Database 11g Release 11.2.0.2.0 – 64bit Production
With the Real Application Clusters and Automatic Storage Management options

SQL> startup

[/perl]

■設定確認

インスタンス起動時のアラートログに以下のような出力が確認できました。

ALTER DATABASE OPEN
Thu Mar 31 15:43:59 2011
Direct NFS: attempting to mount /oradata/ora112 on filer 192.168.X.XXX defined in mtab
Direct NFS: channel config is:channel id [0] local [] path [192.168.X.XXX]
Direct NFS: mount complete dir /oradata/ora112 on 192.168.X.XXX mntport 785 nfsport 2049
Direct NFS: channel id [0] path [192.168.X.XXX] to filer [192.168.X.XXX] via local [] is UP
Direct NFS: channel id [1] path [192.168.X.XXX] to filer [192.168.X.XXX] via local [] is UP

「Direct NFS Client」の領域 /oradata/ora112 配下にデータファイルを作成します。
※今回、その他のデータベースファイルはASM上に配置しています。

[sql light=”true”]
SQL> create tablespace TS_DNFS datafile
‘/oradata/ora112/ts_dnfs.dbf’ size 100M;
[/sql]

「Direct NFS Client」関連の V$ 表の結果結果は以下の通りです。

[sql light=”true”]
SQL> select * from v$dnfs_servers;

ID
———-
SVRNAME
——————————————-
DIRNAME
——————————————-
MNTPORT NFSPORT WTMAX RTMAX
———- ———- ———- ———-
serv2
/oradata/ora112
785 2049 32768 32768

1行が選択されました。

SQL> select * from v$dnfs_files;

FILENAME
——————————————-
FILESIZE PNUM SVR_ID
———- ———- ———-
/oradata/ora112/ts_dnfs.dbf
104865792 16 1

1行が選択されました。

[/sql]

v$dnfs_stats には、「Direct NFS Client」を使った統計がプロセス単位で出力されるらしいです。
また、このビューにはプロセス番号(PNUM 列)しかなかったので v$process と結合すると良さそうです。

[sql light=”true”]
SQL> select program,nfs_read,nfs_write
from v$process p,v$dnfs_stats d
where p.pid=d.pnum;

PROGRAM NFS_READ NFS_WRITE
——————————- ———- ———-
oracle@srv1 (PMON) 0 0
oracle@srv1 (PSP0) 0 0
oracle@srv1 (VKTM) 0 0
oracle@srv1 (GEN0) 0 0
oracle@srv1 (DIAG) 0 0
oracle@srv1 (DBRM) 0 0
oracle@srv1 (PING) 0 0
oracle@srv1 (ACMS) 0 0
oracle@srv1 (DIA0) 0 0
oracle@srv1 (LMON) 0 0
oracle@srv1 (ARC0) 0 0
oracle@srv1 (ARC2) 0 0
oracle@srv1 (LMD0) 0 0
oracle@srv1 (RMS0) 0 0
oracle@srv1 (LMHB) 0 0
oracle@srv1 (MMAN) 0 0
oracle@srv1 (DBW0) 2 0
oracle@srv1 (LGWR) 1 1
oracle@srv1 (CKPT) 0 0
oracle@srv1 (SMON) 0 0
oracle@srv1 (RECO) 0 0
oracle@srv1 (RBAL) 0 0
oracle@srv1 (ASMB) 0 0
oracle@srv1 (MMON) 0 0
oracle@srv1 (MMNL) 0 0
oracle@srv1 (MARK) 0 0
oracle@srv1 (SMCO) 0 0
oracle@srv1 (TNS V1-V3) 0 0
oracle@srv1 (W000) 0 0
oracle@srv1 (W001) 0 0
oracle@srv1 (QMNC) 0 0
oracle@srv1 (Q000) 0 0
oracle@srv1 (Q001) 0 0

34行が選択されました。
[/sql]

今回はここまで。
動作検証は次回の記事で!

Direct NFS Client の検証 その2(動作確認編)

$
0
0

前回の続き。
今回は、様々な処理で実際に Direct NFS Client が動作しているのかどうか確認してみたいと思います。
以下の処理について、v$dnfs_stats の NFS_READ、NFS_WRITE が増加するかどうか確認してみます。

1. データファイル I/O(データファイルを配置)
2. 外部表ロード(外部表のCSVファイルを配置)
3. SQL*Loader ロード(ロード用のCSVファイルを配置)
4. DataPump(ダンプファイルの出力先に指定)
5. アーカイブログ(log_archive_dest に指定)
6. RMAN バックアップ(バックアップの出力先に指定)
7. XML監査ログ(audit_trail=XML、audit_file_dest に指定)

では、早速データファイルから。
単純にテーブルに対する I/O(select、DML、commit等)を行いました。
DBWR、LGWR プロセスが、Direct NFS 領域に対して READ/WRITE を行ったことが分かります。
まぁ、これは予想通り、当たり前の結果ですかね。

[sql light=”true”]
SQL> select program,nfs_read,nfs_write
from v$process p,v$dnfs_stats d
where p.pid=d.pnum and (nfs_read > 0 or nfs_write > 0);

PROGRAM NFS_READ NFS_WRITE
———————————- ———- ———-
oracle@srv1 (DBW0) 2 1
oracle@srv1 (LGWR) 1 1
[/sql]

次に、最も気になっている外部表ロード。
前回も書きましたが、現在関わっている案件で使っているのでこれが早くなると嬉しいところです。
外部表の元ファイルを Direct NFS のマウントポイントに配置し、外部表の作成、外部表の Select を行ってみましたが・・・

[sql light=”true”]
SQL> select program,nfs_read,nfs_write
from v$process p,v$dnfs_stats d
where p.pid=d.pnum and (nfs_read > 0 or nfs_write > 0);

PROGRAM NFS_READ NFS_WRITE
———————————- ———- ———-
oracle@srv1 (DBW0) 2 1
oracle@srv1 (LGWR) 1 1
[/sql]

???
さっきの結果と変わらず何もカウントアップされてない???
検索条件をちょっと変えて、v$session の event 列も出して確認。

[sql highlight=”17″ light=”true”]
SQL> select p.program,s.event,d.nfs_read,d.nfs_write
from v$dnfs_stats d,v$process p,v$session s
where p.pid=d.pnum and p.addr=s.paddr;

PROGRAM EVENT NFS_READ NFS_WRITE
———————– ———————– ——– ———
oracle@srv1 (DBW0) rdbms ipc message 2 1
oracle@srv1 (LGWR) rdbms ipc message 1 1
oracle@srv1 (CKPT) rdbms ipc message 0 0
oracle@srv1 (SMON) smon timer 0 0
oracle@srv1 (RECO) rdbms ipc message 0 0
oracle@srv1 (RBAL) rdbms ipc message 0 0
oracle@srv1 (ASMB) ASM background timer 0 0
oracle@srv1 (MMON) rdbms ipc message 0 0
oracle@srv1 (MMNL) rdbms ipc message 0 0
oracle@srv1 (SMCO) rdbms ipc message 0 0
oracle@srv1 (TNS V1-V3) external table read 0 0



[/sql]

“external table read(= 外部表読み込み)” では使われないらしい・・・
残念。。。
同様のアルゴリズムである SQL*Loader でも使われませんでした。

結局データファイルだけしか使えないのかなー???
と思った矢先、DataPump の expdp/impdp では Direct NFS が使われました。
DataPump の実行中に DMnn、DWnn というプロセスが I/O 操作を行っていることが確認できました。

[sql light=”true”]PROGRAM NFS_READ NFS_WRITE
———————————- ———- ———-
oracle@srv1 (DM00) 0 2
oracle@srv1 (DW00) 2 14[/sql]

DataPump が大丈夫なら、アーカイブログ、RMAN バックアップでも使われるかな?
ログスイッチを実行すると、ARCn が書き込みを行いました。

[sql light=”true”]PROGRAM NFS_READ NFS_WRITE
———————————- ———- ———-
oracle@srv1 (ARC0) 0 4[/sql]

RMAN バックアップ操作を実行。
こちらもちゃんと使われてました。

[sql light=”true”]
PROGRAM EVENT NFS_READ NFS_WRITE
———————– ————————– ——– ———
oracle@srv1 (TNS V1-V3) RMAN backup & recovery I/O 168 118
[/sql]

最後に監査ログ(XML)。
audit_trail=XML にし、audit 設定を行い監査ログを出力。
こちらは NG。

<結論>

【Direct NFS Client が動作する処理】

・データファイル I/O(データファイルを配置)
・DataPump(ダンプファイルの出力先に指定)
・アーカイブログ(log_archive_dest に指定)
・RMAN バックアップ(バックアップの出力先に指定)

【Direct NFS Client が動かない処理】

・外部表ロード(外部表のCSVファイルを配置)
・SQL*Loader ロード(ロード用のCSVファイルを配置)
・XML監査ログ(audit_trail=XML、audit_file_dest に指定)

ということでフラットファイルの扱いには使われないみたいです。

次回、パフォーマンス検証!
と思っていたのですが、そんな潤沢なマシンで検証できていないことと、以下のホワイトペーパーで十分に検証されているようなので割愛したいと思います。

Direct NFS Client Oracle ホワイト・ペーパー

劇的に向上するようなことはなさそうですが、複数経路の NIC を用意することでパフォーマンスが向上する可能性が高くなるようです。

また、複雑なマウントオプションを使ってチューニングする必要がないので、それだけでもメリットかもしれません。

んん???
このホワイトペーパーを読んでみたら、SQL*Loader、外部表にも有効と書いてありました!!
でも、v$dnfs_stats は変わってないし。。。
もう少し調べてみないといけなそうですね。
こちらは次の機会に!

いずれにせよ、設定はとても容易なものですので、NAS を活用する際には Direct NFS Client を試してみては如何でしょうか?

Direct NFS Client の検証 その3(外部表でも使えるの!?再検証編)

$
0
0

前回の検証では、外部表や SQL*Loader 等、フラットファイルに対しては Direct NFS Client が動作しないことを v$dnfs_stats の NFS_READ/NFS_WRITE 列値が変化しないことで確認しました。
しかし、Direct NFS Client Oracle ホワイト・ペーパーによると、これらに対しても有効との記載がありました。

この真実を確かめるべく、今回は別の観点から調査してみたいと思います。

【調査方針】

・マウントオプション rsize/wsize を異なるサイズに設定した 2 つの NFS 領域(同一ディスク)を使用。
・それぞれの NFS 領域に全く同じ内容の外部表の元ファイルを配置して外部表を作成。
・各外部表に対する読み込み性能を比較。
→ この結果、性能差があれば Dirct NFS は有効になっていない(カーネル NFS のマウントオプションの影響を受けている)と言える。

【設定内容】

・NFS#1(/oradata/ora112/dnfs1)
 rsize=32768,wsize=32768   -> NFS(ver3)の Oracle 推奨値
 外部表(ext_tab1)の元ファイルを配置

・NFS#2(/oradata/ora112/dnfs2)
 rsize=512,wsize=512   -> 小さめのサイズを設定
 外部表(ext_tab2)の元ファイルを配置

マウント内容を確認。
※便宜上改行しています。

[perl light=”true”]
# mount -l | grep dnfs
serv2:/oradata/ora112/dnfs1 on /oradata/ora112/dnfs1 type nfs
(rw,rsize=32768,wsize=32768,addr=serv2)
serv2:/oradata/ora112/dnfs2 on /oradata/ora112/dnfs2 type nfs
(rw,rsize=512,wsize=512,addr=serv2)
[/perl]

v$dnfs_servers を確認。

[sql light=”true”]
SQL> select * from v$dnfs_servers;

ID
———-
SVRNAME
——————————————-
DIRNAME
——————————————-
MNTPORT NFSPORT WTMAX RTMAX
———- ———- ———- ———-
1
serv2
/oradata/ora112/dnfs1
838 2049 32768 32768

2
serv2
/oradata/ora112/dnfs2
838 2049 32768 32768

2行が選択されました。
[/sql]

WTMAX,RTMAX(マウントオプションでいう rsize,wsize)は、Direct NFS Client によって Oracle 推奨値の 32768 に調整されています。
これで I/O に Direct NFS Client が利用されているのであれば、どちらのマウントポイントでも同等の性能となる筈です。

では、それぞれの外部表の読み込み性能を確認してみます。

[sql light=”true” highlight=”20,29″]
SQL> — 外部表のパス確認
SQL> select e.table_name,e.location,d.directory_path
from dba_external_locations e,dba_directories d
where e.directory_name=d.directory_name;

TABLE_NAME LOCATION DIRECTORY_PATH
————— ————— ——————————
EXT_TAB1 ext_tab.tbl /oradata/ora112/dnfs1
EXT_TAB2 ext_tab.tbl /oradata/ora112/dnfs2

SQL> — select 性能測定
SQL> set timing on
SQL> — NFS#1 配下の外部表
SQL> select count(*) from ext_tab1;

COUNT(*)
———-
10000000

経過: 00:00:18.71

SQL> — NFS#2 配下の外部表
SQL> select count(*) from ext_tab2;

COUNT(*)
———-
10000000

経過: 00:00:47.97
[/sql]

大きな差が出ました!
比較的大きな外部表の為、rsize,wsize が小さければ性能劣化に繋がります。
つまり、外部表の読み取りでは Direct NFS Client は動作していない(カーネル NFS のマウントオプションの影響を受けた)為、rsize,wsize の小さい NFS#2 の方が劣化したと判断できます。

尚、ファイルキャッシュによる性能差ではないことを確認する為、以下のコマンドでファイルキャッシュをクリアした上で複数回実行してみましたが、上記とほぼ同様に大きな差が生じることを確認しました。

[perl light=”true”]
# echo 3 > /proc/sys/vm/drop_caches
[/perl]

また、念の為、Direct NFS Client が動作するケースでも確認しました。
それぞれの NFS 領域にデータファイルを配置して通常表に対する性能を確認したところ、どちらの領域に置いた表でも同様の性能となりました。
こちらは、Direct NFS Client が動作している(カーネル NFS のマウントオプションは無視されている)と判断できます。

【結論】

やっぱりフラットファイルの I/O では Direct NFS Client は動作しない!!

と、断言したいところですが、ホワイトペーパーには使えると書いてありますので、実案件で検討したい場合は、オラクルサポートにご確認下さい m(_ _)m
そして、真相を私に教えて下さい(笑)


Active Data Guard の検証 その1(環境構築編)

$
0
0

Active Data Guard とは、Physical Standby データべースを READ ONLY でオープンして利用できるという 11g からの新機能です。
先日参加した IOUG のセッションの中で、自分が最も興味を持った機能「Automatic Block Repair(ABR)」が動作する条件が Active Data Guard ということなので、まずは Active Data Guard 環境を構築したいと思います。

尚、過去の「おら! オラ! Oracle」では RMAN のバックアップから Standby データべースを構築していましたが、今回は RMAN の duplicate コマンドを使って構築してみます。
これ、とっても楽ちんです♪
 

■検証環境

【Primary】
OS      :RHEL5.5
hostname:srv1
Oracle  :11.2.0.2
SID     :dg112(シングルインスタンスの DB として構築済)

【Standby】
OS      :RHEL5.5
hostname:srv2
Oracle  :11.2.0.2(ソフトウェアのみインストール済)
SID     :db112as(未構成)

 

■準備1(ディレクトリ作成、Oracle*Net 環境設定)

【Standby】

Physical Standby の SID を静的登録するリスナーを構成し、起動する。

[perl gutter=”false”]
$ vi $ORACLE_HOME/network/admin/listener.ora
$ lsnrctl start LISTENER_DG
[/perl]

[perl collapse=”true” title=”今回設定したリスナー定義の詳細はここをクリック” classname=”titleHidden” gutter=”false”]
LISTENER_DG=
(DESCRIPTION=
(ADDRESS=(PROTOCOL=tcp)(HOST=srv2)(PORT=1522))
)

SID_LIST_LISTENER_DG=
(SID_DESC=
(ORACLE_HOME=/u01/app/oracle/product/11.2.0/db_1)
(SID_NAME=dg112as)
)
[/perl]
 

【Primary & Standby】

Primary & Standby 接続用のネットサービス名を構成する。

[perl gutter=”false”]
$ vi $ORACLE_HOME/network/admin/tnsnames.ora
[/perl]

[perl collapse=”true” title=”今回設定したネットサービス定義の詳細はここをクリック” classname=”titleHidden” gutter=”false”]
DG112 =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = srv1)(PORT = 1521))
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = dg112)
)
)

DG112AS =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = srv2)(PORT = 1522))
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = dg112as)
)
)
[/perl]
 

■準備2(DB 環境設定)

【Primary】

初期化パラメータファイル(pfile)を作成する。

[sql gutter=”false”]
$ export ORACLE_SID=dg112
$ sqlplus / as sysdba

SQL> create pfile from spfile;

ファイルが作成されました。

SQL> exit
[/sql]
 

初期化パラメータファイル(pfile)を編集する。

[perl gutter=”false”]
$ vi $ORACLE_HOME/dbs/initdg112.ora
[/perl]

[perl collapse=”true” title=”今回設定したパラメータ(primary 用)の詳細はこちらをクリック” classname=”titleHidden” gutter=”false”]
audit_file_dest=’/u01/app/oracle/admin/dg112/adump’
audit_trail=’db’
compatible=’11.2.0.0.0′
control_files=’/u01/app/oracle/oradata/DG112/controlfile/o1_mf_6vc2m3tz_.ctl’
db_block_size=8192
db_create_file_dest=’/u01/app/oracle/oradata’
db_domain=”
db_name=’dg112′
db_unique_name=’dg112′
diagnostic_dest=’/u01/app/oracle’
memory_target=838860800
nls_language=’JAPANESE’
nls_territory=’JAPAN’
open_cursors=300
processes=150
remote_login_passwordfile=’EXCLUSIVE’
undo_tablespace=’UNDOTBS1′
##### for Data Guard #####
log_archive_config=’dg_config=(dg112,dg112as)’
log_archive_dest_1=’location=/u01/app/oracle/oradata/DG112/arch VALID_FOR=(all_logfiles,all_roles) DB_UNIQUE_NAME=dg112′
log_archive_dest_2=’SERVICE=dg112as LGWR SYNC VALID_FOR=(online_logfile,primary_role) DB_UNIQUE_NAME=dg112as REOPEN=30′
log_archive_dest_state_1=ENABLE
log_archive_dest_state_2=ENABLE
db_file_name_convert=’/u01/app/oracle/oradata/DG112AS/datafile/’,’/u01/app/oracle/oradata/DG112/datafile/’
log_file_name_convert=’/u01/app/oracle/oradata/DG112AS/onlinelog/’,’/u01/app/oracle/oradata/DG112/onlinelog/’
fal_server=dg112as
fal_client=dg112
standby_file_management=’AUTO’
[/perl]
 

初期化パラメータファイル(pfile)とパスワードファイルをスタンバイ環境にコピーする。

[perl gutter=”false”]
$ scp -p $ORACLE_HOME/dbs/initdg112.ora srv2:$ORACLE_HOME/dbs/initdg112as.ora
$ scp -p $ORACLE_HOME/dbs/orapwdg112 srv2:$ORACLE_HOME/dbs/orapwdg112as
[/perl]
 

修正した pfile から spfile を作成する。

[sql gutter=”false”]
$ sqlplus / as sysdba

SQL> shutdown immediate
データベースがクローズされました。
データベースがディスマウントされました。
ORACLEインスタンスがシャットダウンされました。

SQL> create spfile from pfile;

ファイルが作成されました。
[/sql]
 

アーカイブログモードに設定する。

[sql gutter=”false”]
SQL> startup mount
ORACLEインスタンスが起動しました。

Total System Global Area 835104768 bytes
Fixed Size 2231088 bytes
Variable Size 490734800 bytes
Database Buffers 335544320 bytes
Redo Buffers 6594560 bytes
データベースがマウントされました。

SQL> alter database archivelog;

データベースが変更されました。

SQL> archive log list;
データベース・ログ・モード アーカイブ・モード
自動アーカイブ 有効
アーカイブ先 /u01/app/oracle/oradata/DG112/arch
最も古いオンライン・ログ順序 46
アーカイブする次のログ順序 48
現行のログ順序 48

SQL> alter database open;

データベースが変更されました。
[/sql]
 

強制ロギングモードにする。

[sql gutter=”false”]
SQL> alter database force logging;

データベースが変更されました。

SQL> select force_logging from v$database;

FORCE_LOG
———
YES
SQL> exit
[/sql]
 
 

【Standby】

必要なディレクトリ(データベースファイルの格納先、及び adump)を作成する。

[perl gutter=”false”]
$ mkdir -p /u01/app/oracle/oradata/DG112AS/arch
$ mkdir -p /u01/app/oracle/oradata/DG112AS/controlfile
$ mkdir -p /u01/app/oracle/oradata/DG112AS/datafile
$ mkdir -p /u01/app/oracle/oradata/DG112AS/onlinelog
$ mkdir -p /u01/app/oracle/oradata/DG112AS/standbylog
$ mkdir -p /u01/app/oracle/admin/dg112as/adump
[/perl]
 

プライマリDBからコピーした初期化パラメータファイル(pfile)を編集する。

[perl gutter=”false”]
$ vi $ORACLE_HOME/dbs/initdg112as.ora
[/perl]

[perl collapse=”true” title=”今回設定したパラメータ(standby 用)の詳細はこちらをクリック” classname=”titleHidden” highlight=”1,4,9,20,21,24-27″ gutter=”false”]
audit_file_dest=’/u01/app/oracle/admin/dg112as/adump’
audit_trail=’db’
compatible=’11.2.0.0.0′
control_files=’/u01/app/oracle/oradata/DG112AS/controlfile/o1_mf_6vc2m3tz_.ctl’
db_block_size=8192
db_create_file_dest=’/u01/app/oracle/oradata’
db_domain=”
db_name=’dg112′
db_unique_name=’dg112as’
diagnostic_dest=’/u01/app/oracle’
memory_target=838860800
nls_language=’JAPANESE’
nls_territory=’JAPAN’
open_cursors=300
processes=150
remote_login_passwordfile=’EXCLUSIVE’
undo_tablespace=’UNDOTBS1′
##### for Data Guard
log_archive_config=’dg_config=(dg112,dg112as)’
log_archive_dest_1=’location=/u01/app/oracle/oradata/DG112AS/arch VALID_FOR=(all_logfiles,all_roles) DB_UNIQUE_NAME=dg112as’
log_archive_dest_2=’SERVICE=dg112 ASYNC VALID_FOR=(online_logfiles,primary_role) DB_UNIQUE_NAME=dg112′
log_archive_dest_state_1=ENABLE
log_archive_dest_state_2=ENABLE
db_file_name_convert=’/u01/app/oracle/oradata/DG112/datafile/’,’/u01/app/oracle/oradata/DG112AS/datafile/’
log_file_name_convert=’/u01/app/oracle/oradata/DG112/onlinelog/’,’/u01/app/oracle/oradata/DG112AS/onlinelog/’
fal_server=’dg112′
fal_client=’dg112as’
standby_file_management=’AUTO’

※網掛け箇所が Primary との相違箇所
[/perl]

 

インスタンスを nomount で起動する。

[sql gutter=”false”]
$ export ORACLE_SID=dg112as
$ sqlplus / as sysdba

SQL> startup nomount
ORACLEインスタンスが起動しました。

Total System Global Area 835104768 bytes
Fixed Size 2231088 bytes
Variable Size 490734800 bytes
Database Buffers 335544320 bytes
Redo Buffers 6594560 bytes
[/sql]

 

■構築

ちょっと準備が面倒でした。。。
ここからはとっても簡単です。

【Primary】

まず、RMAN で Primary と Standby データベースに接続します。

[perl gutter=”false”]
$ rman target sys/password@dg112 auxiliary sys/password@dg112as

Recovery Manager: Release 11.2.0.2.0 – Production on 水 4月 26 22:30:21 2011

Copyright (c) 1982, 2009, Oracle and/or its affiliates. All rights reserved.

ターゲット・データベース: DG112 (データベースID=2642084339)に接続されました
補助データベース: DG112に接続されました(マウントされていません)

RMAN>
[/perl]
 

次に duplicate コマンドで Standby データベースを作成します。

[sql gutter=”false”]
RMAN> duplicate target database for standby from active database;
[/sql]

Primary と Standby のデータファイル名が同じ(同じディレクトリ構成)になる場合、nofilenamecheck オプションを付けます。
(今回はディレクトリ構成が異なる為、nofilenamecheck オプションは付けていません。)

duplicate コマンドの詳細は、以下のマニュアルを参照下さい。
DUPLICATEコマンドを使用したスタンバイ・データベースの作成

[perl collapse=”true” title=”duplicate の実行ログを確認したい場合はここをクリック” classname=”titleHidden” gutter=”false”]
RMAN> duplicate target database for standby from active database;

Duplicate Dbが開始されました(開始時間: 11-04-26)
リカバリ・カタログのかわりにターゲット・データベース制御ファイルを使用しています
チャネル: ORA_AUX_DISK_1が割り当てられました
チャネルORA_AUX_DISK_1: SID=137 デバイス・タイプ=DISK

メモリー・スクリプトの内容:
{
backup as copy reuse
targetfile ‘/u01/app/oracle/product/11.2.0/db_1/dbs/orapwdg112′ auxiliary format
‘/u01/app/oracle/product/11.2.0/db_1/dbs/orapwdg112as’ ;
}
メモリー・スクリプトを実行しています

backupが開始されました(開始時間: 11-04-26)
チャネル: ORA_DISK_1が割り当てられました
チャネルORA_DISK_1: SID=17 デバイス・タイプ=DISK
backupが完了しました(完了時間: 11-04-26)

メモリー・スクリプトの内容:
{
backup as copy current controlfile for standby auxiliary format ‘/u01/app/oracle/oradata/DG112AS/controlfile/o1_mf_6vc2m3tz_.ctl';
sql clone "create spfile from memory";
shutdown clone immediate;
startup clone nomount;
sql clone "alter system set control_files =
”/u01/app/oracle/oradata/DG112AS/controlfile/o1_mf_6vc2m3tz_.ctl” comment=
”Set by RMAN” scope=spfile";
shutdown clone immediate;
startup clone nomount;
}
メモリー・スクリプトを実行しています

backupが開始されました(開始時間: 11-04-26)
チャネルORA_DISK_1の使用
チャネルORA_DISK_1: データファイルのコピーを開始しています
スタンバイ制御ファイルをコピー中です
出力ファイル名=/u01/app/oracle/product/11.2.0/db_1/dbs/snapcf_dg112.f タグ=TAG20110426T223039 レコードID=9 スタンプ=749644239
チャネルORA_DISK_1: データファイルのコピーが終了しました。経過時間: 00:00:01
backupが完了しました(完了時間: 11-04-26)

SQL文: create spfile from memory

Oracleインスタンスがシャットダウンしました

補助データベースに接続されました(開始されていません)
Oracleインスタンスが起動しました

システム・グローバル領域の合計は、 835104768バイトです。

Fixed Size 2231088バイト
Variable Size 494929104バイト
Database Buffers 331350016バイト
Redo Buffers 6594560バイト

SQL文: alter system set control_files = ”/u01/app/oracle/oradata/DG112AS/controlfile/o1_mf_6vc2m3tz_.ctl” comment= ”Set by RMAN” scope=spfile

Oracleインスタンスがシャットダウンしました

補助データベースに接続されました(開始されていません)
Oracleインスタンスが起動しました

システム・グローバル領域の合計は、 835104768バイトです。

Fixed Size 2231088バイト
Variable Size 494929104バイト
Database Buffers 331350016バイト
Redo Buffers 6594560バイト

メモリー・スクリプトの内容:
{
sql clone ‘alter database mount standby database';
}
メモリー・スクリプトを実行しています

SQL文: alter database mount standby database

メモリー・スクリプトの内容:
{
set newname for tempfile 1 to
"/u01/app/oracle/oradata/DG112AS/datafile/o1_mf_temp_6vc2nrm3_.tmp";
switch clone tempfile all;
set newname for datafile 1 to
"/u01/app/oracle/oradata/DG112AS/datafile/o1_mf_system_6vc2mc9p_.dbf";
set newname for datafile 2 to
"/u01/app/oracle/oradata/DG112AS/datafile/o1_mf_sysaux_6vc2n6qh_.dbf";
set newname for datafile 3 to
"/u01/app/oracle/oradata/DG112AS/datafile/o1_mf_undotbs1_6vc2nn0p_.dbf";
set newname for datafile 4 to
"/u01/app/oracle/oradata/DG112AS/datafile/o1_mf_users_6vhs4xvr_.dbf";
backup as copy reuse
datafile 1 auxiliary format
"/u01/app/oracle/oradata/DG112AS/datafile/o1_mf_system_6vc2mc9p_.dbf" datafile
2 auxiliary format
"/u01/app/oracle/oradata/DG112AS/datafile/o1_mf_sysaux_6vc2n6qh_.dbf" datafile
3 auxiliary format
"/u01/app/oracle/oradata/DG112AS/datafile/o1_mf_undotbs1_6vc2nn0p_.dbf" datafile
4 auxiliary format
"/u01/app/oracle/oradata/DG112AS/datafile/o1_mf_users_6vhs4xvr_.dbf" ;
sql ‘alter system archive log current';
}
メモリー・スクリプトを実行しています

実行コマンド: SET NEWNAME

制御ファイルで一時ファイル1の名前を/u01/app/oracle/oradata/DG112AS/datafile/o1_mf_temp_6vc2nrm3_.tmpに変更しました

実行コマンド: SET NEWNAME

実行コマンド: SET NEWNAME

実行コマンド: SET NEWNAME

実行コマンド: SET NEWNAME

backupが開始されました(開始時間: 11-04-26)
チャネルORA_DISK_1の使用
チャネルORA_DISK_1: データファイルのコピーを開始しています
入力データファイル・ファイル番号=00001 名前=/u01/app/oracle/oradata/DG112/datafile/o1_mf_system_6vc2mc9p_.dbf
出力ファイル名=/u01/app/oracle/oradata/DG112AS/datafile/o1_mf_system_6vc2mc9p_.dbf タグ=TAG20110426T223123
チャネルORA_DISK_1: データファイルのコピーが終了しました。経過時間: 00:00:36
チャネルORA_DISK_1: データファイルのコピーを開始しています
入力データファイル・ファイル番号=00002 名前=/u01/app/oracle/oradata/DG112/datafile/o1_mf_sysaux_6vc2n6qh_.dbf
出力ファイル名=/u01/app/oracle/oradata/DG112AS/datafile/o1_mf_sysaux_6vc2n6qh_.dbf タグ=TAG20110426T223123
チャネルORA_DISK_1: データファイルのコピーが終了しました。経過時間: 00:00:25
チャネルORA_DISK_1: データファイルのコピーを開始しています
入力データファイル・ファイル番号=00003 名前=/u01/app/oracle/oradata/DG112/datafile/o1_mf_undotbs1_6vc2nn0p_.dbf
出力ファイル名=/u01/app/oracle/oradata/DG112AS/datafile/o1_mf_undotbs1_6vc2nn0p_.dbf タグ=TAG20110426T223123
チャネルORA_DISK_1: データファイルのコピーが終了しました。経過時間: 00:00:07
チャネルORA_DISK_1: データファイルのコピーを開始しています
入力データファイル・ファイル番号=00004 名前=/u01/app/oracle/oradata/DG112/datafile/o1_mf_users_6vhs4xvr_.dbf
出力ファイル名=/u01/app/oracle/oradata/DG112AS/datafile/o1_mf_users_6vhs4xvr_.dbf タグ=TAG20110426T223123
チャネルORA_DISK_1: データファイルのコピーが終了しました。経過時間: 00:00:01
backupが完了しました(完了時間: 11-04-26)

SQL文: alter system archive log current

メモリー・スクリプトの内容:
{
switch clone datafile all;
}
メモリー・スクリプトを実行しています

データファイル1はデータファイル・コピーに切り替えられました
入力データファイル・コピー レコードID=9 スタンプ=749644355 ファイル名=/u01/app/oracle/oradata/DG112AS/datafile/o1_mf_system_6vc2mc9p_.dbf
データファイル2はデータファイル・コピーに切り替えられました
入力データファイル・コピー レコードID=10 スタンプ=749644355 ファイル名=/u01/app/oracle/oradata/DG112AS/datafile/o1_mf_sysaux_6vc2n6qh_.dbf
データファイル3はデータファイル・コピーに切り替えられました
入力データファイル・コピー レコードID=11 スタンプ=749644355 ファイル名=/u01/app/oracle/oradata/DG112AS/datafile/o1_mf_undotbs1_6vc2nn0p_.dbf
データファイル4はデータファイル・コピーに切り替えられました
入力データファイル・コピー レコードID=12 スタンプ=749644355 ファイル名=/u01/app/oracle/oradata/DG112AS/datafile/o1_mf_users_6vhs4xvr_.dbf
Duplicate Dbが完了しました(完了時間: 11-04-26)
[/perl]

 

【Standby】

duplicate が完了すると、Standby データベースが mount 状態で起動します。
インスタンスに接続し、Standby Redo ログファイルを構成します。

※ Standby Redo ログのグループは、Primary のグループ数 + 1 とします。
今回は Primary のグループ数は 3 の為、4 グループ作成します。
また、サイズは Primary と同じサイズにします。

[sql gutter=”false”]
$ sqlplus / as sysdba

SQL> select database_role,open_mode from v$database;

DATABASE_ROLE OPEN_MODE
—————— ———————
PHYSICAL STANDBY MOUNTED

SQL> alter database add standby logfile
group 4 ‘/u01/app/oracle/oradata/DG112AS/standbylog/standbyredo04.log’ size 50M,
group 5 ‘/u01/app/oracle/oradata/DG112AS/standbylog/standbyredo05.log’ size 50M,
group 6 ‘/u01/app/oracle/oradata/DG112AS/standbylog/standbyredo06.log’ size 50M,
group 7 ‘/u01/app/oracle/oradata/DG112AS/standbylog/standbyredo07.log’ size 50M;

データベースが変更されました。

[/sql]

 

【Primary】

Primary にも Standby Redo ログファイルを構成します。

[sql gutter=”false”]
$ mkdir /u01/app/oracle/oradata/DG112/standbylog
$ sqlplus / as sysdba

SQL> alter database add standby logfile
group 4 ‘/u01/app/oracle/oradata/DG112/standbylog/standbyredo04.log’ size 50M,
group 5 ‘/u01/app/oracle/oradata/DG112/standbylog/standbyredo05.log’ size 50M,
group 6 ‘/u01/app/oracle/oradata/DG112/standbylog/standbyredo06.log’ size 50M,
group 7 ‘/u01/app/oracle/oradata/DG112/standbylog/standbyredo07.log’ size 50M;

データベースが変更されました。

[/sql]

 

【Standby】

Active Data Guard は READ ONLY でオープンして利用できるというものです。
データベースをオープンした状態で管理リカバリモードを開始します。

[sql gutter=”false”]
SQL> alter database open;

データベースが変更されました。

SQL> alter database recover managed standby database
using current logfile disconnect from session;

データベースが変更されました。

SQL> select database_role,open_mode from v$database;

DATABASE_ROLE OPEN_MODE
—————— ———————
PHYSICAL STANDBY READ ONLY WITH APPLY
[/sql]
 

OPEN_MODE が “READ ONLY WITH APPLY” となっています。
これで完成!

簡単に動作を確認してみます。
まずは、アーカイブ適用状況の確認。

 

【Primary】

[sql gutter=”false”]
SQL> select max(sequence#) from v$archived_log;

MAX(SEQUENCE#)
————–
57

SQL> alter system archive log current;

システムが変更されました。

SQL> select max(sequence#) from v$archived_log;

MAX(SEQUENCE#)
————–
58
[/sql]

 

【Standby】

[sql gutter=”false”]
SQL> select max(sequence#) from v$archived_log;

MAX(SEQUENCE#)
————–
58
[/sql]

アーカイブの最終シーケンス番号が同じ(=アーカイブの同期が取れている)。

 

次に DML。
 

【Primary】

[sql gutter=”false”]
SQL> create table test_tab1 (col1 number) tablespace users;

表が作成されました。

SQL> insert into test_tab1 values (999);

1行が作成されました。

SQL> commit;

コミットが完了しました。

SQL> select * from test_tab1;

COL1
———-
999

1行が選択されました。
[/sql]

 
 

【Standby】

[sql gutter=”false”]
SQL> select * from test_tab1;

COL1
———-
999

1行が選択されました。
[/sql]
Standby 環境から Primary 環境と同じデータを読むことが出来ました!

次回、魅惑(?)の Automatic Block Repair(ABR)機能を確認します。

Active Data Guard の検証 その2(ABR 検証編)

$
0
0

GW ボケやらセミナーやらで、すっかり更新が遅れてしまいました (^^;q
今回は、Automatic Block Repair(ABR)機能を確認します。

前回の blog にも書きましたが、この機能は先日参加した IOUG のセッションの中で自分が最も興味を持った機能で、データベースファイルのブロック障害が発生した場合に Standby Database 側の正常なブロックを利用してリカバリしてくれるという機能です。
メディア障害と言われるファイルレベルでの障害は意外と発生しないものですが、ブロック障害には何度も遭遇したことがあります。
これが自動で復旧してくれるなんて素敵♪

まずは、ABR が機能しないケース(Active Data Guard を動作させない)でブロック障害を発生させてみます。

【Standby】

管理リカバリモードを停止します。

[sql gutter=”false”]
SQL> alter database recover managed standby database cancel;

データベースが変更されました。

SQL> select database_role,open_mode from v$database;

DATABASE_ROLE OPEN_MODE
—————— ———————
PHYSICAL STANDBY READ ONLY
[/sql]
 

【Primary】

ブロック障害を発生させる表(scott.emp を利用)が格納されているデータファイルID、ブロックID等を確認します。

[sql gutter=”false”]
SQL> select * from scott.emp;

EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
—— ———- ———- —– ——– —— —– ——-
7782 CLARK MANAGER 7839 81-06-09 2450 10
7499 ALLEN SALESMAN 7698 81-02-20 1600 300 30
7369 SMITH CLERK 7902 80-12-17 800 20
7844 TURNER SALESMAN 7698 81-09-08 1500 0 30
7902 FORD ANALYST 7566 81-12-03 3000 20
7698 BLAKE MANAGER 7839 81-05-01 2850 30
7934 MILLER CLERK 7782 82-01-23 1300 10
7900 JAMES CLERK 7698 81-12-03 950 30
7654 MARTIN SALESMAN 7698 81-09-28 1250 1400 30
7521 WARD SALESMAN 7698 81-02-22 1250 500 30
7839 KING PRESIDENT 81-11-17 5000 10
7566 JONES MANAGER 7839 81-04-02 2975 20

12行が選択されました。

SQL> select segment_name,extent_id,file_id,block_id,blocks
from dba_extents
where segment_name = ‘EMP';

SEGMENT_NAME EXTENT_ID FILE_ID BLOCK_ID BLOCKS
————— ———- ———- ———- ———-
EMP 0 4 128 8

SQL> select file#,name from v$datafile where file#=4;

FILE# NAME
———- —————————————————————
4 /u01/app/oracle/oradata/DG112/datafile/users01.dbf
[/sql]

上記の結果から次のことを確認しました。

・EMP 表は物理ファイル /u01/app/oracle/oradata/DG112/datafile/users01.dbf に格納
・block_id 128 から 8 ブロック分を利用している

次に dd コマンドを利用して物理ファイル users01.dbf の該当ブロックを壊します。

[perl gutter=”false”]
$ cd /u01/app/oracle/oradata/DG112/datafile
$ dd if=system01.dbf of=users01.dbf bs=8192 seek=128 count=8
8+0 records in
8+0 records out
65536 bytes (66 kB) copied, 0.000381 seconds, 172 MB/s
[/perl]

ここでは、SYSTEM 表領域のデータファイルの内容を USERS 表領域のデータファイルに書き込むことで壊しています。
dd コマンドで指定している値の意味は以下の通りです。

if -> 入力ファイル
of -> 出力ファイル(破損させるファイル)
bs -> Oracle ブロックサイズ
seek -> 書き込みを行う先頭ブロック(dba_extents で確認した block_id)
count -> seek に指定したブロックから何ブロック書き込むか(dba_extents で確認した blocks)

これで EMP 表のデータが格納されているブロックが壊れた筈です。
この状態で EMP 表を参照してみます。

[sql gutter=”false”]
SQL> select * from scott.emp;

EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
—— ———- ———- —– ——– —— —– ——-
7782 CLARK MANAGER 7839 81-06-09 2450 10
7499 ALLEN SALESMAN 7698 81-02-20 1600 300 30
7369 SMITH CLERK 7902 80-12-17 800 20
7844 TURNER SALESMAN 7698 81-09-08 1500 0 30
7902 FORD ANALYST 7566 81-12-03 3000 20
7698 BLAKE MANAGER 7839 81-05-01 2850 30
7934 MILLER CLERK 7782 82-01-23 1300 10
7900 JAMES CLERK 7698 81-12-03 950 30
7654 MARTIN SALESMAN 7698 81-09-28 1250 1400 30
7521 WARD SALESMAN 7698 81-02-22 1250 500 30
7839 KING PRESIDENT 81-11-17 5000 10
7566 JONES MANAGER 7839 81-04-02 2975 20

12行が選択されました。
[/sql]

おっと・・・
バッファキャッシュに残っていたので、まだ参照できました。
バッファキャッシュをクリアし、もう一度参照してみます。

[sql gutter=”false”]
SQL> alter system flush buffer_cache;

システムが変更されました。

SQL> select * from scott.emp;
select * from scott.emp
*
行1でエラーが発生しました。:
ORA-01578:
Oracleデータ・ブロックに障害が発生しました(ファイル番号4、ブロック番号131)
ORA-01110: データファイル4:’/u01/app/oracle/oradata/DG112/datafile/users01.dbf’
[/sql]

はい、ちゃんと壊れてくれました ^^
では、いよいよ ABR を確認してみます。
 

【Standby】

管理リカバリモードを開始します。

[sql gutter=”false”]
SQL> alter database recover managed standby database
using current logfile disconnect from session;

データベースが変更されました。

SQL> select database_role,open_mode from v$database;

DATABASE_ROLE OPEN_MODE
—————— ———————
PHYSICAL STANDBY READ ONLY WITH APPLY
[/sql]
 

【Primary】

バッファキャッシュには、ブロック障害時の情報が残っているので、再度バッファキャッシュをクリアしてから参照します。

[sql gutter=”false”]
SQL> alter system flush buffer_cache;

システムが変更されました。

SQL> select * from scott.emp;

EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
—— ———- ———- —– ——– —— —– ——-
7782 CLARK MANAGER 7839 81-06-09 2450 10
7499 ALLEN SALESMAN 7698 81-02-20 1600 300 30
7369 SMITH CLERK 7902 80-12-17 800 20
7844 TURNER SALESMAN 7698 81-09-08 1500 0 30
7902 FORD ANALYST 7566 81-12-03 3000 20
7698 BLAKE MANAGER 7839 81-05-01 2850 30
7934 MILLER CLERK 7782 82-01-23 1300 10
7900 JAMES CLERK 7698 81-12-03 950 30
7654 MARTIN SALESMAN 7698 81-09-28 1250 1400 30
7521 WARD SALESMAN 7698 81-02-22 1250 500 30
7839 KING PRESIDENT 81-11-17 5000 10
7566 JONES MANAGER 7839 81-04-02 2975 20

12行が選択されました。
[/sql]

バッチリ参照できました!!
この時のアラートログからは、
“Automatic block media recovery successful for (file# X, block# XXX)”
という出力が確認できます。

[perl collapse=”true” title=”アラートログの詳細はこちらをクリック” classname=”titleHidden” highlight=”16,17,19,20″ gutter=”false”]
Hex dump of (file 4, block 131) in trace file /u01/app/oracle/diag/rdbms/dg112/dg112/trace/dg112_ora_6943.trc
Corrupt block relative dba: 0x01000083 (file 4, block 131)
Bad header found during buffer read
Data in bad block:
type: 30 format: 2 rdba: 0x00400003
last change scn: 0x0000.0030424f seq: 0x1 flg: 0x04
spare1: 0x0 spare2: 0x0 spare3: 0x0
consistency value in tail: 0x424f1e01
check value in block header: 0xb0ec
computed block checksum: 0x0
Reading datafile ‘/u01/app/oracle/oradata/DG112/datafile/users01.dbf’ for corruption at rdba: 0x01000083 (file 4, block 131)
Reread (file 4, block 131) found same corrupt data (no logical check)
Starting background process ABMR
Mon May 30 16:28:07 2011
ABMR started with pid=25, OS id=7137
Automatic block media recovery service is active.
Automatic block media recovery requested for (file# 4, block# 131)
Mon May 30 16:28:08 2011
Automatic block media recovery successful for (file# 4, block# 131)
Automatic block media recovery successful for (file# 4, block# 131)
WARNING: AutoBMR fixed mismatched on-disk single block 400003 with in-mem rdba 1000083.
[/perl]

うーん、素晴らしい!!
ABR の確認が出来たので、個人的にはもう満足してしまいましたが、折角 Active Data Guard の環境を作ったので他にも検証しないと・・・
次回、何しようかな。。。

[DBチューニングコンテスト とんがりナレッジ] Arch Linux で勝負してみた

$
0
0

9/25(金)に社内Oracleのチューニングコンテスト決勝戦が開催されました。
チューニングコンテストのルールは予算10万円でマシンを構築し、予選でTPC-Cのnumber of warehouses=1,sessio=20を規定時間以内に終了させれば決勝進出という内容で、決勝戦はTPC-Hのscale factor=10,session=4の実行終了までの時間を競いました。
細かなチューニングをせずに勝負に挑み2位という結果でした。
その要因の1つがマシンスペックの差によるものです。
これはCPU,メモリ,マザーボードをオーバークロック向きの構成でまとめたことが大きかったように思います。

そしてもう1つの要因となったのがOSでした。
OSを選択する上でこだわったのはシンプルであるということです。
いろいろ検討する中で見つけたのはがArch LinuxというLinuxのディストリビューションでした。
Arch Linuxは「シンプリシティ」、ミニマリズム、エレガンスさ、コードの正しさに焦点を当てて開発されている軽量なLinuxでARM向けのイメージ用意されており、Raspberry PiのOSとしても知られています。
ただし、シンプルさゆえにインストーラは存在せずLive CDを起動してもコンソールが立ち上がるのみといった非常に潔い構成となっています。
検証のため、OSとOracleをインストールした直後にTPC-Hのscale factor=10,session=4でCentOSと比較してみましたが1.17倍ほどArch Linuxの方が速いという結果となりました。

環境構築にあたりUEFIブート、Fake RAIDでディスクを構築しましたが情報がまとまっているサイトがなく苦労しましたのでOracleの導入までの手順を紹介したいと思います。

 

  1. Arch Linux導入

    • インストールメディアの準備次のいずれかからダウンロードしてください。
      ローリングアップデートを採用しているためバージョンではなく日付でインストールメディアは管理されています。
      http://ftp.jaist.ac.jp/pub/Linux/ArchLinux/iso
      http://ftp.tsukuba.wide.ad.jp/Linux/archlinux/iso
    • キーボードレイアウト変更
      インストール作業用にキーボードレイアウトを変更します。
      英語キーボードで作業する場合は不要です。
      # loadkeys jp106
    • ネットワーク接続の確立
      ip aもしくはifconifg

      ipが割り当てられていることを確認。
      リポジトリからパッケージをダウンロードすることになりますので必ずネットワークは確立してください。
      ※インストール後にifconfigを使用したい場合はnet-toolsの導入が必要です。

      # ping -c 3 www.google.com
    • sshの設定
      この設定はLive CDから起動した状態でsshを使用したい場合の設定です。
      インストール後のssh設定は別途必要になります。
      以降の設定をコピー&ペーストで実行したい場合は設定してください。
      # systemctl start sshd
      # passwd "パスワード"
    • パーティション作成
      まずはRAIDの構成を確認します。
      # ls -l /dev/md/*

      raid
      デュアルブートにするためにFakeRaidのRAID0で2つRAID0_VOL1,RAID0_VOL1のラベル名で用意しています。
      今回はArch Linuxには/dev/md/RAID0_VOL1_0を使用します。

      それではパーティションを作成していきます。
      最低限必要となるパーティションは3つあります。

      1. EFI System Partition
      2. Swap Partition
      3. root Partition

      Arch linuxを紹介しているサイトではfdisk,cgdiskなどを使用していますが今回はgdiskを使用します。

      # cgdisk /dev/md/RAID0_VOL1_0

      1. EFI System Partition

       Command (? for help): n
       Partition number (1-128, default 1): 
       First sector (34-500129758, default = 2048) or {+-}size{KMGTP}:
       Last sector (2048-500129758, default = 500129758) or {+-}size{KMGTP}: +512M
       Current type is 'Linux filesystem'
       Hex code or GUID (L to show codes, Enter = 8300): EF00
       Changed type of partition to 'EFI System'

      2. Swap Partition

       Command (? for help): n
       Partition number (2-128, default 2): 
       First sector (34-500129758, default = 1050624) or {+-}size{KMGTP}:
       Last sector (1050624-500129758, default = 500129758) or {+-}size{KMGTP}: +16G
       Current type is 'Linux filesystem'
       Hex code or GUID (L to show codes, Enter = 8300): 8200
       Changed type of partition to 'Linux swap'

      3. root Partition

       Command (? for help): n
       Partition number (3-128, default 3):
       First sector (34-500129758, default = 34605056) or {+-}size{KMGTP}:
       Last sector (34605056-500129758, default = 500129758) or {+-}size{KMGTP}:
       Current type is 'Linux filesystem'
       Hex code or GUID (L to show codes, Enter = 8300):
       Changed type of partition to 'Linux filesystem'

      最後に設定を書き込んで終了です。

       Command (? for help): w
       
       Final checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING
       PARTITIONS!!
       
       Do you want to proceed? (Y/N): Y
       OK; writing new GUID partition table (GPT) to /dev/md/RAID0_VOL1_0.
       The operation has completed successfully.

      念のためgdiskで構成を確認します。
      partition

      続いて作成されたパーティションを確認します。
      dev_partition

       

    • パーティションのフォーマット
      1. EFI System Partition
      # mkfs.vfat -F32 /dev/md/RAID0_VOL1_0p1

      2. Swap Partition

      # mkswap /dev/md/RAID0_VOL1_0p2

      3. root Partition
      今回はext4でフォーマットします。
      xfsなど好みのファイルシステムでフォーマットしてください。

      # mkfs.ext4 /dev/md/RAID0_VOL1_0p3
    • パーティションのマウント
      rootパーティションにEFI System Partitionをブートするため先ほどまでと逆順に進めます。
      3. root Partition
      # mount /dev/md/RAID0_VOL1_0p3 /mnt

      2. Swap Partition

      # swapon /dev/md/RAID0_VOL1_0p2

      1. EFI System Partition

      # mkdir /mnt/boot
      # mount /dev/md/RAID0_VOL1_0p1 /mnt/boot
    • インストール
      ミラーリストの設定
      # vi /etc/pacman.d/mirrorlist

      次の2つがファイルの先頭に来るように移動してください。
      Japanで検索するとすぐに見つかります。

      ## Score: 2.0, Japan
      Server = http://ftp.jaist.ac.jp/pub/Linux/ArchLinux/$repo/os/$arch
      ## Score: 2.2, Japan
      Server = http://ftp.tsukuba.wide.ad.jp/Linux/archlinux/$repo/os/$arch

      以下のように変更してください。
      mirrorlist
      マウントしたrootパーティションにシステムをインストールします。

      # pacstrap -i /mnt base base-devel

      fstabの作成
      EFI System Partition,Swap Paritition,root,Parition以外にもマウントする場合は先にマウントしておくか、
      あとでfstabを編集してください。

      # genfstab -U -p /mnt >> /mnt/etc/fstab

      作成されたfstabを確認してみます。

      # view /mnt/etc/fstab
      #
      # /etc/fstab: static file system information
      #
      # <file system> <dir>   <type>  <options>       <dump>  <pass>
      # /dev/md124p3
      UUID=95c9a107-353a-4b45-beaa-6a10c50d4aa6       /               ext4            rw,relatime,stripe=8,data=ordered       0 1
      
      # /dev/md124p1
      UUID=DD31-1D98          /boot           vfat            rw,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro    0 2
      
      # /dev/md124p2
      UUID=a06a4d39-4a1a-4929-bfb3-f820b4790c5e       none            swap            defaults        0 0
    • Arch Linuxの設定
      以降はインストールしたシステム上で行います。
      次のコマンドでシステムに移動できます。
      # arch-chroot /mnt /bin/bashインストール漏れなどがあってLive CDから起動しなおす場合はマウントとこのコマンドを実行してください。

      ロケール設定

      # vi /etc/locale.gen

      今回はUTF-8で構築するので次の2つの行のコメントをはずします。

      en_US.UTF-8 UTF-8
      ja_JP.UTF-8 UTF-8

      次のコマンドを実行します。

      # locale-gen

      以下のように出力されれば完了です。

      Generating locales...
      en_US.UTF-8... done
      ja_JP.UTF-8... done
      Generation complete.

      次にlocale.confを設定します。

      # echo LANG=en_US.UTF-8 > /etc/locale.conf
      # export LANG=en_US.UTF-8
    • コンソールフォントとキーボードレイアウトの設定
      コンソールの設定なのでgnome,xfce4などは別途設定が必要になります。
      # vi /etc/vconsole.conf
      KEYMAP=jp106
      FONT=Lat2-Terminus16
    • ロケーション設定
      # ln -s /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
    • システムクロック設定
      UTCで設定します。
      # hwclock -u -w
    • ホスト名の設定
       # echo "ホスト名" > /etc/hostname

      Oracleをインストールするので/etc/hostsにもホスト名を追加しておいてください。

    • ネットワーク設定
      以下はdhcpを利用する場合です。
      # systemctl enable dhcpcd.service

      固定IPの場合はnetctlで設定してください。

    • rootパスワード設定
      パスワードの設定を忘れるとログインできなくなるので必ず実施してください。
      # passwd
    • ブートローダーのインストール
      grubのインストール
      ※arch linuxではapt-get,apt-cacheやyumではなくパッケージマネージャーとしてpackmanを使用します。
      # pacman -S grub dosfstools efibootmgr
      # grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=arch_grub --recheck --debug
      # grub-mkconfig -o /boot/grub/grub.cfg

      /boot/EFI/bootにもブータブルスタブを作成。

      # mkdir /boot/EFI/boot
      # cp /boot/EFI/arch_grub/grubx64.efi  /boot/EFI/boot/bootx64.efi
    • raidディスクの読み込み設定
      raidの構成を設定ファイルに書き出す。
      # mdadm --examine --scan > /mnt/etc/mdadm.conf
      # vi /etc/mkinitcpio.conf

      HOOKS行にmdadm,mdadm_udevを追加

      HOOKS="base udev mdadm_udev autodetect modconf block mdadm filesystems keyboard fsck"
      # mkinitcpio -p linux

      このときにmdadmモジュールとmdadm_udevモジュールをブートイメージに追加します。
      mdadmの追加時にmdadm.confが読み込まれますので変更があった場合はmkinitcpioを再実行してください。

      ここまででインストール作業は終了です。
      sshdもインストール環境にはインストールされていませんので必要な場合はopensshを導入してください。

    • 起動確認
      まずはシステムを移動します。
      # exit

      後は再起動してください。

      # reboot
      
      
  2. GUI環境の導入

    • ビデオドライバーの確認
      次のコマンドで必要となるドライバを確認してください
      # lspci | grep VGA

      core i7 4790Kの場合は次のようになります。

       00:02.0 VGA compatible controller: Intel Corporation Xeon E3-1200 v3/4th Gen Core Processor Integrated Graphics Controller (rev 06)

      intelのドライバを検索します。

      # pacman -Ss video intel

      パッケージが2つ見つかりましたが”(xorg-drivers xorg)”と記載されている方が必要になります

      extra/xf86-video-i740 1.3.4-6
          X.org Intel i740 video driver
      extra/xf86-video-intel 1:2.99.917+381+g5772556-1 (xorg-drivers xorg)
          X.org Intel i810/i830/i915/945G/G965+ video drivers

      対象のドライバーをインストールします。

      # pacman -S xf86-video-intel
    • X関連パッケージの導入
      # pacman -S xorg-server xorg-server-utils xorg-xinit xterm
    • gnome関連パッケージの導入
      # pacman -S gnome gnome-extra

      ディスプレイマネージャを有効化します。

      # systemctl enable gdm.service

      localectlでX環境でのキーボード設定ファイルを作成します。

      # localectl set-x11-keymap jp,us pc104 ,dvorak grp:alt_shift_toggle

      作成された設定ファイルを確認します。

      # view /etc/X11/xorg.conf.d/00-keyboard.conf

      次のような内容で作成されるはずです。

      # Read and parsed by systemd-localed. It's probably wise not to edit this file
      # manually too freely.
      Section "InputClass"
              Identifier "system-keyboard"
              MatchIsKeyboard "on"
              Option "XkbLayout" "jp,us"
              Option "XkbModel" "pc104"
              Option "XkbVariant" ",dvorak"
              Option "XkbOptions" "grp:alt_shift_toggle"
      EndSection
      

      GNOMEの日本語化は今回は特に必要がないので省略します。

  3. oracleのインストール

    • ユーザー作成
      # groupadd oinstall
      # groupadd dba
      # useradd -g oinstall -G dba oracle
      # passwd oracle

      ホームディレクトリが自動で作成されないので手動で作成します。

      # mkdir /home/oracle
      # chown oracle:oinstall /home/oracle
      # chmod 775 /home/oracle
    • ulimitの設定
      # vi /etc/security/limits.conf
      oracle soft nproc 2047
      oracle hard nproc 16384
      oracle soft nofile 1024
      oracle hard nofile 65536
      oracle soft stack 10240
      oracle hard stack 32768
    • カーネルパラメータの設定
      カーネルパラメータは/etc/sysctl.confではなく/etc/sysctl.d/99-sysctl.confに設定します。
      # vi /etc/sysctl.d/99-sysctl.conf
      kernel.sem = 250 32000 100 128
      fs.file-max = 6815744
      net.ipv4.ip_local_port_range = 9000 65500
      net.core.rmem_default = 262144
      net.core.rmem_max = 4194304
      net.core.wmem_default = 262144
      net.core.wmem_max = 1048576
      fs.aio-max-nr = 1048576
    • 必要パッケージのインストール
      # pacman -S base-devel elfutils libaio unixodbc sysstat pdksh icu gdb make  binutils expat libstdc++5
    • シンボリックリンク作成
      ※ 既に作成されている場合があります。
      # ln -s /usr/bin/basename /bin/basename
      # ln -s /usr/bin/tr /bin/tr
      # ln -s /usr/lib/libgcc_s.so.1 /lib/libgcc_s.so.1
    • oracleのインストールについては
      手順は通常通りのインストール手順になりますので省略します。
      私はOracle 11.2.0.3をインストールしました。
      12cについては調べた限りではインストールの成功例は見つかりませんでした。
      途中でダイアログがちゃんと表示されなかったり、ライブラリが不足していたりでエラーが出ますがインストールは最後までできます。
      以前はGUIとしてXfce4で構築したのですがそのときにもウィンドウの最大化が出来ないなどの問題が発生してたこともありGUI周りはどうも挙動が怪しいがしてなりません。
      ローリング・リリースで日々アップデートされているOSなので今後に期待です。

[DBチューニングコンテスト とんがりナレッジ] インメモリ、非圧縮、パラレル をキーワードに・・・

$
0
0

ペンネーム:ブロンズバード
●TPCH
【チューニング】
インメモリ、非圧縮、パラレルの3つをキーワードに考え、以下の設定を行った。

/*TPCCとの共通設定*/
alter system set log_checkpoint_timeout=0;
alter system set db_block_checksum=OFF;

/*パラレルクエリの設定*/
alter system set parallel_degree_policy=AUTO scope=both;
alter system set parallel_min_time_threshold=AUTO;

/*インメモリ・非圧縮の設定*/
alter system set inmemory_size = 14G scope=spfile;
alter system set inmemory_clause_default = 'INMEMORY NO MEMCOMPRESS PRIORITY CRITICAL';
alter system set pga_aggregate_limit = 12G

/*resultキャッシュの設定*/
alter system set result_cache_mode = FORCE;
alter system set result_cache_max_size = 8G;
alter system set result_cache_max_result = 4;

【実行後の確認】
(1)時間のかかっているWaitEventを確認する。

SQL>select event, count(*) cnt, sum(TIME_WAITED) wt from v$active_session_history group by event order by wt, cnt;

EVENT					CNT	WT
--------------------------------------- ------- ----------
os thread creation	 		 1	      0
control file heartbeat			 2	      0
null event				 4	      0
cursor: pin S				 1	   1056
control file sequential read		 1	   3990
control file parallel write		 1	   7439
db file async I/O submit		 1	  13357
read by other session		 	 3	  16720
db file sequential read			15	  44308
PX qref latch				35	  50615
direct path write temp			17	 128531
oracle thread bootstrap			 4	 235306
direct path read			88	 456384
db file scattered read			90	 516003
external table read			 1	 918773
IM populate completion			 3	2998241

結果、一番時間がかかっているWaitEventが「IM populate completion」で3秒。(WTはマイクロ秒)
#「IM populate completion」はメモリにデータを移入している際のWaitEvent。
そのため、大きく時間がかかっているWaitEventも無く、この情報からチューニングできるものはないと判断した。

(2)インメモリで全て処理したかを確認する。

SQL> select segment_name, inmemory_size, BYTES, BYTES_NOT_POPULATED, populate_status from v$im_segments;

SEGMENT_NAME	INMEMORY_SIZE	BYTES		BYTES_NOT_POPULATED 	POPULATE_STATUS
--------------- --------------- --------------- ----------------------- ---------------
SUPPLIER	14811136	16777216	0 			COMPLETED
PARTSUPP	1385037824 	1543503872	0 			COMPLETED
PART		278003712  	352321536	0 			COMPLETED
ORDERS		1758527488 	1879048192	0 			COMPLETED
CUSTOMER	248250368  	260046848	0 			COMPLETED
LINEITEM	7497056256 	8589934592	0 			COMPLETED

上記の「SEGMENT_NAME」で表示されているTPCHのテーブルは「BYTES_NOT_POPULATED」が0のため、全てインメモリになった。
TPCHには上記以外に2つのテーブル(REGION,NATION)があるが、インメモリになっていない。
しかし、この2つのテーブルはサイズが小さい(64KB未満)ため、インメモリにならない。
なので、(できる範囲で)インメモリで全て処理できたと判断した。

http://www.oracle.com/technetwork/jp/database/in-memory/overview/twp-oracle-database-in-memory-2245633-ja.pdf
→「64KB未満のオブジェクトはメモリに移入されません。メモリが1MBのチャンクに割り当てられると、IM列ストア内の大量の領域が無駄に消費されるためです。」

(3)インメモリのサイズは適切かを確認する。

SQL>select * from v$inmemory_area;

POOL 		ALLOC_BYTES	USED_BYTES	POPULATE_STATUS	CON_ID
----------- ------------------- --------------- --------------- ----------- 
1MB POOL 	1.2010E+10 	1.1245E+10 	DONE 		0
64KB POOL 	3003121664 	53608448 	DONE 		0

USED_BYTESの合計は、約10.5GB(1.1245E+10 + 53608448)程度であった。
そのため、以下のSQLでも使用しているByte数を確認する。
#インメモリPOOLのサイズが1MBと64KBの2種類しか存在しないため、
#先の「64KB未満のオブジェクトがメモリに移入されない」という理由がわかる。

SQL> select sum(inmemory_size), sum(BYTES) from v$im_segments;

SUM(INMEMORY_SIZE) 	SUM(BYTES)
----------------------- --------------
1.1182E+10 		1.2642E+10

結果、インメモリのサイズは現状の14GBではなく13GBでも大丈夫そうだが、
実際に13GBにすると、(2)でのSQLで「BYTES_NOT_POPULATED」が全て0とはならなかった。
(原因は時間切れで調査できず・・・)
なので、このまま変更せずに14GBとした。

[DBチューニングコンテスト とんがりナレッジ] 優勝への道① ハードウェア選定+OS構築編

$
0
0

2015/9/25に行われた、社内DBチューニングコンテストに優勝しました!!
今回はその時の話を書いていきます。

まず、競争条件について。
最初に聞いた条件は以下の通りでした。

【競争条件】
 <測定ツール>
 - HammerDBを使用
 <決勝>
  (1) TPC-H 4セッション(Scale Factor=10 )
 <予選>
  (1) TPC-C 20セッション(1セッションごとに100万トランザクション)
  (2) TPC-H 4セッション(Scale Factor=1)
  上記の合計24セッションが終わった時間で競う。(同時性は問わない)

 <パーツ条件>
  ・予算 10万円以内 ※ただし、超えた場合は自己負担も可
  ・電源(中古品禁止) ※Bronze以上
  ・マザーボード(中古品禁止) 
 <禁止事項>
  ・作成されるデータはいじってはいけない
  ・DB起動時にデータをメモリ上に乗せてはいけない

予算10万円でどんなPCが組めるか??

構成案としては下記で考えました。

【構成案】
  1. Memory : 全データがメモリに乗り(32GB以上)、なるべく高速なもの( DDR-3以上)
  2. CPU : なるべく性能のいいもの
  3. Disk : なるべく高速なディスク (SSD、容量 : 100GB以上)
  4. マザーボード : 上記性能を発揮できて、なるべく安いもの
  5. 電源 : なるべく安いもの
  ※数字は優先順位

メモリはそこまで悩まずに価格感だけ確認して、最初に悩んだのはCPUでした。

PassMarkを参考にしたところ、二つの選択肢が出てきました。

<CPUの選択肢>
・Interl Core i7 4790K (Average CPU Mark : 11,237)
・AMD FX-9590 (Average CPU Mark : 10,282)

それぞれCPUについて調べていると、AMD FX-9590が爆熱で空冷だと厳しいとの噂を確認しました。
空冷の代わりに、水冷を使って冷やすとのこと。

水冷使ったことないし、面白そう!!

結果、水冷にしてみたいという単純な好奇心が勝ってしまいました。(後で後悔しましたが。。。)

ディスクは、本当に速度が出るものを選びたかったため、
ベンチマークサイトに載っているシーケンシャルリードが500MB以上のものという条件で探しました。

マザーボードは対応している記載はなかったんですが、
「Socket AM3+ / AM3 」の記載があるし大丈夫だろうと安易な考えで選んでしまいました。
※対応していない結果、ファームを最新に上げると起動できなくなるという問題にはまってかなり焦りました。。。

最後に電源は、電源容量だけ気にしただけだったので、そこまで悩みませんでした。
※600Wにしたのは、AMD FX-9590の TDPが220Wだったので少し余裕を見た結果です。

以下が、最終的に決定したPCの構成です。

【構成パーツ】
  CPU : AMD FX 8-Core Black Edition FX-9590 水冷クーラー付属
  MEMORY : F3-1866C10Q-32GZM
  SSD : SDSSDHII-120G-J25
  マザーボード : 990FX Killer
  電源 : KEIAN BullMAX ATX電源 600W 80PLUS BRONZE取得 KT-F600-12A

また、OS関連は下記で構成しました。

【OS+DB】
 OS : Oracle Linux Server release 7.1
 Oracle : 12.1.0.2.0
 ※理由 : 基本的に最新のもののほうが速いため

 <Disk性能と設定>
  RAID : RAID0
  ファイルシステム : ext4 : ブロックサイズ : 32KB

ちなみに、Diskの速度を測定したところ、想定通り、1GB出ているので問題ありませんでした。

<iostatの結果>
 Device: tps      MB_read/s MB_wrtn/s MB_read MB_wrtn
 sda     4213.00     526.62      0.00     526       0
 sdb     4215.00     526.75      0.01     526       0
 dm-0    8432.00    1053.50      0.01    1053       0

あとは、下記のカーネルパラメータを変更しておきました。

 vm.swappiness = 0
 ※予期せぬSWAPを防ぐため
【PCパーツ選定時の間違いポイント】
TPC-C(warehouse:1)は、チューニングすればするほど、1行の行ロックを全セッション(CPUコア)が待つ状態になる、CPUネックの状態になりました。
メモリやディスクの待ちがほぼない状態にした後で、行ロックをいかに速く外すかを考えた時、結局シングルコアの速度差が効いているように感じました。
なので、実はシングルコアの速度も気にしないといけませんでした。

 <シングルコアの速度>
  ・Interl Core i7 4790K (Single Thread Rating : 2,531)
  ・AMD FX-9590 (Single Thread Rating : 1,724)

【コンテスト終了後】
Interl 4790K環境にて同等の設定を入れた結果、TPC-Cの実行時間は、ほぼSingle Thread Ratingのスコア通りの違いになりました。
TPC-Hでも同様の確認をしましたが、Average CPU Mark通りの結果差でした。
TPC-Cの場合、warehouseの行数がコア数未満の場合は、シングルコアの速度を気にしていないと痛い目を見るという簡単な考慮漏れに深く反省しました。

TPCC編に続く
 

[DBチューニングコンテスト とんがりナレッジ] 優勝への道② TPC-C チューニング編

$
0
0

今度は予選で実施したTPC-Cについて書いていきます。

競争条件については、下記の通りです。

<予選>
 (1) TPC-C 20セッション(1セッションごとに100万トランザクション)
 (2) TPC-H 4セッション(Scale Factor=1)
 ※TPC-Hのチューニングについてはここでは割愛します。

まずは、事前準備で実施した内容から。

【HammerDBの問題対応】
 <tpmを確認するため>
  grant select on sys.user$ to system;

 <WAREHOUSEのW_YTDが桁溢れするため>
  alter table WAREHOUSE modify W_YTD NUMBER(24,2);

 
それでは、本題です。

以下の優先順位でチューニング実施し、CPUネック(待ちもない状態)の状態にしていくことを考えました。

【チューニングの優先順位】
  1. Diskアクセスを減らす
  2. 減らせないものは、Diskからのアクセスを高速にする
  3. Memoryアクセスを減らす
  4. 減らせないものは、Memoryからのアクセスを高速にする
  5. CPUリソースを無駄に使用している処理を減らす
  6. 減らせない処理は、CPUリソースの使用方法を効率化する
  ※ネットワークは今回は関係ないため省略しています。

 
まずはDiskアクセスについてです。
TPC-CでDiskアクセスが多い個所と考えた時、真っ先に気になるのは、やはりREDOです。
また、REDO生成量(更新量)が多いということは、UNDOも多いはずです。
まずは、そこをポイントにチューニングしていきます。
※チューニング前後のベンチマーク結果は記載できないため、代わりに効果(高,中,低)という表現で記載することをご了承ください。

【REDOログの高速化】
まずは、REDOログです。
REDOログの書き込みを高速化する上で、大きく分けて方法は2パターンあります。
1. 書き込み量自体を減らす
2. 書き込み速度を向上させる

では、それぞれに対し実施した方法です。
1. 書き込み量自体を減らす
今回は、正攻法と邪道な方法の2つを考えました。

<実施内容>
 a. まともなやり方でREDOログ生成量を減らす。(効果:低)
 b. 邪道なやり方でREDOログ生成量を減らす。(効果:高)

a. まともなやり方でREDOログ生成量を減らす。(効果:低)
こちらは、12cからの新機能のTemporay Undoを使用しました。

<パラメータ変更内容>
 TEMP_UNDO_ENABLED=TRUE

b. 邪道なやり方でREDOログ生成量を減らす。(効果:高)
どう考えても、通常運用に耐えられませんが、REDOログ生成量を減らすとともに、COMMIT待ちも失くしました。

<変更パラメータ>
 _disable_logging=true
 commit_logging=batch
 commit_wait=nowait

2. 書き込み速度を向上させる
以下の2つを考えました。

<実施内容>
 a. REDOログをRAMディスクに配置する。(効果:高)
 b. 一度の書き込みを多くし、書き込み速度を高速化する。(効果:低)

a. REDOログをRAMディスクに配置する。(効果:高)
REDOログの生成量を減らし、待ちを失くした結果、RAMディスクにREDOログを配置した場合でも速度差が変わらない状況になりました。
そのため、メモリリソース節約のために、REDOログはRAMディスクには配置しませんでした。
※通常の状態でRAMディスクに配置した場合、

b. 一度の書き込みを多くし、書き込み速度を高速化する。(効果:低)
ログバッファやチェックポイント間隔を調整し、少しでも一度の書き込みを多くしようと考えました。

<変更パラメータ>
 log_checkpoint_interval=2147483647
 log_checkpoint_timeout=2147483647
 log_buffer=2147483648

【UNDOの高速化】
次にUNDOです。
UNDO自体は、REDOログとは違い、(テーブル構成やSQLを変えられない条件では)DBの性質上減らそうにも減らせません。
そのため、UNDOをRAM上に配置しました。

1. UNDOのRAMディスクへの配置(効果:中)

<実行コマンド>
 CREATE UNDO TABLESPACE RAMUNDO DATAFILE '/dev/shm/ramundo.dbf' SIZE 1G;

<変更パラメータ>
 undo_tablespace='RAMUNDO'
 undo_retention=5
 ※UNDO表領域のサイズ調整のため、undo_retentionも同時に変更しています。

【データファイルやオブジェクトの事前拡張】
100万トランザクション * 20セッション = 2000万トランザクション
のデータがどれくらいのサイズになるか不明だったため、今回の環境ではデータファイルのリサイズが発生していました。
リサイズが発生し始めたタイミングでパフォーマンスが落ち始めるため、事前にデータファイルを十分なサイズで作成し直しました。
また、合わせてオブジェクトも事前に拡張しました。

<実施内容>
 1. データファイルのリサイズ(効果:中)
 2. オブジェクトの事前拡張(効果:低)

1. データファイルのリサイズ(効果:中)

<実行コマンド>
 CREATE BIGFILE TABLESPACE "TPCC1" LOGGING DATAFILE '/opt/oracle/base/oradata/tpcc1/tpcc1.dbf' 
 SIZE 16384M REUSE AUTOEXTEND ON NEXT 100M MAXSIZE UNLIMITED 
 EXTENT MANAGEMENT LOCAL SEGMENT SPACE MANAGEMENT AUTO;
 ※実際には最初に作成したため、ALTERではなく、CREATEになっています。

2. オブジェクトの事前拡張(効果:低)

<オブジェクトの拡張>
 alter table tpcc1.order_line allocate extent ( SIZE 12G ) ;
 alter table tpcc1.history allocate extent ( SIZE 1G ) ;
 alter table tpcc1.orders allocate extent ( SIZE 500M ) ;
 alter index tpcc1.orders_i1 rebuild storage (INITIAL 500M);
 alter index tpcc1.orders_i2 rebuild storage (INITIAL 500M);

【制御ファイルのIO高速化】
冗長化ポイントは、性能面のみ考えた場合は無駄なIOとなってしまうため、制御ファイルは1つにしました。

1. 制御ファイルの削除(効果:低)

<パラメータ変更内容>
 control_files='/opt/oracle/base/oradata/orcl/control01.ctl'

【データファイルのIO高速化】
少しでも、データファイルへの書き込みを速くするため、DB Writerの数を調整しました。

<パラメータ変更内容>
 DB_WRITER_PROCESSES=8

【便利機能の停止】
便利な機能は、内部で更新等が発生しているため、それなりに負荷をかけています。
運用面を無視して速度のみを考えた場合はやはり停止したほうが速くなります。
実際に実施した内容は、以下の3つです。

<実施内容>
 1. 統計収集の停止(効果:中)
 2. 内部チェック、アドバイザ系、便利な機能の停止(効果:低)
 3.ダイナミック・サンプリングの無効化(効果なし)

1. 統計収集の停止(効果:中)
便利な分だけ、やはりかなり効果がありました。
ただし、チューニングに必要になる情報がほぼ見れなくなるため、設定は最後に実施しました。

<変更パラメータ>
 timed_statistics=false
 statistics_level=BASIC

2. 内部チェック、アドバイザ系、便利な機能の停止(効果:低)
こちらは、思ったほど効果がありませんでした。

<パラメータ変更内容>
 DB_BLOCK_CHECKING=FALSE
 DB_BLOCK_CHECKSUM=FALSE
 TRACE_ENABLED=FALSE
 _db_mttr_advice=OFF
 _library_cache_advice=FALSE
 _smm_advice_enabled=FALSE
 _smm_advice_enabled=FALSE
 db_cache_advice=OFF
 REPLICATION_DEPENDENCY_TRACKING=FALSE

3.ダイナミック・サンプリングの無効化(効果なし)
今回は、統計情報を事前に収集しているため、特に効果がありませんでしたが、統計情報を収集していない環境では問題になる場合もあるため、記載しておきます。

<パラメータ変更内容>
 optimizer_dynamic_sampling=0

【SGAの調整】
影響度的には一番大きいですが、チューニングポイントというには微妙なので、記載しておきます。
また、Diskとは関係ありませんが、SGA関連としてshared_pool_sizeの設定値も記載します。

<設定パラメータ>
 db_cache_size=15032385536
 shared_pool_size=1073741824
 ※それぞれ、TPC-Cを実行する上で必要な分を設定しました。

ここまでで、大分Diskネックの状態は解消できました。
 
次は、メモリアクセスについて考えます。

【ブロックサイズの調整】
実際、ここはそこまで手を出さなかった箇所でした。
そもそも、実際に実行されているSQL自体も最速の実行計画で実行されていたため、indexと行とで2ブロックのアクセスで完結しているSQLだけでもどうにかできないか考えました。

1. 毎回、2ブロックしかアクセスされないオブジェクトを2KBのブロックサイズに変更(効果:低)
DB自体は32KBのブロックサイズにしていたため(TPC-H環境と同居のため)、もう少し効果を期待したかったのですが、あまりありませんでした。

<実行コマンド>
 CREATE BIGFILE TABLESPACE "TPCC1_2" LOGGING DATAFILE '/opt/oracle/base/oradata/tpcc1/tpcc1_2.dbf' 
 SIZE 100M REUSE AUTOEXTEND ON NEXT 100M MAXSIZE UNLIMITED 
 EXTENT MANAGEMENT LOCAL SEGMENT SPACE MANAGEMENT AUTO blocksize 2048;
 alter table warehouse move tablespace tpcc1_2;
 alter table district move tablespace tpcc1_2;
 alter index warehouse_i1 rebuild tablespace tpcc1_2;
 alter index district_i1 rebuild tablespace tpcc1_2;
 alter table tpcc1.customer move tablespace tpcc1_2;
 alter index tpcc1.customer_i1 rebuild tablespace tpcc1_2;
 alter index tpcc1.customer_i2 rebuild tablespace tpcc1_2;

<変更パラメータ>
 db_2k_cache_size=104857600

 
最後にCPUです。

【CPUリソースの効率化】
とにかく、無駄なオーバーヘッドや待ちを減らすことを考えました。
結果、実施したのは以下の3つです。

<実施内容>
 1. hugepageの設定(効果:中)
 2. PL/SQLの高速化(効果:低)
 3. latchの調整(効果:低)

1. hugepageの設定(効果:中)
hugepageを使用することで、ページ表(仮想メモリーから物理メモリーへのマッピング)は小さくなります。
ページ表を最新状態に保つオーバヘッドがなくなることで、CPUネックになる環境では効果があります。

<変更パラメータ(kernelパラメータ)>
  vm.nr_hugepages = 7000

2. PL/SQLの高速化(効果:低)
HammerDBの処理方式を確認していたところ、PL/SQLを使用して処理が行われていました。
そのため、HammerDBで作成されるプロシージャや乱数生成などのプロシージャの負荷を下げるために実施しました。

<実行内容>
 startup upgrade
 @$ORACLE_HOME/rdbms/admin/dbmsupgnv.sql
 shutdown
 startup
 @$ORACLE_HOME/rdbms/admin/utlrp.sql
 alter procedure tpcc1.neword compile plsql_code_type=native reuse settings;
 alter procedure tpcc1.delivery compile plsql_code_type=native reuse settings;
 alter procedure tpcc1.payment compile plsql_code_type=native reuse settings;
 alter procedure tpcc1.ostat compile plsql_code_type=native reuse settings;
 alter procedure tpcc1.slev compile plsql_code_type=native reuse settings;

<パラメータ変更内容>
 PLSQL_CODE_TYPE=NATIVE
 plsql_optimize_level=3

3. latchの調整(効果:低)
他にチューニングポイントはないかと待機イベントを眺めていたところ、latchが少し出ていたため、調整を行いました。
発生自体少しだっため、やはり効果は少なかったです。

<パラメータ変更内容>
 _db_block_hash_latches=32768
 _kgl_latch_count=9

 
【おまけ:プロセスの優先度の調整(効果:なし)】
DBWやLGWRなど、主にCPUを使用していたプロセスの優先度を調整して、更にパフォーマンス向上を目指せるかと思ったのですが、効果は出ませんでした。
むしろ、設定によっては、悪化している気配さえあり、少し検討に時間がかかりそうだったため、実施できませんでした。

<変更検討パラメータ>
 _high_priority_processes
 _highest_priority_processes

以上がTPC-Cで実施した内容となります。

TPC-H編に続く
 

[DBチューニングコンテスト とんがりナレッジ] 優勝への道③ TPC-H チューニング編

$
0
0

最後に、決勝で実施したTPC-Hについてです。

競争条件は、下記の通りです。

<決勝>
 (1) TPC-H 4セッション(Scale Factor=10 )

まずは、事前準備から。

表領域は特に意識せずに作成しました。

<実行コマンド>
 CREATE BIGFILE TABLESPACE "TPCH10" LOGGING DATAFILE '/opt/oracle/base/oradata/tpch10/tpch10.dbf' SIZE 30720M REUSE 
 AUTOEXTEND ON NEXT 100M MAXSIZE UNLIMITED 
 EXTENT MANAGEMENT LOCAL SEGMENT SPACE MANAGEMENT AUTO ;
 CREATE BIGFILE TABLESPACE "TPCH10_LINEITEM" LOGGING DATAFILE '/opt/oracle/base/oradata/tpch10/tpch10_lineitme.dbf' SIZE 4700M REUSE
 AUTOEXTEND ON NEXT 100M MAXSIZE UNLIMITED 
 EXTENT MANAGEMENT LOCAL SEGMENT SPACE MANAGEMENT AUTO;
 CREATE BIGFILE TABLESPACE "TPCH10_ORDERS" LOGGING DATAFILE '/opt/oracle/base/oradata/tpch10/tpch10_orders.dbf ' SIZE 1500M REUSE
 AUTOEXTEND ON NEXT 100M MAXSIZE UNLIMITED
 EXTENT MANAGEMENT LOCAL SEGMENT SPACE MANAGEMENT AUTO ;
※複数作ったのは、大きい表だけでもRAM上に配置しようかと考えたためです。(実際は、Diskネックにはならなかったため、RAM上には配置しませんでした。)

チューニングの優先順位は、TPC-Cとほぼ同じです。
※やり取りするデータサイズが増えることから、ネットワーク部分も考慮に入れました。

【チューニングの優先順位】
  1. Diskアクセスを減らす
  2. 減らせないものは、Diskからのアクセスを高速にする
  3. ネットワークはアクセスを減らす。
  4. 減らせないものは、ネットワークはアクセスを高速にする。
  4. Memoryアクセスを減らす
  5. 減らせないものは、Memoryからのアクセスを高速にする
  6. CPUリソースを無駄に使用している処理を減らす
  7. 減らせない処理は、CPUリソースの使用方法を効率化する

※TPC-C同様、チューニング前後のベンチマーク結果は記載できないため、代わりに効果(高,中,低)という表現で記載することをご了承ください。

TPC-Hと考えた時、まず最初に思いつくのは、
インメモリ化とパラレルクエリ、COMPRESSです。

【インメモリの使用】(効果:高)
一見、インメモリですべて処理したほうが速いように思いますが、実際はディスクから読み込んでポピュレート(カラム型フォーマットでのデータロード)する処理があるため、
今回の環境では、LINEITEMのみが一番速くなりました。
※当然、ポピュレート完了後のインメモリのみの処理では、すべて行っている状態のほうが速いです。

<実行コマンド>
 alter TABLE TPCH10.LINEITEM inmemory;
 alter TABLE TPCH10.ORDERS no inmemory;
 alter TABLE TPCH10.PART no inmemory;
 alter TABLE TPCH10.REGION no inmemory;
 alter TABLE TPCH10.NATION no inmemory;
 alter TABLE TPCH10.SUPPLIER no inmemory;
 alter TABLE TPCH10.CUSTOMER no inmemory;
 alter TABLE TPCH10.PARTSUPP no inmemory;

【テーブルのパーティション化(効果:高)とCOMPRESS(効果:中)】
パラレル・クエリで高速化を実現するために、大きいテーブルを対象に、パーティション化しました。
合わせて、ディスク読み込み量を減らすため、COMPRESSしています。
ただし、COMPRESSはLINEITEN以外では、効果がほとんどない状態でCPUリソースを無駄に使用してしまっていたため、LINEITEMのみ実施しました。
また、パーティション・キーは、HammerDBのアクセスが年や月単位のアクセスが比較的多かったため、
1ヶ月から1年の間隔の中で、どのクエリもなるべく速くなる状態を目指し調整しました。
※参考値取得のために、日単位も実行したのですが、その場合、信じられないくらい遅くなりました。。。
(パラレルクエリの性質上当然と言えば当然ですが)

<実行コマンド>
 create table TPCH10.lineitem
 inmemory
 COMPRESS FOR DIRECT_LOAD OPERATIONS
 pctfree 1
 pctused 99
 initrans 10
 parallel 2
 nologging
 tablespace TPCH10_LINEITEM
 partition by range (l_shipdate)
 (
 PARTITION P_920101 VALUES LESS THAN (TO_DATE('1992-01-01','YYYY-MM-DD')) TABLESPACE TPCH10_LINEITEM,
 PARTITION P_920401 VALUES LESS THAN (TO_DATE('1992-04-01','YYYY-MM-DD')) TABLESPACE TPCH10_LINEITEM,
 PARTITION P_920701 VALUES LESS THAN (TO_DATE('1992-07-01','YYYY-MM-DD')) TABLESPACE TPCH10_LINEITEM,
 PARTITION P_921001 VALUES LESS THAN (TO_DATE('1992-10-01','YYYY-MM-DD')) TABLESPACE TPCH10_LINEITEM,
 PARTITION P_930101 VALUES LESS THAN (TO_DATE('1993-01-01','YYYY-MM-DD')) TABLESPACE TPCH10_LINEITEM,
 PARTITION P_930401 VALUES LESS THAN (TO_DATE('1993-04-01','YYYY-MM-DD')) TABLESPACE TPCH10_LINEITEM,
 PARTITION P_930701 VALUES LESS THAN (TO_DATE('1993-07-01','YYYY-MM-DD')) TABLESPACE TPCH10_LINEITEM,
 PARTITION P_931001 VALUES LESS THAN (TO_DATE('1993-10-01','YYYY-MM-DD')) TABLESPACE TPCH10_LINEITEM,
 PARTITION P_940101 VALUES LESS THAN (TO_DATE('1994-01-01','YYYY-MM-DD')) TABLESPACE TPCH10_LINEITEM,
 PARTITION P_940401 VALUES LESS THAN (TO_DATE('1994-04-01','YYYY-MM-DD')) TABLESPACE TPCH10_LINEITEM,
 PARTITION P_940701 VALUES LESS THAN (TO_DATE('1994-07-01','YYYY-MM-DD')) TABLESPACE TPCH10_LINEITEM,
 PARTITION P_941001 VALUES LESS THAN (TO_DATE('1994-10-01','YYYY-MM-DD')) TABLESPACE TPCH10_LINEITEM,
 PARTITION P_950101 VALUES LESS THAN (TO_DATE('1995-01-01','YYYY-MM-DD')) TABLESPACE TPCH10_LINEITEM,
 PARTITION P_950401 VALUES LESS THAN (TO_DATE('1995-04-01','YYYY-MM-DD')) TABLESPACE TPCH10_LINEITEM,
 PARTITION P_950701 VALUES LESS THAN (TO_DATE('1995-07-01','YYYY-MM-DD')) TABLESPACE TPCH10_LINEITEM,
 PARTITION P_951001 VALUES LESS THAN (TO_DATE('1995-10-01','YYYY-MM-DD')) TABLESPACE TPCH10_LINEITEM,
 PARTITION P_960101 VALUES LESS THAN (TO_DATE('1996-01-01','YYYY-MM-DD')) TABLESPACE TPCH10_LINEITEM,
 PARTITION P_960401 VALUES LESS THAN (TO_DATE('1996-04-01','YYYY-MM-DD')) TABLESPACE TPCH10_LINEITEM,
 PARTITION P_960701 VALUES LESS THAN (TO_DATE('1996-07-01','YYYY-MM-DD')) TABLESPACE TPCH10_LINEITEM,
 PARTITION P_961001 VALUES LESS THAN (TO_DATE('1996-10-01','YYYY-MM-DD')) TABLESPACE TPCH10_LINEITEM,
 PARTITION P_970101 VALUES LESS THAN (TO_DATE('1997-01-01','YYYY-MM-DD')) TABLESPACE TPCH10_LINEITEM,
 PARTITION P_970401 VALUES LESS THAN (TO_DATE('1997-04-01','YYYY-MM-DD')) TABLESPACE TPCH10_LINEITEM,
 PARTITION P_970701 VALUES LESS THAN (TO_DATE('1997-07-01','YYYY-MM-DD')) TABLESPACE TPCH10_LINEITEM,
 PARTITION P_971001 VALUES LESS THAN (TO_DATE('1997-10-01','YYYY-MM-DD')) TABLESPACE TPCH10_LINEITEM,
 PARTITION P_980101 VALUES LESS THAN (TO_DATE('1998-01-01','YYYY-MM-DD')) TABLESPACE TPCH10_LINEITEM,
 PARTITION P_980401 VALUES LESS THAN (TO_DATE('1998-04-01','YYYY-MM-DD')) TABLESPACE TPCH10_LINEITEM,
 PARTITION P_980701 VALUES LESS THAN (TO_DATE('1998-07-01','YYYY-MM-DD')) TABLESPACE TPCH10_LINEITEM,
 PARTITION P_981001 VALUES LESS THAN (TO_DATE('1998-10-01','YYYY-MM-DD')) TABLESPACE TPCH10_LINEITEM,
 PARTITION P_MAX VALUES LESS THAN (MAXVALUE)
 )
 as select * from TPCH10.LINEITEM_BK;

 create table TPCH10.PARTSUPP
 pctfree 1
 pctused 99
 initrans 10
 parallel 2
 nologging
 tablespace TPCH10
 partition by range (PS_PARTKEY)
 (
 PARTITION P_001 VALUES LESS THAN ( 500000) TABLESPACE TPCH10,
 PARTITION P_002 VALUES LESS THAN (1000000) TABLESPACE TPCH10,
 PARTITION P_003 VALUES LESS THAN (1500000) TABLESPACE TPCH10,
 PARTITION P_MAX VALUES LESS THAN (MAXVALUE)
 )
 as select * from TPCH10.PARTSUPP_BK;

 create table TPCH10.ORDERS
 pctfree 1
 pctused 99
 initrans 10
 parallel 2
 nologging
 tablespace TPCH10_ORDERS
 partition by range (O_ORDERDATE)
 (
 PARTITION P_920102 VALUES LESS THAN (TO_DATE('1992-01-01','YYYY-MM-DD')) TABLESPACE TPCH10_ORDERS,
 PARTITION P_930101 VALUES LESS THAN (TO_DATE('1993-01-01','YYYY-MM-DD')) TABLESPACE TPCH10_ORDERS,
 PARTITION P_940101 VALUES LESS THAN (TO_DATE('1994-01-01','YYYY-MM-DD')) TABLESPACE TPCH10_ORDERS,
 PARTITION P_950101 VALUES LESS THAN (TO_DATE('1995-01-01','YYYY-MM-DD')) TABLESPACE TPCH10_ORDERS,
 PARTITION P_960101 VALUES LESS THAN (TO_DATE('1996-01-01','YYYY-MM-DD')) TABLESPACE TPCH10_ORDERS,
 PARTITION P_970101 VALUES LESS THAN (TO_DATE('1997-01-01','YYYY-MM-DD')) TABLESPACE TPCH10_ORDERS,
 PARTITION P_980101 VALUES LESS THAN (TO_DATE('1998-01-01','YYYY-MM-DD')) TABLESPACE TPCH10_ORDERS,
 PARTITION P_MAX VALUES LESS THAN (MAXVALUE)
 )
 as select * from tpch10.ORDERS_BK;

【PGAの調整】
PGAの調整をしていない場合、一時表領域が使われてしまい、せっかくのパラレルクエリも遅くなってしまいます。
今回は、流れるクエリがわかりきっているため、セッションレベルで下記を指定しました。
※一時表領域が使用される場合に悪化するだけのため、効果は記載していません。

<変更パラメータ>
 WORKAREA_SIZE_POLICY = MANUAL
 SORT_AREA_SIZE = 524288000
 HASH_AREA_SIZE = 1048576000
 ※実際の設定は、ログオントリガーで実施

【direct path readの高速化】(効果:中)
LINEITEM(inmemory)をポピュレートする際に実行されるdirect path readを高速化します。
OSのIOサイズ(1MB)に合わせて調整しました。
※iostat上で見ても、最初に測定したディスク性能が出ていました。

<パラメータ変更内容>
 _adaptive_direct_read=TRUE
 _db_file_direct_io_count=1048576
 db_file_multiblock_read_count=32

【direct path readのの抑制】(効果:低)
一見、上記の高速化と矛盾しているように見えますが、こちらはインメモリ化しないほうのテーブルの話です。
メモリが足りる環境で、あえてdirect path readを何回も発生させているのはどう考えても非効率です。
そのため、強制的にdirect path readを抑制しました。

<パラメータ変更内容>
alter session set events '10949 trace name context forever, level 1'
_small_table_threshold=1000000
_VERY_LARGE_OBJECT_THRESHOLD=100000

【リザルトキャッシュの使用】(効果:低)
いかにインメモリの処理が速いとは言っても、リザルトキャッシュを使用できた場合よりは速くなりません。
乱数の生成結果にも左右されますが、少しでも速度がほしかったため、設定しました。
※実行が1回のみのため、恩恵はそこまでありませんでしたが、複数回行う場合は、(効果:高)となります。

<パラメータ変更内容>
 result_cache_max_size=1G
 result_cache_mode=FORCE

【ネットワーク関連のチューニング】(効果:低)
せっかく速くしたクエリでも、ネットワーク部分で遅延があっては意味がないため、ネットワーク関連のパラメータを変更します。
SDUでの分割を少なくし、下位レイヤーでのパケット数を減らします。
また、合わせて送受信バッファも調整しました。

<パラメータ変更内容(listener.ora,tnsnames.ora)>
(SDU=2097152)
(RECV_BUF_SIZE=4194304)
(SEND_BUF_SIZE=4194304)

【その他パラメータの調整】
inmemoryサイズとDB Bufferのサイズは実際にテーブルサイズに合わせて調整しました。
また、パラレルクエリでは、ラージプールも使用するため、少し大きめに設定しました。

<パラメータ変更内容>
 db_block_size=32768
 inmemory_size=4500m
 db_cache_size=6000m
 large_pool_size=5G

ここまでで、CPU(全コア100%)もDisk(1GB/s)も完全に性能が出ている状態になっていました。
そのため、少しでも高速化を目指すとなると、それぞれの処理の効率化です。

【パラレルクエリの更なる高速化】(効果:低)
パラレルクエリを高速化するために、メッセージサイズを調整しました。
※実際はそこまで効果はありませんでしたが。。。

<パラメータ変更内容>
 PARALLEL_EXECUTION_MESSAGE_SIZE=32768

【ポピュレートの高速化】(効果:低)
今度は、ポピュレートを高速化します。
なぜなら、ポピュレート完了までは、ひたすらディスクからデータを取ってくるからです。(インメモリ領域にデータがないため)
TPC-Hだけで考えた場合、どう考えても、速くポピュレートを終わらしたほうがいいに決まっています。
通常、ポピュレートはバックグラウンドプロセスで行われます。
また、使えるCPUリソースもデフォルトでは1%に制限されている(INMEMORY_TRICKLE_REPOPULATE_SERVERS_PERCENT)ため、
最大値の50%に引き上げます。
合わせて、最大プロセス数もCPUコア数*2の値に設定します。
※デフォルト1なのは、ポピュレート中でもシステムに負荷をかけずに運用を続けられるようにするためです。

<パラメータ変更内容>
 INMEMORY_TRICKLE_REPOPULATE_SERVERS_PERCENT=50
 inmemory_max_populate_servers=16

結果、ポピュレート終了までの時間は多少速くなりましたが、まだ物足りません。

【もっとポピュレートを高速化!!】(効果:中)
50%ではやはり、同じデータを何度もディスクから読み込みます。
そのため、1度だけのディスク読み込みになるように、ポピュレートをバックグラウンドではなくフォアグランドで実施するように変更しました。
また、データが変わらないこともわかりきっているので、不要な再ポピュレートも抑制させました。
結果として、高速化 + 計測結果の安定化(フォアグラウンドなので)を実現できました。
リソース状態は、CPUネック(ポピュレート)の状態で、ディスクIOは300MB/s程度になっていました。

<パラメータ変更内容>
 _inmemory_populate_fg=TRUE
 _inmemory_repopulate_disable=TRUE

ここまでで、当初の目標値は達成していたため、ひとまず満足していたのですが、勝負となると欲が出てきます。
結果、邪道な方法に手を出してしまいました。。。

【禁断の・・・】
データ変えなければルール違反ではないかな??
とルールの穴を突いているようで、最後まで使うか、本当に悩みましたが、勝負に勝ちたい気持ちに負けて使ってしまいました。
Materialized View(MVIEW)!!!
当然データは変えてはいけないので、MVIEWを作りクエリ・リライトでMVIEWを使用するようにしました。
ただ、正直、結構面倒な作業になりました。

どの乱数にも対応できるようなMVIEW(乱数部分の条件なし)の場合
→インメモリ処理のほうが圧倒的に速い

乱数部分を埋めた状態のMVIEWを作成
→HammerDBのソースを確認して、乱数生成のパターンの確認したところ、一つあたり数万以上のパターンになるクエリもあったため
MVIEW作成自体、かなり時間がかかりました。。。

仕様上クエリ・リライトできないパターン
→作成しながら気づいたのがこのパターンです。
結果、一番時間のかかるLINEITEMを読む時間は短縮できずに、
INMEMORYクエリ→MVIEW化の高速化になってしまいました。
何万ものMVIEWのクエリリライトでは、今度はパースの時間がインメモリパラレルクエリの時間より遅くなってしまいました。

結果、パターンがそれほど多くないもののみをMVIEW化しました。
※効果は言うまでもないため、記載していません。

<作成したMVIEW数>
query  1 : 61
query  4 : 60
query  5 : 35
query  9 : 93
query 18 :  4
query 21 : 25

<パラメータ変更内容>
 alter session set query_rewrite_enabled=FORCE
 ※ログオントリガーで設定

【バチがあたったのか。。。】
勝負前日の夜、最後に実行時間の計測だけ行ったところ、いきなり速度が80%程度に落ちてしまいました。
待機イベントを確認していると、リソース・マネージャに制限されていたため、最後に無効にしました。

<パラメータ変更内容>
 alter system set resource_manager_plan='';
 execute dbms_scheduler.set_attribute('WEEKNIGHT_WINDOW','RESOURCE_PLAN','');
 execute dbms_scheduler.set_attribute('WEEKEND_WINDOW','RESOURCE_PLAN','');

以上が今回、TPC-Hで実施した内容です。


[10月三木会技術ブログ]TPCHハードウェアチューニング

$
0
0

毎月大阪で第三木曜に行っている勉強会、「三木会」でお話した内容をまとめます。

今回のテーマは
「Oracleのdirect path read (diskからメモリにのせずreadし続ける) でTPCHを10GB/sが可能か?」になります。

Oracleからのdirect path readで読み出すということですが、まずDBで読み出す前に、ハードウェア的に10GB/sの性能を出す必要があるため、hardwareボトルネック中心のお話になります。

*** 検証の経緯

9月の検証では、oracleのdirect path readで1GB/secを計測しました。
500MB/secのSSDをRAID0でつなげていたので、当時の最大スループットがでていたということになります。

その時のマシン構成は以下の通りです。

(1GB/sec)デスクトップマシンの構成

CPU
  AND FX 8320
  8core/8thread

motherboard
  msi 970 gaming

memory
  Memoria G.SKILL VALUE 16GB (2X8GB) 240P DDR3 1600 PC3 12800 F31600C11D16GNT 4枚
  8Gx4 = 32G

SSD
  SanDisk Ultra II 2枚
  max read 550 / max write 500
  RAiD 0 を2枚で構築
  oracle datafileのみを置く

HDD
  SEAGATE ST1000VX001 [1TB SATA600]
  OS用やoracle等アプリケーション用のHDD

グラフィックカード
電源650W

OS
  CentOS7.1

この構成で10万相当のマシンになります。
もともと検証用に購入したものでないため、メモリを多めにつんでいる以外は、データベース向けの構成にはなっていません。

これでSSDのread性能を限界まで引き出せるのなら、パーツを更に追加してどこまで行けるのか?(目標は10GB!)という疑問に答えるのが今回の検証です。
今回は、このマシンにプラス20万の予算で行います。

*** 基本的な方針

まず、基本的な方針を以下のように考えました

  - SSDを20枚、RAID 0(500M/sec x 20)でつなぐ
  - 残りの予算でRAIDcardを購入
  - motherboard、CPUでネックがでるかもしれないので様子を見て購入

最初はoracleを使わず、ハードウェア的にシーケンシャルディスクリード性能を上昇させます。
そのために、現状のハードウェアでボトルネックとなりそうな箇所を割り出します。

*** ボトルネックの割り出し
ハードウェアの知識が浅いため、基本的なところから考えました。

ボトルネックになりそうな箇所の洗い出し。
	1. CPU処理性能
	2. マザーボードチップセット (間の帯域)
	3. SSD 最大読み出し速度 (500M/s 以上で20枚)
	4. SATA3.0 (最大転送速度を6Gbps実効速度600MB/s)
	5. HBA/raid card (PCIexpressの規格 gen2x16なら8GB/s)
        6. SATAport 20確保

まず最初に考えるべきなのは、チップセットとCPU間のバス帯域。
ここが10GBない場合は、motherboardを取り替える必要があります。

msi 970 gamingでの各バス規格と最大帯域 (双方向)

northbrige (チップセット) -> CPU
  HyperTransport 3.0   20GB/sec

PCIExpress -> northbrige
  PCIExpressの対応規格がgen2x16のため8GB/s

southbrige -> northbrige
  A-Link Express II 2GB/sec
  motherboardのSATAポートはここを通るため2GBで頭打ちになる

*** 追加購入部品の選定

以下のような条件でパーツ探しました。極力安いことも条件です。

RAIDcard
  PCIExpress2.0x16 SATAport16個以上に相当するポートが取れること。
SSD
  シーケンシャルリード 500MB/sec以上のもの

SATAケーブル
  SATA3.0相当の性能

これを受けて、以下のパーツを購入しました。

RAIDcard
  HighPoint SAS/SATA RAID CARD 6Gb/s RocketRAID 2740
    SASport4 (内部16port)
    PCI Express 2.0 x16
    ¥26,000と定価の半額で安いものがあったため決定

SSD
  SSDplus SDSSDA-120G-J25C
  read:520 MB/s   write:180 MB/s (writeはTPCHの場合遅くても良い)
  ¥5,000~6,000 と安い
  20枚買うと¥110,000程度

SAS→SATAケーブル
  miniSAS → SATAファンアウトケーブル
  SATA4portに変換できるSASケーブル
  ¥1,000程度

合計¥140,000

*** 測定
以上のパーツを組み合わせて測定してみました。

fioというベンチマークツールを走らせ、処理中のiostatの監視を行い計測。

iostat -mx
Device:         rrqm/s   wrqm/s     r/s     w/s    rMB/s    wMB/s avgrq-sz avgqu-sz   await  svctm  %util
sdb               0.00     0.00 21297.00    0.00  1632.50     0.00   128.00     1.58    0.14   0.07  83.60

1.5GB/sec程度しか出ていない!
原因を割り出すために幾つかのチューニングを試します。

*** 原因調査
原因を切り分けていくため、幾つかの確認とチューニングを施しました。

1. SSDは一枚read 500MB/sec でるのか?

新しく購入したSSDのread性能を一枚で (raid0無し) で確認しました。

hdparm -Tt
  /dev/sda  522.43 MB/sec

SSDは悪くないようです。

2. ディスクチューニングをしてみる
TPCHでよくやるチューニングと同等のチューニングを行いました。
このチューニングでパフォーマンスの上昇が見られるのか、どこで頭打ちになるのかを確認します。

ブロックサイズの調整 (上昇)
チャンクサイズの調整 (上昇)
Parallels度の調整 (実行プロセス数)

結果
|               | ChunkSize64k | CS256k | CS1024k |
|               |          xfs |    xfs |     xfs |
|               |       (MB/s) | (MB/s) |  (MB/s) |
|---------------+--------------+--------+---------|
| Seq-Read-32k  |       1372.3 | 1242.3 |   653.6 |
| Seq-Read-128k |       1814.4 | 1817.5 |  1090.5 |
| Seq-Read-256k |       1845.4 | 1988.5 |  1747.7 |
| Seq-Read-512k |       1999.5 | 2199.7 |  2185.7 |
| Seq-Read-1m   |       2052.6 | 2397.6 |  2585.7 |
| Seq-Read-2m   |       2414.2 | 2538.9 |  2592.1 |
| Seq-Read-4m   |       2273.5 | 2530.2 |  2600.5 |
| Seq-Read-8m   |       2197.5 | 2527.6 |  2595.9 |
| Seq-Read-16m  |       2115.1 | 2526.8 |  2605.5 |
| Seq-Read-32m  |       2350.1 | 2524.1 |  2603.8 |
| Seq-Read-128m |       2117.4 | 2502.9 |  2590.4 |
| Seq-Read-256m |       2265.2 | 2484.2 |  2565.1 |
| Seq-Read-512m |       2138.7 | 2429.8 |  2510.1 |

fioの場合、設定のiodepthがParallels度に当たります。iodepth=10として
ブロックサイズを上昇、チャンクサイズを上昇させながら測ったところ2.6GB/sec近くまで上昇しました。

しかし、この2.6GBで頭打ちとなってしまい、これ以上はどうやってもでません…
この辺りで時間も残り少なくなってきました。

*** 頭打ちの原因のポイント

最も原因として怪しいのはRAIDcardチップセットではないかと思われます。
その根拠は以下のように考えました

1. SSD、SASケーブルは問題ない
2. PCIExpressの規格の処理限界より大きく下回るためPCIExpressも問題はない
3. CPU利用率も限界ではない
4. SSDを一枚から順番に足していくと2.5GBまでは順調に上昇するが、2.5GBから急に上がらなくなる。request数も4,0000程度で制限される。
5. 別のASUS系のマザーボードでWindows7環境のベンチマークをとったときも同じように2.5Gで頭打ちとなった (カーネルパラメータなどで抑えられている可能性も低い)

==> RAIDCard のコントローラーチップセットの処理限界が濃厚

*** 10GB/secを実現させるには?

勉強会最後に弊社のハードウェア部門の方に実績のあるサーバーで、実演していただきました。
弊社がデータベースアプライアンスとして売り出している「Insight Qube」を使用して、Oracleからのdirect path readで10GB/secを見事に成功させていただきました。

そこで頂いたアドバイスなどから、10GB/secへ近づくための注意点をまとめました。

1. raidcardのコントローラーの処理性能は公開していないことも多い
2. 規格的に8GB/secのパフォーマンスとされているが、実際の処理性能は測るまではわからない
3. SSD,RAIDcardは、実際にベンチマークでの結果がでているものから試算する
4. RAIDcardはコントローラー限界があるため、複数枚さすことを考えてmotherboardのPCIExpress最大リンク幅は多いほうが良い
5. Oracleを使用した場合はCPU使用率も大きく上がった 10GB/secではCPUがネックになる可能性が高い

これらの実現には
RAIDcardの追加購入 (実績あるもの)、motherboardの変更 (PCIExpressの最大リンク幅が狭いため)、CPUの変更 (次にネックになる可能性が高い)
が必要となるのですが、予算的に購入は難しいため今回のベンチマークは以上となりました。

今回の失敗を踏まえて、最初から多くのディスクリードを見積もってパーツを選定すればもっと良い結果にできそうです。

11月の三木会では今回作成したマシンを使用して、SQL Server2016のパフォーマンステストを行いたいと思います。

最後に、今回の検証に使用したマシンと記念に一枚
DSC_0413

[DBチューニングコンテスト とんがりナレッジ] CPUを100%活用する

$
0
0

11/27(金)に社内SQL Serverのチューニングコンテスト決勝戦が開催されました。
前回のOracleは2位でしたが、今回は優勝しましたのでその要因について紹介したいと思います。

今回はチューニングを始めるにあたって前回は何が良くて何が悪かったのかをまずは考えてみました。

  • 良かった点

    • マシンスペック??
  • 悪かった点

    • チューニング不足

悪かった点のチューニング不足については知識不足もあり、マテリアライズビューをしようとしなかったことが敗因の一つだと考えました。
SQL Serverにはインデックス付きビューというマテリアライズビューにあたる機能があります。
今回はインデックスビューを使用しましたが下手に使うよりはカラムナーストアインデックスの方が速かったため今回は割愛します。
良かった点に挙げているマシンスペック??は本来であれば同じ予算内でマシンを構築するルールのため存在しません。
実際に自分のマシンとほぼ同等の構成のマシンを使用している参加者もいます。
ではマシンスペックの差とはなんなのか?
TPC-HではCPU使用率が100%になることがあります。
その場合、一般的にはCPUリソースを100%活用できているということになるかと思います。
しかし

CPU使用率100%=CPUスペックを100%活かせている

とは限りません!
前回のOracleのチューニングコンテスト終了後に自分が理由がいまいち理解できなかったこともあり、ある実験をしました。

  • 実験内容

    • CPUのコアを100%使用する
  • 環境

    1. Hさんのマシン
    • OS : CentOS 5.7
    • CPU : Core i7 4790K
  • soneetaのマシン(Cent OS)
    • OS : CentOS 6.2
    • CPU : Core i7 4790K
  • soneetaのマシン(Arch Linux)
    • OS : Arch Linux
    • CPU : Core i7 4790K

soneetaとHさんのマシンには一点だけ大きく違う点がありますがCPUは共通でCore i7 4790Kです。
※ 厳密に言うとマザーボードも違うのですが今回の実験には関係ないことも今回のチューニングコンテスト終了後に確認しています。

  • 結果

    約2分後の状態

    1. Hさんのマシン
      h_cpu
    2. soneetaのマシン(CentOS)
      soneeta_cpu_archlinux
    3. soneetaのマシン(Arch Linux)
      soneeta_cpu_centos
  • 詳細

    1. Hさんのマシン
      • 起動時 : 4.0GHz
      • 負荷開始時 : 4.0GHz前後
      • 2分後 : 3.1GHzで安定
    2. soneetaのマシン(Arch Linux)
      • 起動時 : 4.0GHz
      • 負荷開始時 : 4.0GHzで安定
      • 2分後 : 4.0GHzで安定
    3. soneetaのマシン(Arch Linux)
      • 起動時 : 4.0GHz
      • 負荷開始時 : 4.0GHzで安定
      • 2分後 : 4.4GHzで安定


Core i7 4790Kは倍率ロックフリーモデルで定格クロック4.0GHzで状況に応じて最大4.4GHzまでクロックアップします。
この為、正しい動作は3のsoneetaのマシン(Arch Linux)のみになります。
それでは1,2の違いと2,3の違いとは何なのか?
1,2については先に挙げたハード面での1点の違いによるものです。
違いはCPUファンです。
HさんのマシンはCore i7 4790Kに付属するファンを使用、soneetaのマシンではReserator 3 Maxという簡易水冷ファンを使用しています。
倍率ロックフリーモデルの場合は通常時は定格クロックで動作し、CPUがビジーな状態になるとクロックアップしますがここに問題があります。
もしクロックアップした時にCPU温度が上がりすぎた場合にはどうなるか?
答えは簡単で当然クロックを維持できない為、クロックを下げるといった動作を取ります。
その時にクロックがどこまで下がるかというと、実は定格クロック以下まで下がってしまいます。
Hさんのマシンの場合には、付属のファンでは定格クロックの4.0GHzでもCPU温度が高すぎるため、定格クロックを下回ってしまったということになります。
2,3の違いはOSによるものでCPU周波数スケーリングの問題です。
Arch LinuxのWikipediaに記載されていますがLinuxでもカーネル3.4から必要モジュールが自動ロードされるようになっており、CentOS 5.7,6.2が対応していなかったため、倍率ロックフリーの機能を活かしきれず最大クロック数までクロックアップしません。
さて今回はSQL ServerのチューニングコンテストでしたがWindowsではどうでしょうか?

  • 実験内容

    • CPUのコアを100%使用する
  • 環境

    1. Kさんのマシン
    • OS : Windows Server 2016 CTP3
    • CPU : Core i7 4790K
    • CPUファン : 付属のファン
  • Iさんのマシン
    • OS : Windows Server 2016 CTP3
    • CPU : Core i7 4790K
    • CPUファン : 虎徹 SCKTT-1000
  • soneetaのマシン
    • OS : Windows Server 2016 CTP3
    • CPU : Core i7 4790K
    • CPUファン : Reserator 3 Max

  • 結果

    約3分後の状態

    1. Kさんのマシン
      k_cpu
    2. Iさんのマシン
      i_cpu
    3. soneetaのマシン
      soneeta_cpu


2,3は同等の結果でArch Linuxの場合と同等の状態で空冷のファンでも十分に冷却できていることがわかります。
驚きなのは1はCentOSのときより悪く2.2GHzまでクロックダウンしてしまっています。
さらにはCPU使用率も100%を維持できなくなってしまいました。
Oracleのチューニングコンテストのときから、オーバークロックの設定をしても速くならないとか逆に遅くなってしまうという話があったのはこの為だと考えられます。
それではCentOSとArch Linuxの違いのようにOSによる制限はあるのか?
Windowsでは電源オプションがそれにあたります。
powerplan
デフォルトではバランスになっているため高パフォーマンスに設定する必要があります。
Core i7 4790Kでバランスの場合には4.0GHz、高パフォーマンスにすると4.4GHzのクロックで動作します。

今回の教訓

  • CPUは冷やせ!
  • 想定通りのクロックで動作するか確認する!

[DBチューニングコンテスト とんがりナレッジ] スロークエリチューニング

$
0
0

決勝戦のTPC-Hのチューニングとしてパーティショニングとカラムストアインデックスに加えて、実行計画で不足しているインデックスや統計情報を追加などを実施しました。ですが、22のクエリのうち以下の2つが実行に時間が掛かっていました。

  1. Query9

  2. Query13

それぞれ実際には以下のようなSQLが実行されます。

  1. Query9

    SELECT
        nation,
        o_year,
        SUM(amount) AS sum_profit
    FROM
        (
        SELECT
            n_name AS nation,
            DATEPART(yy,o_orderdate) AS o_year,
            l_extendedprice * (1 - l_discount) - ps_supplycost * l_quantity AS amount
        FROM
            part,
            supplier,
            lineitem,
            partsupp,
            orders,
            nation
        WHERE
            s_suppkey = l_suppkey AND
            ps_suppkey = l_suppkey AND
            ps_partkey = l_partkey AND
            p_partkey = l_partkey AND
            o_orderkey = l_orderkey AND
            s_nationkey = n_nationkey AND
            p_name LIKE '%:1%'
        ) profit
    GROUP BY
        nation,
        o_year
    ORDER BY
        nation,
        o_year DESC OPTION(maxdop $maxdop)
    
  2. Query13

    SELECT
        c_count,
        COUNT(*) AS custdist
    FROM
        (
        SELECT
            c_custkey,
            COUNT(o_orderkey) AS c_count
        FROM
            customer
            LEFT OUTER JOIN
            orders ON
            c_custkey = o_custkey AND
            o_comment NOT LIKE '%:1%:2%'
        GROUP BY
            c_custkey
        ) c_orders
    GROUP BY
        c_count
    ORDER BY
        custdist DESC,
        c_count DESC OPTION(maxdop $maxdop)
    

特にQuery13は決勝参加者の多くが実行時間が長いと認識しており、各々が対策を打ったクエリでした。
私はQuery9,Query13を他の20クエリと比較して、この2つだけで使用されているあるものに気が付きました。
それはLIKE句に部分一致の文字列検索を含んでいるということです。
だからと言って何の対策も思い付かずにいたときに、他の参加者と話をしているとチューニング内容もほぼ一緒なのにも関わらず自分よりもQuery13の実行が圧倒的に速い人がいることに気が付きました。
詳しく話を聞いてみるとどうやら構築時のトラブルもあり、英語版のWindows Server 2016に英語版のSQL Server 2016をインストールしたというのが唯一の違いです。
この時にあることが閃きます。
英語で文字列を検索するのと日本語で文字列を検索する速度は一緒なんだろうか?
そこでOSとSQL Serverを英語版で再インストールしてみましたが改善されませんでした。
条件は同じはずなのに何故だろう?
原因がわからずオプションを眺めていると、Japanese_CI_ASと書かれている項目を発見しました。
英語版でインストールしたにも関わらず、照合順序の設定が日本語のままになっています。
再度、SQL Serverをインストールしなおし、確認するとインストーラーのデフォルトでは照合順序がJapaneseになっています。
どうやらWindowsのロケール設定で照合順序が自動的に選択されるようです。
この為、英語環境でのデフォルトであるSQL_Latin1_General_CP1_CI_ASに選択し直してインストールしました。
collation
照合順序の設定方法はいくつかあります。

  1. SQL Serverのインストール時に指定する
  • データベースのデフォルト設定に影響
  • データベースのオプションで指定
  • カラム単位で指定
  • そこでインストール後にデータベースオプションで、Japanese_CI_ASとSQL_Latin1_General_CP1_CI_ASを変更してQuery13の実行速度を比較したところ、SQL_Latin1_General_CP1_CI_ASの方が4~5倍ほど処理が速いようです。
    ただ疑問が残ります。
    そもそも照合順序で何が変わるのか?
    答えはMicrosftのサイトに記載されていました。
    https://technet.microsoft.com/ja-jp/library/ms175194(v=sql.105).aspx
    インストール時の選択画面にも表示されていましたが照合順序には2種類あるようです。
    さらに文末には明確に「パフォーマンスが異なります」と書かれています。

    1. Windows照合順序
    2. SQL Server照合順序

    日本語の場合はWindows照合順序となりUnicodeデータとして取り扱われるため2倍の領域が確保され、結果としてパフォーマンスが悪くなってしまうようです。
    加えてUnicode以外の日本語の場合はコードページが932になるとのことなのでキャラクタセットはShift-JISということになります。
    Shift-JISは2バイト目にASCIIコードが出現するという欠点があり、その判定処理はEUC-JP,UTF-8に比べて複雑になってしまいます。
    これら2つの要因が英語と日本語での実行速度に影響を与えていたようです。
    日本語データを格納するようなデータベースでは、カラム毎の用途に応じて照合順序を適切に設定することで恩恵を受けることができそうです。

    [DBチューニングコンテスト とんがりナレッジ] インデックス付きビューを使用してみた

    $
    0
    0

    2015/11/27に行われた、第2回社内DBチューニングコンテストに参加しました。
    記録は残念ながら2位タイでしたが、今回もその時に実施した内容を書いていきたいと思います。

    今回は、DBがSQL Serverで、競争条件は前回と同じでした。

    【競争条件】
     <測定ツール>
     - HammerDBを使用
     <決勝>
      - TPC-H 4セッション(Scale Factor=10 )
    

    以下は、今回使用した環境です。

    【OS+DB】
     OS : Microsoft Windows Server 2012 R2
     DB : Microsoft SQL Server 2016 (CTP2.4) - 13.0.600.65 (X64)
     ※H/Wは、前回のコンテスト時に使用したものを使用しています。
    

    まず、最初に実施したのは、列ストアインデックスの使用です。
    よりサイズが大きいテーブルのほうが、効果が大きいはずということで、対象テーブルのサイズを確認しました。

    【テーブルのサイズ】
     テーブル名 サイズ(MB)
     ---------  ----------
     LINEITEM        11439
     ORDERS           2621
     PARTSUPP         1964
     PART              380
     CUSTOMER          262
     SUPPLIER           15
     NATION          0.008
     REGION          0.008
    

    ひとまず、CUSTOMERまで、列ストアインデックスを作成してみました。
    結果は、以下の通りでした。

    ss_columnstore_index
    ※ベンチマークの数字は公表できないため、隠させて頂いております。

    処理時間として、5倍、高速化に成功しました!!

    ただ、一点疑問が残りました。
    大きい順から実施したのにもかかわらず、PARTSUP,PARTでは、ほぼ効果がありませんでした。
    しかし、なぜかCUSTOMERでは少し速くなりました。

    理由を考えるため、クエリごとの処理時間の割合を確認してみることにしました。

    <CUSTOMERへの列ストアインデックス作成前の状態>
    CUSTOMERへの列ストアインデックス作成前

    この状態では、Query13のみの実行に全体の60%以上がかかっていました。
    ということで、実際のQuery13のSQLを確認してみました。

    select
     c_count , count(*) as custdist
    from
     (
      select
       c_custkey , count(o_orderkey) as c_count
      from
       customer left outer join orders
      on c_custkey = o_custkey and o_comment not like '%:1%:2%'
      group by c_custkey
      ) c_orders
    group by c_count
    order by custdist desc, c_count desc option (maxdop $maxdop)
    

    実はQuery13は、ORDERSとCUSTOMERのみを使用しているSQLでした。
    結論としては単純にサイズの問題というより、CUSTOMERをカラムナーで処理を行うほう高速になるSQLがQuery13に残っていただけという理由のようでした。

    実は結構ここまでで高速化できていたので、列型で格納できるDBのすごさに、個人的に非常に驚いていました。
    ※行型で格納して、列型に変換して処理を行うようなDBでは、ディスクからデータを持ってくる際に
    変換処理時間が必要になるためその分処理が余分に必要になり、そこのボトルネックで大分苦労したからです。

    では、残りのチューニングを実施していきます。
    再度、Queryごとの実行割合を確認してみました。

    列ストアインデックス作成済み

    まだQuery13が半分を占めていました。。。

    ここで、インデックス付きビューに手を出し始めました。
    使ってみた感想は、結構、制約が厳しい!でした。
    以下は、今回主に関係のあった、インデックス付きビューを使用する際の制約です。

     <禁止事項>
     - サブクエリ
     - 外部結合
     - 自己結合
     - HAVING句
     - ビューに作成する最初のインデックスは一意である必要がある
    

    とりあえず、組み替えて速くできそうな部分だけインデックス付きビューを使用できるようにしてみました。

    ■Query13
    select
     c_count , count(*) as custdist from
     (
      select
       c_custkey , count(o_orderkey) as c_count
      from
       customer left outer join orders
      on c_custkey = o_custkey and o_comment not like '%:1%:2%'
      group by c_custkey
      ) c_orders
    group by c_count order by custdist desc, c_count desc option (maxdop $maxdop)
    
    ■作成ビュー
    CREATE VIEW [dbo].[SQL13_pending_deposits] WITH SCHEMABINDING 
    SELECT o_custkey ,  count_big(*) as col_cnt 
    FROM  [dbo].[orders]   
    WHERE ( NOT  o_comment like '%pending%deposits%' ) GROUP BY  o_custkey;
    

    きちんとSQLServerのオプティマイザが自分の意図を解釈してくれるか不安だったのですが、
    無事に想いが伝わった結果が以下です。

    Query13_インデックス付きビュー作成

    実行時間の半分をしめていたQuery13がなくなったため、実行時間も半分になりました!

    ここで、再度残りのクエリーの割合を確認

    Query13_インデックス付きビュー作成時のQuery割合

    残りの、TOP3のQueryは、Query 20, Query 9 ,Query 18でした。

    ひとまず、手を出しやすそうなところからということで、Query18を実施しました。

    ■Query18
    select top 100 c_name, c_custkey, o_orderkey, o_orderdate, o_totalprice, sum(l_quantity) 
    from customer, orders, lineitem 
    where o_orderkey in ( select 
                           l_orderkey 
                          from lineitem 
                          group by l_orderkey 
                          having sum(l_quantity) > :1
                         ) 
    and c_custkey = o_custkey 
    and o_orderkey = l_orderkey 
    group by c_name, c_custkey, o_orderkey, o_orderdate, o_totalprice 
    order by o_totalprice desc, o_orderdate option (maxdop $maxdop)
    
    ■作成ビュー
    CREATE VIEW [dbo].[SQL18] WITH SCHEMABINDING 
    AS select l_orderkey,sum(l_quantity) as sum_l_quantity,count_big(*) cnt 
    from dbo.lineitem group by l_orderkey ;
    
    

    これは、havingが使えないため、havingで使用する計算結果までをインデックス付きビューで使用できるようにしました。
    その結果は、以下です。

    Query18_インデックス付きビュー作成

    15%程度ほど速くなりました。
    では、そのままQuery9も実施してみます。

    ■Query9
    select nation, o_year, sum(amount) as sum_profit from 
    ( select n_name as nation, datepart(yy,o_orderdate) as o_year, 
       l_extendedprice * (1 - l_discount) - ps_supplycost * l_quantity as amount 
      from part, supplier, lineitem, partsupp, orders, nation 
      where s_suppkey = l_suppkey and ps_suppkey = l_suppkey 
      and ps_partkey = l_partkey and p_partkey = l_partkey 
      and o_orderkey = l_orderkey and s_nationkey = n_nationkey 
      and p_name like '%:1%'
    ) profit 
    group by nation, o_year 
    order by nation, o_year desc option (maxdop $maxdop)
    
    ■作成ビュー
    CREATE VIEW [dbo].[SQL9_coral] WITH SCHEMABINDING 
    AS select o_orderkey,n_name as nation, datepart(yy,o_orderdate) as o_year
    , l_extendedprice * (1 - l_discount) - ps_supplycost * l_quantity as amount 
    from dbo.part, dbo.supplier, dbo.lineitem, dbo.partsupp, dbo.orders, dbo.nation 
    where s_suppkey = l_suppkey and ps_suppkey = l_suppkey and ps_partkey = l_partkey 
    and p_partkey = l_partkey and o_orderkey = l_orderkey and s_nationkey = n_nationkey 
    and p_name like '%coral%';
    
    

    Query9は簡単にできるかと思ったのですが、ビューが一意になっていなかったため、o_orderkeyを足して対応しました。
    そして、実行結果が以下です。

    Query9_インデックス付きビュー作成

    なぜか、思ったほど速くならない。。。

    原因を確認するため、実際に単体でSQLを実行するときちんとリライトしていました。
    しばらく悩みながら、DOPを変えるとどうなるのか試したところ、
    DOPが高いとリライトしない状態になっていました!

    オプティマイザがそう判断するということは、本当にインデックス付きビューを使用するより、
    DOP高いほうが速いのかと確認したところ。。。

    DOP 8 での実行時間               : 2
    DOP 2 での実行時間               : 6
    インデックス付きビューでの実行時間 : 1
    ※実行時間は、時間ではなく、インデックス付きビューの実行時間を1としたときの数字です。
    

    やっぱり、嘘でした。。。
    たしかに、DOP8のほうがDOP2より速くなっていますが、インデックス付きビューを使用するよりは遅かったです。
    しかも、実行時のOSのリソース状態はCPUネックの状態です。
    DOP8では、8コアを全て使用しての数字のため、4session * DOP2で実行するのと実際にはあまり大差がありません。

    ということで、気を取り直してDOP2で再度実行してみました。

    Query9_インデックス付きビュー作成_DOP2

    無事にリライトして、19%程度高速化できました。

    で、ここで元々目標としていた数字を達成できたため、終了し、勝負に挑みました!

    結果は冒頭の通り、、、
    あきらかに目標値の設定をミスりました。。。

    ■反省点
    自分の甘えを深く反省し、次回の勝負に挑みたいと思います。
    また、合わせて、

    自分の中の仮説
    AMD FX-9590
    →Core i7 4790K
    にすることで、
    TPCH : 約1.3倍 , TPC-C : 約1.7倍
    が実現するか検証してみたいと思います。

    [DBチューニングコンテスト とんがりナレッジ] SSD命!! でも勝負はそこではなかった…

    $
    0
    0

    祝!2位タイ!!

    というわけで、チューニングコンテスト2回目にして待望の入賞を果たすことができました。平間です。

    「若手エンジニアが挑戦!」なんて煽り文句が社内外で出ていた中、アラフォーの自分が出てもいいのか?いや、出るんだ!だって賞品欲しいし(入賞者には賞品があるのです)、という心の中の葛藤も乗り越えてつかんだ栄冠です。しかし今回は第2回、実は第1回は入賞はおろか予選落ちしてしまっていたのです…。

    というわけで、今回のブログは第1回に敗れた反省からのスタートです。

    最強のSSDを作る!

    コンテスト出場にあたって組み立てるPCの予算は10万。これを超える分は自腹となるため、何とかこの範囲内で収まる高速なマシンを作る必要があります。また、私が所属しているビッグデータ・ソリューション事業部は高速データベースマシンのInsight Qubeを企画・販売している部署です。そのプライドにかけても、高速なストレージを持つマシンを作る必要があります。

    そうして構成したPCのパーツが以下の通り。

    分類 名称 スペック等
    CPU Intel Core i5 4690k 4core 4thread 3.5GHz/3.9GHz
    M/B ASRock Z87M Pro4 PCIe 3.0 x16 1本、PCIe 2.0 x16 1本、SATA3 6ポート
    メモリ DDR3-1600 8GB 2枚 合計16GB
    HDD 東芝 DT01ACA100 3.5インチ 7200rpm 1TB
    SSD CFD CSSD-S6T128NHG6Q 4枚 東芝製 128GB Read 530MB/s Write 490MB/s
    HBA アユート PM-MSATA22GT-R PCIe 2.0 x2 Marvell 88SE9230採用

    SSDが4枚!これらをRAID0で構成してあげれば、SATA3でも最近のNVMe SSD並みの2GB/s越えのスループットが出るんじゃないの?なんて思ったわけです。

    さて、ここに1枚のブロック図があります。今回使用したマザーボードが搭載しているZ87チップセットのブロック図です(コスト削減のために型落ち品を選んだため、新品なのですが旧世代のチップセットでした)。

    z87-chipset-diagram-3x2この図を見ると、6ポートあるSATAはZ87チップセットにつながっています。データをCPUまで送るにはZ87とCPUとの間をつなぐDMI2.0というバスを通過する必要があるのですが、実はこのバスの帯域は20Gbpsしかありません。SATA3は6Gbpsなので、6×6=36Gbps。全然足りませんね。今回搭載したSSDは4枚ですが、これでも24Gbps。実際はシリアル伝送するためのエンコード処理なども入って実効速度はさらに低下します。実測するとSSD3枚分、1.5GB/s程度の速度しか出ませんでした。

    ssdold

     

    残り1枚分はどうするんだ、ということで登場したのが先ほどの表にさりげなく入っていたHBA、追加のSATAアダプタカードです。このカードも選択には注意が必要です。よく格安で売っているカードはPCI-Express 2.0を1レーンしか使用していません。これは500MB/sの帯域しかなく、実行速度はそれ以下となってしまいます。よって最低でも2レーン使用しているものを選ぶ必要があるのです。

    そこを注意深く選択し、きちんとCPUに直結されているPCI Expressのスロットに装着すると(チップセット側のスロットに装着してはいけませんよ。DMI2.0で詰まってしまいますから)、以下の結果が待っているわけです。じゃーん。

    ssdnew

     

    頑張るのはそこではなかった…

    ここまでで勝った気になっていたのです。第1回では…。

    CPUは何とかなると思っていました。Core i5 4690kで、Hyper-Threadingこそ使えませんがオーバークロックは可能です。1万円高いi7を使わなくても、オーバークロックしてやれば大丈夫、マルチスレッドなんて…。

    ところがどっこい、チューニングコンテストはTPC-Cはもちろん、TPC-Hも複数セッションを同時に実行する必要があります。スレッド数が多い方が当然有利です。しかもデータ量はたいしたことなく、自慢の2GB/sパワーを発揮する機会もほとんどなく…。

    敗れたり!

    というわけで、第2回は自腹でCPUを買い替え(これには妻の応援と家計からの拠出という涙の物語があるのですが、それはさておき)、Core i7 4790Kという他の挑戦者と同じ土俵にやっと乗ったわけです。4790kは優秀ですね。マザーボードにプリセットされたお手軽オーバークロック設定をちょっとセットしただけで、あっさりと4.7GHz動作ですよ。以前のCore i5 4690kではどうしても4.6GHz止まりだったので、スレッド数だけではなくクロックを見ても買い替えた甲斐がありました。

    すごいぜCore i7 4790K!

    corei7

     

    今回はDB (SQL Server)のことをほぼ何も書かずに終わりますが、チューニングコンテストで猛威を振るったカラムストアインデックスも(当然私も使いました)、その発想は「いかに無駄にディスクを読まずにI/O待ちを減らすか」ですので、処理がI/OからCPUに寄ったあとは、CPU処理能力の勝負となるわけですね。つまり、ディスク周りへのこだわりはほどほどにしてCPUを強化するという戦略が、この第2回でも正解だったというわけです。

    というわけで、最後にとても当たり前のことを偉そうに一言。

    DBサーバーだってCPUが命!

    おあとがよろしいようで。

    Viewing all 48 articles
    Browse latest View live