データベース応答時間の計測

システムのパフォーマンスを見るために、一定時間をおいてトランザクションを発生させ、その所要時間の変化を計測しようという意図でプログラムを作ってみました。
メインフレームなどではよくやることのようです。

応答時間計測用プログラム (SQL & RPG)

ある SQL文を定期的に実行し、その応答時間をデータベースに書き込んでいくというだけのシンプルなプログラムです。
データベースに蓄積された応答時間の変遷で、システムの DB アクセスの状態をモニターしよう、という意図で作成されています。
例えばこの応答時間が蓄積されるデータベースにトリガーをセットして、ある応答時間を超えたらメッセージを出す、とかね。(もちろんこのプログラムの中で応答時間を見てメッセージ等を出す、っていうのでもいいでしょう)

今回は F1 というデータ件数が一定の基準となるテーブルを F2 というテーブルにコピーするスピードを計っていますが、ここはどちらかと言えばアプリケーションの中からモデルとなるトランザクションをサンプリングして実行させる、という方がいいかもしれません。
いずれにせよ、ここで対象とするデータベースのトランザクションはテーブル、SQL文、ともに何でもよく、計測するにふさわしいサンプルとなっているかどうかが採用基準となるでしょう。

また、今回のケースでも F1 というテーブルのデータ件数を調整して、ピーク時に邪魔にならないが、システムのデータベースマネジャーの状態をちゃんと反映できる程度にはいい時と悪い時の差がでる (実際このプログラムでは 1秒以上かかるトランザクションでないと 0 になってしまいます。単にコーディングが面倒だっただけなのですけど ... )、といったようにしないといけません。このあたりは稼動させるシステムによって状況が異なりますから、時間をかけて試行錯誤で決めるしかないでしょう。

ちなみに、そのことにあまり深い意味はないのですが ILE ではない RPG で書いています。
私、前にもどこかに書いた気がするのですが RPG はとにかくろくすっぽわからないのでなんか変な書き方してたらごめんなさい。

     H        1
      *
     I            DS
     I                                        1   60CLOCKB
     I                                        1   20HB
     I                                        3   40MB
     I                                        5   60SB
     I            DS
     I                                        1   60CLOCKE
     I                                        1   20HE
     I                                        3   40ME
     I                                        5   60SE
      *
     C                     Z-ADD*ZERO     CLOCKB
     C                     Z-ADD*ZERO     CLOCKE
      *
     C                     TIME           CLOCKB  60
      *
     C/EXEC SQL
     C+ DELETE FROM F2
     C/END-EXEC
      *
     C/EXEC SQL
     C+ INSERT INTO F2 SELECT "KEY", TEXT, PACK, ZONE FROM F1
     C/END-EXEC
      *
     C                     TIME           CLOCKE  60
      *
      *計測時間の算出STR
      *
     C           SE        IFGT SB
     C           SB        IFEQ *ZERO
     C                     Z-ADDSE        SW      30
     C                     ELSE
     C           SE        SUB  SB        SW
     C                     ENDIF
     C                     ENDIF
      *
     C           SE        IFLT SB
     C           SE        IFEQ *ZERO
     C           60        SUB  SB        SW
     C                     ELSE
     C           60        SUB  SB        SW1     30
     C           SW1       ADD  SE        SW
     C                     ENDIF
     C                     ENDIF
      *
     C           SB        IFEQ SE
      *
     C           ME        IFGT MB
     C           MB        IFEQ *ZERO
     C                     Z-ADDME        MW      20
     C                     ELSE
     C           ME        SUB  MB        MW
     C                     ENDIF
     C           MW        MULT 60        SW2     30
     C                     ENDIF
      *
     C           ME        IFLT MB
     C           ME        IFEQ *ZERO
     C           60        SUB  MB        MW
     C                     ELSE
     C           60        SUB  ME        MW1     20
     C           MW1       ADD  MB        MW
     C                     ENDIF
     C           MW        MULT 60        SW2
     C                     ENDIF
      *
     C           SW1       ADD  SW2       SW
      *
     C                     ENDIF
      *
      *計測時間の算出END
      *
      *
      *結果の書き出し
      *
     C/EXEC SQL
     C+ INSERT INTO R1  (PROC_TIME, TIME_BEGAN, TIME_END)
     C+      VALUES(:SW, :CLOCKB, :CLOCKE)
     C/END-EXEC
      *
     C                     SETON                     LR

結果を格納するテーブル R1 は、PROC_TIME (NUMERIC 3,0)、TIME_BEGAN (NUMERIC 6,0)、TIME_END (NUMERIC 6,0) という 3つのフィールドを持っています。

コンパイルは CRTSQLRPG コマンドで行いました。

制御用プログラム (CL)

上のプログラムはとりあえず RSPBYSQL と名付けて使用しますが、それを呼び出す CL プログラムは例えば以下のようになります。

最初の一回は時間がかかるのとあまりサンプルにならない、ということと毎日結果のデータベースはクリアして別の累積データベースに別途コピーする、という考えで、この CL プログラムでは最初のレスポンス計測プログラムを実行後に CLRPFM コマンドを実行しています。ですので、この辺は運用に依存します。

全体として、計測を行うインターバルと終了させる時間が来ているかどうかを判断しているだけのプログラムです。
インターバル、終了時間はデータエリアやファイル等の変数で持たせてもいいですね。

            DCL        VAR(&TIME) TYPE(*CHAR) LEN(6)                   
            DCL        VAR(&TIMED) TYPE(*DEC) LEN(6 0)                 
            ADDLIBLE   LIB(TESTDB)                                   
            MONMSG     MSGID(CPF0000)                                  
            CALL       PGM(TESTDB/RSPBYSQL)                        
            CLRPFM     FILE(TESTDB/R1)                               
            DLYJOB     DLY(20)                                         
LOOP:       CALL       PGM(TESTDB/RSPBYSQL)                        
            DLYJOB     DLY(300)                                        
            RTVSYSVAL  SYSVAL(QTIME) RTNVAR(&TIME)                     
            CHGVAR     VAR(&TIMED) VALUE(&TIME)                        
            IF         COND(&TIMED > 200000) THEN(GOTO CMDLBL(EXIT))   
            GOTO       CMDLBL(LOOP)                                    
EXIT:       ENDPGM                                                     

ということで終了時間だけをデータエリアに変えてみたのがこちらです。TESTDB ライブラリーの中にタイプ *DEC で長さ 6 0 の ENDTIME というデータエリアを作成し、そこに終了時間をセットして参照してもらう、というやり方です。
終了時間が着たら単に EXIT というだけでなく、保管用累積結果データベースへのコピーなんかをここで行ってもいいですね。

また、上の例ではその都度のシステムの状況を見るために、メモリーの状況はあえてその時任せにしてありますが、SETOBJACC コマンドなどを使用して必ずメモリーにセットしてからプログラムを実行する、または必ずメモリーからパージしてプログラムを実行する、などを行った方がより正確なデータがとれる、ということもあるかもしれません。厳密に言えば、どのプールに対してモニターしたいか、にもよるでしょう。
例えばこの計測ツールを QBATCH で稼動させているが実は見たいのはオンラインのレスポンスだというような場合を考えましょう。実際に、日中バッチ用のプールがガラガラで対話型用のプールがたいへんなことになっている場合は、ただ「その時任せ」では少なくともメモリに関してはあまり状況を反映しないことになる (バッチ用のプールでは対象ファイルがメモリーに置き続けられるが、対話型用のプールではそういう状況ではない場合、条件の有利さが異なってきてしまいますね) と考えられます。
ということで、とりあえずサンプルとして SETOBJACC コマンドも入れてみました。これを入れたら最初に一回プログラムを呼んで、というロジックはいらないかな。

            DCL        VAR(&TIME) TYPE(*CHAR) LEN(6)                   
            DCL        VAR(&TIMED) TYPE(*DEC) LEN(6 0)                 
            DCL        VAR(&ENDTIME) TYPE(*DEC) LEN(6 0)               
            ADDLIBLE   LIB(TESTDB)                                   
            MONMSG     MSGID(CPF0000)                                  
            CALL       PGM(TESTDB/RSPBYSQL)                        
            CLRPFM     FILE(TESTDB/R1)                               
            DLYJOB     DLY(20)  
LOOP:       SETOBJACC  OBJ(TESTDB/F1) OBJTYPE(*FILE) POOL(*PURGE)                                                               
            CALL       PGM(TESTDB/RSPBYSQL)                        
            DLYJOB     DLY(300)                                        
            RTVSYSVAL  SYSVAL(QTIME) RTNVAR(&TIME)                     
            CHGVAR     VAR(&TIMED) VALUE(&TIME)                        
            RTVDTAARA  DTAARA(TESTDB/ENDTIME) RTNVAR(&ENDTIME)    
            IF         COND(&TIMED > &ENDTIME) THEN(GOTO CMDLBL(EXIT)) 
            GOTO       CMDLBL(LOOP)                                    
EXIT:       ENDPGM                                                     

インターバル (つまり DLYJOB コマンドの引数になる待ち時間) もこの CL プログラムの引数でもらうとか何かのデータエリアにセットしておいてその都度見る、などの方法が考えられます。試行錯誤の段階の時なんかはデータエリアかなんかから都度読むってやり方の方がインターバルも適宜変えられるからいいかもしれませんね。

[Top Pageに戻る]

Ads by TOK2