大量ログデータ処理メモ
仕事でログデータの処理を実装したが、想定よりもずっと大きいサイズのログデータだったり実装の仕方に問題があったりしたことで色々やらかしてしまった。また同じことをやっても問題なので、メモを残しておく。
ちなみにログデータは以下のように日付のディレクトリがあり、その下に1時間ごとのデータが出力されている形式になっている。
- yyyyMM
- dd
- 00.log
- 01.log
...
処理は並列で
始めにファイル一覧を取得してそこから最初のファイルから順に処理して…と対応していたが、今回の案件では処理するファイルの順番は関係なかったため、一つずつ対応する理由がなかった。一つずつ対応していた時は数時間かかっていたが、最終的に並列処理することで数十分で完了させることが出来た。
今回の件に限らず、基本的に処理は並列で行えるかどうかというのは常に意識すべきだと強く感じた。
トランザクションに気を付ける
最初にファイル一覧を取得してからすべてのファイルを処理し、その結果を保存するまで全て同一のトランザクションで処理しようとしていた。テスト環境はログのファイルサイズが非常に小さかったため気が付かなかったが、本番環境だと一つ一つのサイズが大きかったためDB接続がタイムアウトした。さすがに何も考えなさすぎだったと反省している。
最終的に各ファイルの結果を保存するときのみトランザクションを張るようにした。DBのトランザクションを張る時間はなるべく短い時間で、というのは昔聞いていたはずだが、すっかり忘れていたせいで失敗してしまった。
処理の途中で失敗した場合を考慮する
最初は全ての処理が完了した場合に取り込み成功した旨を記録していたが、この処理だと一部の取り込みが失敗した場合に全ファイルを再確認する必要があった。ファイルサイズが小さければ全ファイルを再確認するくらい問題ないが、ファイルサイズが大きいと再読み込みに非常に時間がかかるため、これをはじめから考慮すべきだった。
最終的にはどの時刻のファイルまで取り込めたかどうかを記録することで読み込めなかったファイルを後から判別できるようにした。
データをメモリに読み込みすぎない
最初は各ファイルを
- すべてメモリに読み込む
- 読み込んだ内容の中から必要な物のみ取り出す
- 求める形式に修正する
- 修正したデータを保存する
という流れで実装していたが、これだと実質1と3のデータをメモリに抱え込むことになるためOut Of Memoryが発生した。
ファイルサイズが大きいとメモリに抱え込むのは無理なため途中でStreamを取得する方式に変更した。これだとファイルを開きっぱなしにはなるが必要な部分しか読み込まない(3で修正したデータしかメモリには持たない)ため、Out Of Memoryが発生することは無くなった。
まとめ
改めてみるとどれも基本的なことが出来ていないことで失敗しているが、やはりログファイルというものは基本的に大きいサイズになるというのが分かっていなかったのが一番の問題だったのかなという気がする。
テスト環境では動いたが本番環境では動かない…というありがちな失敗をやらかす羽目になったため、もう少し事前に本番環境のデータを事前に確認するという流れを心掛けるべきだったのかもしれない。
Discussion