集計=バッチ処理?

このエントリーをはてなブックマークに追加

弊社製品 Marketing Platform には、Webページのアクセス履歴を取得したり、メール文面にあるURLをクリックした履歴を取得したり、メールを開封した履歴を取得したりする機能がある。そしてこれらの履歴データを統計情報として活用するため、どのWebページが何回アクセスされたか、どのURLが何回クリックされたかといった集計を行っている。

少し前のリリースで、これらの履歴データを集計していたバッチ処理を廃止して、アクセスを受け付けるたびにリアルタイムで集計するように変更した。これはリアルタイムにすることが目的だったのではないので副産物ではあるが、なかなか意味のある変更ではないかと思っている。

何かのミーティングの折にボスが「うちの製品はリアルタイム性が重要」というコメントをしていたからだ。
これは検索機能についての言葉ではあったが(検索結果を早く出すためにあらかじめ検索結果を持っておく方式はダメだよねーとかいう話だったような)、そもそも集計バッチ処理を待たないとデータが反映されないのでは、リアルタイム性なんて生まれない。
そういう意味では、本来の目的になかったことでも、製品コンセプトに合うという意味では良かったと思っている。

結果がすぐに見えるというのは、開発もしやすいし、テストもしやすいので、そういった意味でも非常にラクになった。


これまでデータが本体と別サーバにあってAPI経由でやり取りしていたので、新機能のために集計データの格納場所を変えたいということがそもそもの目的であった。
最初はデータの格納場所を変えることだけ考えていたので、単にアクセスを受け付けた後に適切な場所へ転送することを考えた。しかし、大量アクセスがただ転送されるだけでは、転送先サーバでは今までなかったアクセスが増えるだけである。ムダに負荷を増加させるわけにはいかないので、アクセス集中や集計処理による高負荷を避けることが目的に加わった。



今、集計処理を変えるという話題になると、Hadoopを使えば?と思う人は多いかもしれない。処理を小分けするので、確かに大量データを処理しきれずにハングアップすることはなくなるだろう。
しかし、効果のある形で使うには大量ノードが必要だ。また、集計対象をある程度の期間で区切ってバッチ処理に回すとき、例えば1時間に1回のバッチ処理をすると、集計処理が1時間以内に終わってくれないと厄介なことになる。もちろんそこまで考慮して、一定期間で区切るのではなくて1つ終わったら次に進むようにすればいいのだが、どんどん後続の処理が遅れていくのも困る。

インフラチームとも相談したところ、今後ますますWebアクセス履歴の需要は増えていくだろうということで、今よりも大量のアクセスを想定したシステムにしたいとのこと。また、一時的にアクセスが集中することもあるだろうけど、最大のときに合わせると普段ムダが生まれてしまうので、AWS (Amazon Web Services) などを使って一時的にサーバを増やしたりしたいとのことだった。

こうしたことから、急にアクセス数が増えてもサーバの負荷が一定になるように、キューシステムを採用することになった。キューにタスクを登録する側(アクセスを受け付ける側)と、キューからタスクを取り出して処理する側(アプリから検索したりできるようにDBに記録する側)を切り離しておくことで、どちらか足りないほうだけを増やすことができる。


残る課題は集計処理をどのタイミングで行うか?ということだった。これまでの延長で考えると、DBに記録してからバッチ処理で集計することになる。しかし、わざわざキューシステムを取り入れて負荷を一定にしているのに、集計バッチで負荷を発生させるわけにはいかない。
ということで、キューシステムが負荷を一定にしてくれるのに便乗して、キューからタスクを取り出してDBに入れる間に、集計も一緒にやってしまうことにした。集計処理もほとんどが数字をプラス1するだけの簡単なものなので、1アクセスごとに処理することも可能だろうという判断だ。

そしてバッチ処理は消えた。

キューシステムとして採用したのは既に利用実績があった Gearman (Gearman::WorkerGearman::Clientだ。Gearman はキューをメモリ上に持つため、キューに登録されたタスクが消えることがある。キューをDB上に持つ  TheSchwartz を使うことも考えたが、もともとアクセス履歴をDB上に持っているので、これをそのままバックアップとして利用し、もしタスクが消えてもキューに登録し直せばいいと考えることにした。



Gearman 自体の使い方については特殊なことはしていないが、Gearmanの不確実なところをカバーするようにしている。アクセス履歴がGearmanを通じて正常に集計されて登録まで完了したら、逆にアプリ本体側からGearmanに完了フラグをつけるというタスクを登録し、完了フラグをアクセス履歴DBに書き戻している。こうして、定期的に完了フラグがついていないものだけを Gearman に再登録することで、処理を再実行できるようにしている。


処理するデータ量の桁がいくつか変わったら、またシステム構成も見直すことになるだろうが、現在のところとりあえずこれで動いている。


参考

次の記事
« Prev Post
前の記事
Next Post »
Related Posts Plugin for WordPress, Blogger...