MTU lab

javaとパイプでつなぐ

概要

MTU と他のプログラムを「名前付きパイプ」と呼ばれるものを使ってつなぐと、CSVデータをディスクへ書き/読みするI/Oが無くなるのでCSVファイルを介在させるよりも性能が向上します。
このトピックでは java言語で記述されたプログラムとMTUを名前付きパイプで直接つなぐ方法を具体的に説明します。
SCOTTスキーマでお馴染みのサンプルインスタンス表(EMP,DEPT,SALGRADE)を使い、読者がチュートリアルとして試すことが出来るよう配慮しました。

このトピックではjava言語のサンプルとして下記の2つを提供しています。

ファイル名 内容
NamedPipeSingle.java シングルスレッド版
NamedPipeMulti.java マルチスレッド版

シングルスレッド版

シングルスレッド版をMTUとともに動作させたときのイメージは下記の通りです。


NamedPipeSingleは名前付きパイプ経由でダイレクトに送られてくるCSVデータを一つ一つ画面へ表示します。
処理がシリアルに進むので、ある表のCSV表示処理が終わるまで次のCSV表示処理を開始することが出来ません。
すべての処理が終わるまでに掛かる時間は、それぞれの表を一つずつアンロードしたときの時間の合計です。

マルチスレッド版

マルチスレッド版をMTUとともに動作させたときのイメージは下記の通りです。シングル版と重複している名称は省略しています。


NamedPipeMultiは名前付きパイプ経由でダイレクトに送られてくるCSVデータを一斉に画面へ表示します。
処理がパラレルに進むので、ある表のCSV表示処理が終わる前に次のCSV表示処理が開始されます。
すべての処理が終わるまでに掛かる時間は、それぞれの表を一つずつアンロードしたときの時間の合計を並列度で割った数より少し多くなります。

名前付きパイプ(Named-pipe)

名前付きパイプはファイルのようにプログラムから利用できる、OSの資源です。Windows の場合は CreateNamedPipeというAPIを使って作ることができます。
下記の書式に基づく名前を与えることが出来ます。pipename の部分が任意に指定できますが、全体で256文字を超えてはならない制限があります。
\\.\pipe\pipename
java プログラムは名前付きパイプを、あたかもファイルシステム上のファイルを扱うかのごとく、java.io.File クラスのオブジェクトから操作することが出来ます。
MTU は環境変数 STREAM_LOCATOR へ

named_pipe://\\.\pipe\{C}.{X}

という文字列を指定することにより、出力データを名前付きパイプへ送ることが出来るようになります。

これはバージョン4.1から登場した新機能です。

準備

  1. プログラム実行に必要なSCOTT スキーマはSQL*Plus を使ってSYSかSYSTEMユーザでログインしプロンプトで@?/rdbms/admin/utlsampl.sqlを実行して作成しておきます。
  2. JDK をインストールします。バージョン 5 以上をお使いください。最新のバージョンはオラクル社のサイトからダウンロードすることが出来ます。
  3. java 用のビルド環境および実行環境として環境変数 JAVA_HOME と環境変数 PATH を設定します。 
  4. 製品のサンプル版をダウンロードしてインストール後、 [スタートメニュー] – [PLUMSIX] – [MTU v4.1 xx-bit xxxx版] にある [プログラミング・サンプル集]というショートカットを選択します。
  5.  
  6. エクスプローラが開き、サンプルプログラムが表示されます。javaの拡張子のファイル任意のフォルダへ保存します。
  7. コマンドプロンプトを開き、サンプルプログラムを保存したフォルダへチェンジした後、コンパイルします。
  8. javac NamedPipeSingle.java
    javac NamedPipeMulti.java
    

    コンパイルに失敗する場合は、環境変数 JAVA_HOME や PATH が正しいことをチェックしてみてください。

  9. [スタートメニュー] – [PLUMSIX] – [MTU v4.1 xx-bit xxxx版] にある [コマンド・プロンプト]というショートカットを選択します。
  10. メモ帳などの使い慣れたエディタを使ってカレントフォルダにあるenv.batを開きます。
  11. notepad env.bat

    下記に示した環境変数の値を書き換えます。

    環境変数名 出荷時初期値 書き換える値
    USERID SYSTEM/MANAGER SYSTEM/パスワード@DB別名
    LISTTABLE EMP,DEPT,SALGRADE
    STDOUT 0x0000 0x0001
    STREAM_LOCATOR named_pipe://\\.\pipe\{C}.{X}
    SKIPSCR true
    EMBED_COLUMN_NAME ,:”:

  12. カレントディレクトリにある install.bat スクリプトを実行して製品コンポーネントをインストールします。
  13. 次のようなメッセージが表示され製品コンポーネントがインストールされます。

    C:\ProgramData\PLUMSIX\mtu_4.1\x64>install.bat
    Multi-threaded Unloader version 4.11
    (c) 2003 Plumsix Co.,Ltd. All rights reserved.
    Windows 7 Professional Service Pack 1 (build 7601), 64-bit native
    Number of NUMA nodes: 1
    Number of physical processor packages: 1
    Number of processor cores: 2
    Number of logical processors: 2
    Number of processor L1/L2/L3 caches: 4/1/0
    製品コンポーネントのインストールを実行します...
    クライアント・キャラクタセットID は 838 (JA16SJISTILDE) です。
    ネット・サービス名:mtu01 へユーザ名:SYSTEM として接続します。
    しばらくお待ちください...
    Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 - 64bit Production
    With the Partitioning, OLAP and Data Mining options
    接続先は SYSTEM@xxx (ネット・サービス名:mtu01) です。
    サーバー・タイプは DEDICATED です。
    監査接続IDは 4799234 です。
    サーバーの日付時刻は 2012-10-24 18:34:09 です。
    対象スキーマは SCOTT です。
    以下の製品コンポーネントを SYSTEM スキーマへインストールしました。
    APLDDP01 (PACKAGE), APLDDT01 (TABLE), APLDDT02 (TABLE), APLDDT03 (TABLE), APLDDT04 (TABLE)
    データベース接続用スクリプトを作成しました。C:\ProgramData\PLUMSIX\mtu_4.1\x64\input\_connect.sql
    アンインストール用スクリプトを作成しました。C:\ProgramData\PLUMSIX\mtu_4.1\x64\input\_drp_prod.sql
    パブリックシノニム作成用スクリプトを作成しました。C:\ProgramData\PLUMSIX\mtu_4.1\x64\input\_cre_pub_syn.sql
    パブリックシノニム削除用スクリプトを作成しました。C:\ProgramData\PLUMSIX\mtu_4.1\x64\input\_drp_pub_syn.sql
    処理対象となった表の総数      :            5
    ラウンドトリップ(回)        :           28
    出力バイト総数(kbytes)      :         2.51
    所要時間(秒)                :         0.34
    処理速度(kbytes/秒)         :         7.44
    正常終了しました。
    

実行

これで準備が整いました。それでは実行してみましょう。

  1. start コマンドを実行してコマンドプロンプトをもう一つ表示させます。サンプルプログラムを格納したフォルダへCDコマンドを使ってチェンジします。
  2. CD /d サンプルプログラムを格納したフォルダ
  3. 元のコマンドプロンプトでカレントディレクトリにあるunload.batスクリプトを実行します。次のようなメッセージが表示され、MTU が開始します。しかし、名前付きパイプの読み取り側がまだいないので途中でハングします。
  4. C:\ProgramData\PLUMSIX\mtu_4.1\x64>unload
    Multi-threaded Unloader version 4.11
    (c) 2003 Plumsix Co.,Ltd. All rights reserved.
    Windows 7 Professional Service Pack 1 (build 7601), 64-bit native
    Number of NUMA nodes: 1
    Number of physical processor packages: 1
    Number of processor cores: 2
    Number of logical processors: 2
    Number of processor L1/L2/L3 caches: 4/1/0
    表データファイル出力を実行します...
    
    #### 途中略 ####
    
    空領域サイズチェックの閾値は 1024Mバイトです。
    データファイルと制御ファイルを出力中です。[1561M] C:\ProgramData\PLUMSIX\mtu_4.1\x64\output
    ストリームの位置:named_pipe://\\.\pipe\{C}.{X}
    TableName                     :      NumRows:  Elapse:Rows/s:Bytes:  B/s:   Len
    DEPT                          :    Executing
    

  5. ハングしたら start コマンドで開始しておいたコマンドプロンプトへ移り、シングルスレッド版の java プログラムを実行します。
  6. java プログラムがパイプ経由でCSVデータを受け取り始めるとデータを画面に表示します。

    C:\TEMP\java_test>java NamedPipeSingle
    *** Start: \\.\pipe\EMP.dat ***
    "EMPNO","ENAME","JOB","MGR","HIREDATE","SAL","COMM","DEPTNO"
    7369,"SMITH","CLERK",7902,"19801217000000",800,,20
    7499,"ALLEN","SALESMAN",7698,"19810220000000",1600,300,30
    7521,"WARD","SALESMAN",7698,"19810222000000",1250,500,30
    7566,"JONES","MANAGER",7839,"19810402000000",2975,,20
    7654,"MARTIN","SALESMAN",7698,"19810928000000",1250,1400,30
    7698,"BLAKE","MANAGER",7839,"19810501000000",2850,,30
    7782,"CLARK","MANAGER",7839,"19810609000000",2450,,10
    7839,"KING","PRESIDENT",,"19811117000000",5000,,10
    7844,"TURNER","SALESMAN",7698,"19810908000000",1500,0,30
    7900,"JAMES","CLERK",7698,"19811203000000",950,,30
    7902,"FORD","ANALYST",7566,"19811203000000",3000,,20
    7934,"MILLER","CLERK",7782,"19820123000000",1300,,10
    *** End:   \\.\pipe\EMP.dat ***
    *** Start: \\.\pipe\DEPT.dat ***
    "DEPTNO","DNAME","LOC"
    10,"ACCOUNTING","NEW YORK"
    20,"RESEARCH","DALLAS"
    30,"SALES","CHICAGO"
    40,"OPERATIONS","BOSTON"
    *** End:   \\.\pipe\DEPT.dat ***
    *** Start: \\.\pipe\SALGRADE.dat ***
    "GRADE","LOSAL","HISAL"
    1.E+00,7.E+02,1.2E+03
    2.E+00,1.201E+03,1.4E+03
    3.E+00,1.401E+03,2.E+03
    4.E+00,2.001E+03,3.E+03
    5.E+00,3.001E+03,9.999E+03
    *** End:   \\.\pipe\SALGRADE.dat ***
    

    同時にハングしていたMTUが再開します。

    データファイルと制御ファイルを出力中です。[1561M] C:\ProgramData\PLUMSIX\mtu_4.1\x64\output
    ストリームの位置:named_pipe://\\.\pipe\{C}.{X}
    TableName                     :      NumRows:  Elapse:Rows/s:Bytes:  B/s:   Len
    EMP                           :           12:  316.70:    0.: 727.:   2.:   60.
    DEPT                          :            4:  316.73:    0.: 124.:   0.:   31.
    SALGRADE                      :            5:    0.06:   87.: 152.:2666.:   30.
    処理対象となった表の総数      :            3
    ラウンドトリップ(回)        :           83
    出力バイト総数(kbytes)      :         2.17
    所要時間(秒)                :       317.59
    処理速度(kbytes/秒)         :         0.01
    正常終了しました。
    

  7. 再び手順 2 へ戻り、再び unload.bat を実行してみましょう。今度は手順 3 でマルチスレッド版の NamedPipeMulti を実行してみます。
  8. データファイルと制御ファイルを出力中です。[1560M] C:\ProgramData\PLUMSIX\mtu_4.1\x64\output
    ストリームの位置:named_pipe://\\.\pipe\{C}.{X}
    TableName                     :      NumRows:  Elapse:Rows/s:Bytes:  B/s:   Len
    EMP                           :           12:    6.68:    1.: 727.: 108.:   60.
    SALGRADE                      :            5:    6.69:    0.: 152.:  22.:   30.
    DEPT                          :            4:    6.69:    0.: 124.:  18.:   31.
    処理対象となった表の総数      :            3
    ラウンドトリップ(回)        :          136
    出力バイト総数(kbytes)      :         2.17
    所要時間(秒)                :         7.42
    処理速度(kbytes/秒)         :         0.29
    正常終了しました。
    

    startコマンドの画面でjava NamedPipeMulti を実行します。

    C:\TEMP\java_test>java NamedPipeMulti
    *** Start: \\.\pipe\EMP.dat ***
    *** Start: \\.\pipe\DEPT.dat ***
    *** Start: \\.\pipe\SALGRADE.dat ***
    Waiting...
    "EMPNO","ENAME","JOB","MGR","HIREDATE","SAL","COMM","DEPTNO"
    7369,"SMITH","CLERK",7902,"19801217000000",800,,20
    7499,"ALLEN","SALESMAN",7698,"19810220000000",1600,300,30
    7521,"WARD","SALESMAN",7698,"19810222000000",1250,500,30
    7566,"JONES","MANAGER",7839,"19810402000000",2975,,20
    7654,"MARTIN","SALESMAN",7698,"19810928000000",1250,1400,30
    7698,"BLAKE","MANAGER",7839,"19810501000000",2850,,30
    7782,"CLARK","MANAGER",7839,"19810609000000",2450,,10
    7839,"KING","PRESIDENT",,"19811117000000",5000,,10
    7844,"TURNER","SALESMAN",7698,"19810908000000",1500,0,30
    7900,"JAMES","CLERK",7698,"19811203000000",950,,30
    7902,"FORD","ANALYST",7566,"19811203000000",3000,,20
    7934,"MILLER","CLERK",7782,"19820123000000",1300,,10
    *** End:   \\.\pipe\EMP.dat ***
    "GRADE","LOSAL","HISAL"
    1.E+00,7.E+02,1.2E+03
    2.E+00,1.201E+03,1.4E+03
    3.E+00,1.401E+03,2.E+03
    "DEPTNO","DNAME","LOC"
    4.E+00,2.001E+03,3.E+03
    5.E+00,3.001E+03,9.999E+03
    10,"ACCOUNTING","NEW YORK"
    *** End:   \\.\pipe\SALGRADE.dat ***
    20,"RESEARCH","DALLAS"
    30,"SALES","CHICAGO"
    40,"OPERATIONS","BOSTON"
    *** End:   \\.\pipe\DEPT.dat ***
    Synchronyzed.
    

  9. 以上

まとめ

いかがでしたか?プログラムはうまく動いたでしょうか。シングルスレッド版のほうは、おおよそ予想通りの動きをしたかと思います。
それと比べると、マルチスレッド版のほうは毎回、CSVデータの表示される順序が変わるため同時実行されているという実感が沸きます。
サンプルインスタンス表の行数のオーダーでははっきりした差がありませんが、行数が数百万といったオーダーを超えるとマルチ・スレッド+名前付きパイプ接続の効果が現れてきます。
このチュートリアルを参考にOracle Database 上にある大量データを並列処理でスムーズに処理する java プログラム開発に皆さんもチャレンジしてみてはいかがでしょうか。

最後に、マルチスレッド・プロセスをプログラミングする上でのポイントをまとめます。

  1. 排他制御を使って他のスレッドが進入できない区間を設ける必要があるので、実効並列度は同時スレッド数より少なくなりがちです。
  2. このような区間のことを「クリティカルセクション」と呼びますが、サンプルプログラムでは synchronized 修飾子という java の 処理系が直接サポートする排他制御の機能を使って実現しています。コンソールはプロセスに一つしかないため、乱れの無い画面表示を得るには System.out.println 関数の呼び出しをクリティカルセクションに置く必要があります。
  3. クリティカル・セクションを必要以上に広げすぎると実効並列度の低下を招くので注意して下さい。
  4. オブジェクトの作りすぎ/壊しすぎに注意しましょう。これらの操作はヒープへのアクセスを伴うので並列化できません。
  5. 多くのオブジェクトが java 側の処理で一度に生成されるので、メモリを消費しすぎることの無いようコンストラクタの作り方に工夫が必要です。
  6. アンロード対象が多数ある場合、セマフォを使って構築されるオブジェクト数の上限をカットすると省メモリに効果的です。
by 開発1号