前回はCassandraへデータを投入する方法を説明しました。今回はデータの更新と削除についてご説明します。
データ更新ではタイムスタンプに注意
Cassandraにおけるデータ更新は、実はインサートのときと同じAPIを使います。1点だけ違うのは、更新の場合はタイムスタンプが重要になることです。タイムスタンプが以前に入れたものより後になっていないと、データが更新されません。
以下のコードでそれを確かめてみましょう。
Cassandra-cliを使って確認してみてください。
データが更新日付が後のもので上書きされているのがわかるでしょうか。
実行してみるとわかるのですが、Cassandraでは更新日付がきちんとデータ投入時より後になってないと更新できません。例外も出さないため、注意が必要です。タイムスタンプは日付をきちんととらなくてはいけない理由がこのあたりにあります。
batch_mutateを使った場合でも、カラムがあれば同様に更新できます。
1行ならremove、まとめてならbatch_mutateで削除
次はデータの削除です。データを削除するには以下の2つの方法があります。
- removeメソッド削除する
- batch_mutateでDeletionオブジェクトをセットして削除する
1行を削除するような処理は前者で問題ありません。一方、まとめて削除したいようなケースでは後者のほうがbatch_mutateメソッドで、削除を表現するDeletionオブジェクトを使って削除するのがよいです。ThriftのAPIを1行ずつコールしないので、効率よく処理できます。
ではそれぞれの詳細を見ていきましょう。
removeメソッドで削除する場合の3つの選択肢
removeメソッドで削除する場合、以下の3つの選択肢があります。
- ① キーで指定したロウ内のカラムを削除する
- ② キーで指定したロウ内のスーパーカラムを削除する
- ③ そのキーのロウごと削除する
これらは、インサートのときと同じように、すべてColumnPathで指定します。ColumnPathにはカラムファミリの指定が必須ですが、それ以外は上記の①~③に応じて設定する内容を変えます。
①キーで指定したロウ内のカラムを削除する
以下は特定カラムを削除するサンプルコードの抜粋です。このサンプルでは、カラムを1つ追加して、そのデータを削除することを試みています。
注目していただきたいことが2点あります。1点目は、ColumnPathを使う際に、setColumnで明示的に削除するColumnを指定していることです。
2点目は、更新のときと同様に古いタイムスタンプで削除を試みますが、それはうまくいかずにデータは削除されないことです。削除されたデータを検索メソッドであるgetで読み出そうとすると、NotFoundExceptionが発生します。タイムスタンプをインサート時より後にしてあげるときちんとデータが削除されます。
②キーで指定したロウ内のスーパーカラムを削除する
スーパーカラムを削除する場合は、ColumnPathでsetSuperColumn()でスーパーカラムを指定してください。
さらにスーパーカラム内の特定カラムだけを削除することもできます。下記のサンプルでは、ColumnPath#setSuperColumn()とsetColumn()を併用して、スーパーカラム内の特定カラムだけを削除しています。
③キーで指定したロウごと削除する
キーで指定したロウごと削除することもできます。この方法が最も簡単なのですが、ColumnPathでsetSuperColumn()もsetColumn()せずに削除するとロウそのものを削除することになる点に注意してください。
batch_mutateメソッドで削除する
removeメソッドでも十分機能的には事足りるのですが、スーパーカラムなどをもっと大量に削除したい場合はbatch_mutateを使ったほうが効果的です。batch_mutateのDeletionにある以下の2つのオプションが役立ちます。
- スーパーカラムを設定して削除する
- SlicePredicate(カラム名の集合か、一定数のレンジ)を設定して削除する
では実際のサンプルコードを見てみましょう。
ほとんどのコードが前回のbatch_mutateを使ったインサートのときと同じなのですが、ポイントはDeletionを使うところです。
Deletionオブジェクトを設定するだけで削除処理とみなされるので、ユーティリティやフレームワークは比較的楽に作ることができそうです。もし削除の処理をライブラリやフレームワークなどで統一するのであれば、batch_mutateで統一するほうが実行効率がよくなります。
分散データベースでの削除について
ここまで削除処理を見てきました。比較的簡単に実行できるのでAPIレベルで難しいところはないと思います。ところが、Cassandraが分散データベースである点を考慮すると、実はこの削除という処理は厄介なことになってきます。
Cassandraは複数ノードを立てて運用する場合、レプリケーション数を3かそれ以上にするのが一般的です。つまり複数ノード間でデータのレプリケーションを行うようになっています。
また、Cassandraは一貫性モデルをイベンチュアルコンシステンシに沿って設計されているので、内部的には以下の処理が行われています。
- ユーザの検索処理が終わった後に各ノード間の整合性をとる(read repair)
- 一時的に死んでいたサーバに後からデータの同期を行う(hinted handoff)
- 非同期に定期的に各ノードが保持しているデータを同期する(antientropy)
このため、本当にただデータを削除するだけだと、ノード間の整合性でデータが復旧されてしまったり、システムとしてのデータ一貫性のレベルが低下してしまう危険性があります。これは避けねばなりません。そこでCassandraでは、データを単純に物理削除するのではなく、データ削除の特別な値におきかえる処理(いわゆる論理削除)を最初に行っています。
この削除用の特別な値のことをトゥームストーン(墓標とか墓石という意味です)と呼んでいます。この仕組みのおかげで、レプリカ間でもトゥームストーンが伝播するので、削除された値であることがすぐにわかります。
ただし、これだけだと、削除されたデータが物理的には削除されないため、ディスクを圧迫していくばかりです。そこで、トゥームストーンがついたデータはある一定のタイミングで物理的に削除される仕組みになっています。このタイミングはCassandraのstorage-conf.xmlのGCGraceSecondsで変更することができます。デフォルトは10日です。また、運用管理者の操作によって、永続化されたデータをコンパクトにする処理を行った際にトゥームストーンも削除します。
なお、今回作成したサンプルプログラム全体は、以下からダウンロード可能です。
以上、今回は更新と削除についてまとめました。次回は検索をしてみます。お楽しみに。