- 2007年12 月25日(火)
- PHP
毎度!
話せるシステム屋を目指しています。
tatsumiです。こんにちは。
とある人の話なんですけれどね、
その人は3人いる兄弟の中で唯一離婚経験があるもんだから
正月が嫌で嫌で仕方ないんだそうです。
それと言うのも、正月は親族一同が集まってご飯を食べるのが恒例となっていて
弟達のお嫁さんに囲まれるのが居心地が悪く肩身が狭いそうなんですよ。
まあ、なんて言うか…
これが本当の「バツが悪い」ですね!
(会場がどっかんどっかん爆笑に…)
(なる訳ない!)
すみません…。
こんな小話は置いておいて本題に入りたいと思います。
PHPでバッチ処理を書いてみる
業務系の仕事をやっていると
絶対に避けて通れないのがバッチ処理ですよね。
現在でもバッチ処理はC言語やCOBOLで書かれることが多いようですが、
数億・数十億円といったような大規模なシステムでない限り
PHPを使用していても十分に要件を満たせると思います。
(もちろん、状況によって異なりますが)
ということで、今回はPHP(cli)でバッチ処理に挑戦してみます。
バッチ処理の要件
まず、最初に、バッチ処理に求められるコトを確認しておきましょう。
携わっているシステムによって色々な要件があると思いますが、
- 整合性の担保
データの整合性などが崩れないように処理順序などを正しくスケジューリングする。 - 処理効率性の担保
並行稼動しているシステムへの影響を最小限にしつつ決められた時間内に処理を終える。 - 運用の容易さ
エラー発生時などの調査や再実行が容易にできる。
の以上3点が基本的かつ共通のポイントではないでしょうか?
そのため、ここでは上記3ポイントをどうクリアしていくかを考えていきたいと思います。
整合性の担保
複数の処理を時間だけでスケジューリングすると問題が起こる可能性があります。
例えば、順番に実行する必要がある処理Aと処理Bの2つの処理があり
12:00から処理Aを起動、13:00から処理Bを起動として設定しているとします。

その時、処理Aが13:00までに完了していれば問題はありませんが、
何らかの理由で処理Aが13:00までに完了しなかった場合に障害が発生しますよね。

こういった可能性がゼロでない限り、システムの整合性を担保するためには
処理Aが完了していることを確認してから処理を実行する必要があります。
具体的にはデータベースやファイルなどにフラグを持つか、
処理Aが終了した時に処理Bを呼び出すようにしてあげれば良い訳ですが
処理を完了しているかを確認するのは面倒なので
今回は処理Aが完了した時に処理Bを呼び出そうと思います。
PHPで処理を順番に呼び出したいときは
<?php
//処理A
require_once(”processA.php”);
//処理B
require_once(”processB.php”);
?>
こんな感じで書くのが簡単ですが、
これだと処理Aと処理Bのモジュール結合度が密になってしまうので
もう少し工夫をしてそれぞれの処理を別のプロセスとして起動するようにします。
<?php
$processList = array(’processA’,'processB’);
foreach($processList as $process){
$result = system(”php “.$process.”.php”);
if( $result !== ’success’ ){
//エラー処理を書いたり
}
}
?>
こうすることでよりモジュール結合度を疎にすることが出来ますし、
メモリの開放を適切に行っていない個所があったとしても
処理が変わる度に初期化されますので比較的安心です。
また、万が一それぞれの処理でPHPのFATALエラーが発生したとしても
上記のように書いておけばエラー処理を書くことが出来ますので
各処理をオブジェクトにして順次実行するよりも良いでしょう。
処理効率性の担保
ほとんどの場合、バッチ処理で最も時間がかかるのが
データベースアクセスの発生する個所だと思われます。
まず、適切にINDEXを設定して、SQLのチューニングをして下さい。
積極的にストアドプロシージャなどを使用するのも良いかも知れません。
それ以外の個所で処理速度が遅い場合は
アルゴリズムを勉強して無駄のないコードを書くしかありません。
繰り返し処理、判断処理をいかに減らせるかを考えて下さい。
ただし、そこに貴重なリソースを割くくらいなら
サーバのメモリやCPUを増強した方が安上がりかも知れません。
それで無理ならPHPを選択したことを後悔して下さい。
運用の容易さ
バッチ処理はログを取ることが特に重要ですが、
ただログを取るだけでなく処理結果レポートとして
メールなどで受け取れるようにしておくと問題発生時の素早い対応が可能となります。
また、何らかの事情により再実行時をする必要が出てきたときに
ソースコードなどを編集するのを避けるため
下記のように外部パラメータで実行したい処理を指定できるようにしておきます。
<?php
$processList = array( ‘processA’ , ‘processB’ );//実行する処理を外部パラメータとして受け取りも可能に
//php execute.php processA とかで実行する
$argv = $_SERVER['argv'];
unset($argv[0]);
foreach( $argv as $targetProcess ){
//不正な入力があったら強制終了
if( !in_array($targetProcess,$processList) ){
die(’不正な入力です’);
}
$processListTemp[] = $targetProcess;
}
if( count($processListTemp)>0 ){
$processList = $processListTemp;
}foreach($processList as $process){
$result = system(”php “.$process.”.php”);
if( $result !== ’success’ ){
//エラー処理を書いたり
}
}//レポートメール送信処理を書いたり
?>
ちなみに私が過去に経験した案件では実行したい処理だけでなく
メールの送信先やログレベルの指定、処理日付の指定や接続DBなどを
外部パラメータとして指定できるようにしていました。
そして、指定されたパラメータを各処理プロセスで共有できるように
セッションファイルのようなものを作成していました。

簡単に手順を説明すると以下のようになります。
手順
- 「起動処理」がパラメータを受け取り、セッションファイルを作成する
- 1が終われば、実行したい処理を指定して「制御処理」を呼び出す
- 「制御処理」はセッションファイルを読込み、指定された処理を実行する
- 「制御処理」は3の結果を「起動処理」に返す
- 2に戻る
- 全ての処理が終われば「起動処理」がレポートメールを送信して終了する
ちょっと面倒臭く感じるかも知れませんが、
一度このような枠組みを用意しておくと
処理を追加したり修正することが非常に簡単になります。
もし、行き当たりばったりにベタベタと作ってしまっている方がいらっしゃるようでしたら
これを参考にして作ってみてください。
きっと少し楽になるはずです。
以上、tatsumiでした。
- Newer: MondoRescueによるバックアップとリカバリ(CentOS5.1)
- Older: XHTML&CSSレイアウトについて

