- 締切済み
Perl:大容量ログを正常に読み込む方法が知りたい
ある動作のログを出力するシステムの機能改修の立場にある者です。 言語はperlです。 大容量ログを正常に読み込む方法が知りたいのですが、急いでおります。 ログが出力されているファイルが大容量で、 ログ読み込みの始めのうちは問題なく扱えていますが、 ログをどんどん読み込んでいくと 「Internal Server Error」となります。 スクリプトは下記の通りです。 open LOGFILE,○○ while($** = <LOGFILE>){ ログの文字列をいろいろ切り出して、配列に格納。 必要なログ情報を取り出せたらループから出る。 } close(LOGFILE); 上記の配列を扱い、ログを多様な表示スタイルで出力します。 ブラウザでのログ表示で検索条件で指定されたログを抽出できたら 途中でループから逃げているので問題なく動作しております。 しかし、ページを切り替え、ログをどんどん読み込んでいくと (おそらく)配列に格納するメモリが大容量のため エラーになってしまいます。 ※ログ表示で設置している検索条件機能 (1)何ページ目表示指定 (2)1ページあたりのログ表示行数指定 (100行表示で3ページ目を見る場合、201行目から300行が表示されます。) そのためページ切り替え毎にログを格納している配列を破棄?するなど メモリを解放することが必要になる?かと思っています。 しかし、ページ切り替えごとにログの一番初めから読み込み直す必要もあるのでは?とも考えています。 ※ログの一文目からカウンタで順番を数えていて、後でログを格納した配列でキーを指定してログを取り出しています。 検索条件を変えながらでも、かつメモリを解放?しながらログを配列に格納して扱う方法はあるでしょうか。 つまづいてしまって進めません。。 ※メモリを増やすといったパワープレーではなく、 プログラムの書き方でメモリの負荷を少なくして実現させたいです。 もしいい方法をご存知の方がいらっしゃれば、よろしくお願いいたします。 長文失礼いたしました。
- みんなの回答 (5)
- 専門家の回答
みんなの回答
- hanabutako
- ベストアンサー率54% (492/895)
自分だったらその条件を見たときにDBMSの使用を考えます。 ログそのものをDBMSに保存されるようにするか、ログを定期的にDBMSにコンバートします。 テキストファイルでやっている程度のことなのでsqliteを使うだけでもかなりの速度向上を実感できると思います。nページ目を見るというのはSELECT文のOFFSETを使って前のところを飛ばすようにして実現します。無駄にメモリーに読み込まないようにするためにLIMITも使うと良いでしょう。 元のプログラムのままやるとしたら、nページ目をやるときはn-1ページ目までを配列に保存することなく無視すると言うことで実装するしかないでしょうね。
- Wap58
- ベストアンサー率33% (29/87)
おそらくテキストなどに書き出さない限り プログラム終了で変数の内容は全て破棄されます ブロック内に重い処理があるんじゃないですか 出来るだけ関数を使わない処理を考えて下さい
- Gotthold
- ベストアンサー率47% (396/832)
> 201行目以上 "または" 300行目未満 だと、結局 全行対象になるので正しくは > if ( 200 <= $i && $i < 300 ) { > じゃないですかね。 ですね。適当に書いてたら間違えてしまいました。 > このwhileとifの間で使用した配列にはログを読み込めば読み込むほど、 > メモリ使用量が増えていくかと思います。 いや、加工結果をどこか格納したりせずに捨てていればメモリ使用量は増えないです。 書き方がまずくてメモリを食っている可能性もありますが。 そもそもエラーの原因がメモリ不足であることはちゃんと確認しましたか? どちらにしても、使わないデータを加工してしまっているのは無駄なので直した方が良いですね。
- superside0
- ベストアンサー率64% (461/711)
> if( 200 <= i || i < 300 ){ # 201行目から300行目だけ 201行目以上 "または" 300行目未満 だと、結局 全行対象になるので正しくは if ( 200 <= $i && $i < 300 ) { じゃないですかね。 で、本題ですが、 $i = 0; while($** = <LOGFILE>){ ログを切ったり、いじったりして、表示用に改変したログをつくる。 <<★ if ( 行番号を使った表示範囲の絞り込み ) { 表示処理 } $i++; } としているということですよね。 なら、★のところで、 メモリを使うアルゴリズムになってしまっているんじゃないですかね。 使わないデータを加工しても意味がないので、加工処理も行で限定すればよいだけでは? つまり、 $i = 0; while($** = <LOGFILE>){ if ( 行番号を使った表示範囲の絞り込み ) { ログを切ったり、いじったりして、表示用に改変したログをつくる。 表示処理 } $i++; } で。 もしくは、加工部のアルゴリズムを見なおして、メモリ使わない処理に書き換えるか。
- Gotthold
- ベストアンサー率47% (396/832)
> そのためページ切り替え毎にログを格納している配列を破棄?するなど > メモリを解放することが必要になる?かと思っています。 > しかし、ページ切り替えごとにログの一番初めから読み込み直す必要もあるのでは?とも > 考えています。 Internal Server Errorが出るって事は、 サーバ側でCGI経由とかで実行してるんですよね? だったら、ページ切り替えのたびに Perlスクリプトは1から実行されて終了するので、 メモリの内容なんて残りませんよ。 毎回最初からやり直しです。 > 検索条件を変えながらでも、かつメモリを解放?しながら > ログを配列に格納して扱う方法はあるでしょうか。 ちゃんと必要な分だけ配列に格納すれば メモリなんてそんなに食わないのでは? こんな感じで open LOGFILE,○○ i = 0; while($** = <LOGFILE>){ if( 200 <= i || i < 300 ){ # 201行目から300行目だけ #切り出し処理とかはここで push(@配列, $**); #配列に格納 } i++; } close(LOGFILE);
補足
ご回答ありがとうございます! >Internal Server Errorが出るって事は、 >サーバ側でCGI経由とかで実行してるんですよね? その通りです! >ちゃんと必要な分だけ配列に格納すれば すみません、説明が足りませんでした。 表示する行数指定というものは、 すでにもともとのログをいじって改変したものを ブラウザに表示する段階でのログの行数になります。 つまり、 open LOGFILE,○○ i = 0; while($** = <LOGFILE>){ ログを切ったり、いじったりして、 表示用に改変したログをつくる。 if( 200 <= i || i < 300 ){ # 201行目から300行目だけ push(@配列, $**); #配列に格納 } i++; } close(LOGFILE); という流れになっています。 このwhileとifの間で使用した配列にはログを読み込めば読み込むほど、 メモリ使用量が増えていくかと思います。 当方、初心者なもので理解が弱くてすみません。。