PlatformのAPI開発を担当している狭間と申します。Platformで使用しているCassandraのデータ設計時に気をつけていることを実際に起きた事例を交えてお話したいと思います。Cassandraのverstionは1.1.5を使用していて、100台構成のクラスタを組んでいます。ピーク帯ではおよそ50000write/sec、40000read/secのリクエストを処理していて、およそ45TBのデータを保持しています。そのような条件下で発生した事例と対処方法を紹介させていただきます。Platformでの使用例Cassandraでは以下の様なデータ構造をもっていてデータの最小単位はColumnで以下の様な構造をしています。ttlCassandraではColumnFamilyと呼ばれる単位でデータがまとめられいて(RDBでいうテーブルに近いイメージ)、ColumnFamilyは複数のrowを持ち、rowは複数のColumnを持ちます。keyとColumnのnameを指定してデータを取得します。RowkeyとColumnのnameを指定してデータを保存、取得するというのが最も単純な使い方ですが、nameがソートされて格納されることを利用して範囲指定で一括取得などを行うことができます。nameはデータ型を指定でき(long、UTF-8など)、そのデータ型に応じたソート順で保存されます。我々は使っているデータ構造として代表的なものを幾つか紹介します。開発当初よく使用していたのは単純なKVSの用に利用するパターンです(開発初期は他のデータストアの採用も検討していたので)。keyを指定してvalueを取得するような形で利用します。valueにはJson形式のデータを格納していることが多いです。nameをフィールド名にしてRDBのテーブルの様な形でデータを保存しています。timeとatomicなカウンターを組み合わせています)。この形は気をつけないととんでもないことになります。nameを検索キーの様な形で使用することもできます。name指定でデータを取得できるのでIDが1のユーザとIDが2のユーザがフレンドかどうかの判定を行ったりすることができます。運用当初ははデータ量も少なくそれほど問題にはならなかったのですが、サービスが拡大するにつれ様々な問題が発生しました。ここではアプリ起因となって発生した問題とその時に行った対策を紹介したいと思います。特定のnodeのみ負荷があがる運用開始してしばらくしてから、特定のnodeのみ負荷があがるという現象が発生しました。データのレプリケーション数分のnodeの負荷だけが上がっていたので、そのnodeが保持しているデータに問題があるだろうとあたりを付け調査した所、特定のデータのreadが大量に発生していたことが原因でした。nameを検索キーとして使用するよう形)で保存していて、全ユーザーのアクセスが一つのキーに集中していました。そもそもなんでそんなものをCassandraに入れるんだよと言われると何も言えないのですが、、、データ反映に遅延があっても問題ないデータだったのでアプリ側でキャッシュすることでとりあえずしのいでいます。Cassandraはwriteに比べreadは弱く、またkey単位での分散なのでこういったデータは保存しないほうが良いです(お前が言うなって感じでしょうが)。移行はできていないのですが、別のデータストアへの移行を検討中です。データ設計の話ではないのですが、監視はやっておいた方がよいと思います。当時は監視もきちんと行われていなくて、実際に問題が発生するまで気づかず、発生してからもJMXの値を手動で更新して調査を行っていたので、原因の特定にも時間がかかりました。現在はGrothForecastなどで異常なデータアクセスなどがないか監視を行っているので、こういった問題が起きそうかどうか事前に把握できるようになっています。監視周りに関しては弊社の@oranieがブログに書いていますので、興味があるようでしたらそちらもご覧になって下さい。特定のnodeのデータが肥大化する運用を開始して半年くらい経ってからnodeごとのデータの偏りが顕著になってきて、一部nodeだけディスク容量が足りなくなるという自体が発生しました。肥大化していたのはユーザのフレンドのアクティビティを時系列に保存しているところでした。timeにしてユーザのフレンドが何らかのアクションを起こすたびにcolumnを追加するという方式で実装していて、ttlを設定して一定時間経過後に消えるようにはしていたのですが、それ以外に特に制限はなくcolumnが無限に増えていくような設計になっていました。そのためフレンドが多いユーザのrowは大量のカラムを持つこととなり、そういったユーザのデータを保持しているnodeのみデータが肥大化するという結果になりました。keyでの分散を行っているcassandraではcolumnが際限なく増えていくような設計は避けたほうがいいと思います。keyを日別に作ってRowを分割するような方法で対応しました。Columnの削除はtombstoneと呼ばれるプロパティが設定され、データが読み込まれなくなるだけでcompactionが行われるまで削除されません。columnの追加、削除を大量に行われるような機能を作ると気づいたらディスクが足りないなんてことになるかもしれないので、そのような機能を作る場合には注意しておいたほうがよいと思います。Columnの削除にはもう一つ気をつけたほうがいいことがあって、それはtombstoneがついたcolumnは読み込み時にスキップされているだけであるということです。nameの範囲指定で一括取得するような処理を行う場合には注意が必要です。先ほど紹介したフレンドのアクティビティの例ではColumnのnameが時刻でttlによってColumnの削除を行っているので、範囲指定で一括取得する際に現在時刻とColumn追加時に指定しているttlから残っているColumnの中で最も古いものの時刻を計算して、必ずその範囲内で取得するようにしています。まとめCassandraは構築が比較的容易でデータ構造も柔軟なため非常に扱いやすいように見えますが、実際に運用を始めると一筋縄でいかない部分が多いです。Cassandraの採用を検討している方、実際に運用されている方の少しでも参考になれば嬉しいです。したほうがよい等のご意見があれば参考にさせていただきたいのでぜひ教えて下さい。Reserved.スパムを報告お問い合わせ利用規約ヘルプ。