前回はCassandraへデータを投入する方法を説明しました。今回はデータの更新と削除についてご説明します。
データ更新ではタイムスタンプに注意
Cassandraにおけるデータ更新は、
以下のコードでそれを確かめてみましょう。
Date oldDate = new SimpleDateFormat("yyyy/MM/dd").parse("1970/01/01");
long oldTimestamp = oldDate.getTime();
Cassandra.Client client = new Cassandra.Client(protocol);
try {
final String key = "sample_update1";
final String columnName = "update_hoge";
String value = "original_value";
long timestamp = System.currentTimeMillis();
final ColumnPath columnPath = new ColumnPath(COLUMN_FAMILY);
columnPath.setColumn(columnName.getBytes());
// 1件カラムをインサート
client.insert(KEYSPACE, key, columnPath, value.getBytes(),
timestamp, ConsistencyLevel.ONE);
value = "update_value";
timestamp = System.currentTimeMillis();
// 同一カラムを更新日付を後にしてアップデート
client.insert(KEYSPACE, key, columnPath, value.getBytes(),
timestamp, ConsistencyLevel.ONE);
value = "update2_value";
timestamp = oldTimestamp;
// 同一カラムを更新日付を前にしてアップデート
client.insert(KEYSPACE, key, columnPath, value.getBytes(),
timestamp, ConsistencyLevel.ONE);
} catch (InvalidRequestException e) {
...
Cassandra-cliを使って確認してみてください。
cassandra> get Keyspace1.Standard1['sample_update1'] => (column=7570646174655f686f6765, value=update_value, timestamp=1277138220269) Returned 1 results.
データが更新日付が後のもので上書きされているのがわかるでしょうか。
実行してみるとわかるのですが、
batch_
1行ならremove、まとめてならbatch_mutateで削除
次はデータの削除です。データを削除するには以下の2つの方法があります。
- removeメソッド削除する
- batch_
mutateでDeletionオブジェクトをセットして削除する
1行を削除するような処理は前者で問題ありません。一方、
ではそれぞれの詳細を見ていきましょう。
removeメソッドで削除する場合の3つの選択肢
removeメソッドで削除する場合、
- ① キーで指定したロウ内のカラムを削除する
- ② キーで指定したロウ内のスーパーカラムを削除する
- ③ そのキーのロウごと削除する
これらは、
①キーで指定したロウ内のカラムを削除する
以下は特定カラムを削除するサンプルコードの抜粋です。このサンプルでは、
注目していただきたいことが2点あります。1点目は、
2点目は、
Date oldDate = new SimpleDateFormat("yyyy/MM/dd").parse("1970/01/01");
long oldTimestamp = oldDate.getTime();
Cassandra.Client client = new Cassandra.Client(protocol);
try {
final String key = "sample_delete1";
final String columnName = "delete_hoge";
String value = "削除のサンプルです";
long timestamp = System.currentTimeMillis();
final ColumnPath columnPath = new ColumnPath(COLUMN_FAMILY);
columnPath.setColumn(columnName.getBytes());
// 1件カラムをインサート
client.insert(KEYSPACE, key, columnPath, value.getBytes(),
timestamp, ConsistencyLevel.ONE);
// 古いタイムスタンプで削除しようとすると、削除できない.特に例外も出ない。
client.remove(KEYSPACE, key, columnPath, oldTimestamp,
ConsistencyLevel.ALL);
// 削除されていない事を確認
ColumnOrSuperColumn ret = client.get(KEYSPACE, key, columnPath,
ConsistencyLevel.ONE);
Column retColumn = ret.getColumn();
String retName = new String(retColumn.getName());
String retValue = new String(retColumn.getValue());
System.out.println("カラムキー : " + retName);
System.out.println("カラム値: " + retValue);
System.out.println("タイムスタンプ: " + retColumn.getTimestamp());
timestamp = System.currentTimeMillis();
// タイムスタンプを更新して削除.
client.remove(KEYSPACE, key, columnPath, timestamp,
ConsistencyLevel.ALL);
// 削除されていてカラムが既に無いのでNotFoundException発生
ret = client.get(KEYSPACE, key, columnPath, ConsistencyLevel.ONE);
②キーで指定したロウ内のスーパーカラムを削除する
スーパーカラムを削除する場合は、
さらにスーパーカラム内の特定カラムだけを削除することもできます。下記のサンプルでは、
final String superColumnName = "hogehoge1";
final String key = "delete_one_column";
long timestamp = System.currentTimeMillis();
// スーパーカラムをインサートしておく
insertSuperColumn(superColumnName, key, client, timestamp);
// データがきちんと入ったかgetメソッドで確認する
ColumnPath columnPath = new ColumnPath(COLUMN_FAMILY);
columnPath.setSuper_column(superColumnName.getBytes());
ColumnOrSuperColumn ret = client.get(KEYSPACE, key, columnPath,
ConsistencyLevel.QUORUM);
SuperColumn superColumn = ret.getSuper_column();
System.out.println("削除前");
for (Column c : superColumn.getColumns()) {
String retName = new String(c.getName());
String retValue = new String(c.getValue());
System.out.printf("\tカラムキー :\t%s\n", retName);
System.out.printf("\tカラム値:\t%s\n", retValue);
System.out.printf("\tタイムスタンプ:\t%s\n", c.getTimestamp());
}
timestamp = System.currentTimeMillis();
// 削除用のColumnPathを作る。スーパーカラム内の特定カラムが削除したいのでそのカラム名"fooKey"を指定する。
ColumnPath deletePath = new ColumnPath(COLUMN_FAMILY);
deletePath.setSuper_column(superColumnName.getBytes());
deletePath.setColumn("fooKey".getBytes());
client.remove(KEYSPACE, key, deletePath, timestamp,
ConsistencyLevel.ALL);
// 削除されたかどうか確認する。このケースでは、スーパーカラム自体は残り、カラム"fooKey"だけがなくなっている。
ret = client
.get(KEYSPACE, key, columnPath, ConsistencyLevel.QUORUM);
superColumn = ret.getSuper_column();
System.out.println("削除後");
for (Column c : superColumn.getColumns()) {
String retName = new String(c.getName());
String retValue = new String(c.getValue());
System.out.printf("\tカラムキー :\t%s\n", retName);
System.out.printf("\tカラム値:\t%s\n", retValue);
System.out.printf("\tタイムスタンプ:\t%s\n", c.getTimestamp());
}
③キーで指定したロウごと削除する
キーで指定したロウごと削除することもできます。この方法が最も簡単なのですが、
final String superColumnName = "sample1";
final String key = "delete_entire_row";
long timestamp = System.currentTimeMillis();
// スーパーカラムをインサートしておく
insertSuperColumn(superColumnName, key, client, timestamp);
ColumnPath columnPath = new ColumnPath(COLUMN_FAMILY);
columnPath.setSuper_column(superColumnName.getBytes());
// スーパーカラムがデータとしてあるか確認する。
ColumnOrSuperColumn ret = client.get(KEYSPACE, key, columnPath,
ConsistencyLevel.QUORUM);
SuperColumn superColumn = ret.getSuper_column();
for (Column c : superColumn.getColumns()) {
String retName = new String(c.getName());
String retValue = new String(c.getValue());
System.out.println("カラムキー : " + retName);
System.out.println("カラム値: " + retValue);
System.out.println("タイムスタンプ: " + c.getTimestamp());
}
timestamp = System.currentTimeMillis();
// ロウごと削除する。
client.remove(KEYSPACE, key, new ColumnPath(COLUMN_FAMILY),
timestamp, ConsistencyLevel.ALL);
// ロウごと消えているため、NotFoundExceptionが発生する。
client.get(KEYSPACE, key, columnPath, ConsistencyLevel.QUORUM);
batch_mutateメソッドで削除する
removeメソッドでも十分機能的には事足りるのですが、
- スーパーカラムを設定して削除する
- SlicePredicate
(カラム名の集合か、 一定数のレンジ) を設定して削除する
では実際のサンプルコードを見てみましょう。
final String superColumnName = "sample1";
final String key = "super_sample1";
long timestamp = System.currentTimeMillis();
// スーパーカラムをインサート
insertSuperColumn(superColumnName, key, client, timestamp);
ColumnPath columnPath = new ColumnPath(COLUMN_FAMILY);
columnPath.setSuper_column(superColumnName.getBytes());
// データが投入されたか確認
ColumnOrSuperColumn ret = client.get(KEYSPACE, key, columnPath,
ConsistencyLevel.QUORUM);
SuperColumn superColumn = ret.getSuper_column();
for (Column c : superColumn.getColumns()) {
String retName = new String(c.getName());
String retValue = new String(c.getValue());
System.out.printf("\tカラムキー \t\t:%s\n", retName);
System.out.printf("\tカラム値\t\t:%s\n", retValue);
System.out.printf("\tタイムスタンプ\t:%s\n", c.getTimestamp());
}
timestamp = System.currentTimeMillis();
// batch_mutateの構造を作って、Deletionをセットする
Map<String, Map<String, List<Mutation>>> mutationMap = new HashMap<String, Map<String, List<Mutation>>>();
Map<String, List<Mutation>> map = new HashMap<String, List<Mutation>>();
List<Mutation> list = new ArrayList<Mutation>();
Mutation mutation = new Mutation();
// Deletionを作成し、スーパーカラムで所定の名前を指定する
Deletion deletion = new Deletion(timestamp);
deletion.setSuper_column(superColumnName.getBytes());
mutation.setDeletion(deletion);
list.add(mutation);
map.put(COLUMN_FAMILY, list);
mutationMap.put(key, map);
// 削除実行。
client.batch_mutate(KEYSPACE, mutationMap, ConsistencyLevel.ALL);
// 削除されたことを確認する。削除されていればNotFoundExceptionが発生する。
client.get(KEYSPACE, key, columnPath, ConsistencyLevel.QUORUM);
ほとんどのコードが前回のbatch_
Deletionオブジェクトを設定するだけで削除処理とみなされるので、
分散データベースでの削除について
ここまで削除処理を見てきました。比較的簡単に実行できるのでAPIレベルで難しいところはないと思います。ところが、
Cassandraは複数ノードを立てて運用する場合、
また、
- ユーザの検索処理が終わった後に各ノード間の整合性をとる
(read repair) - 一時的に死んでいたサーバに後からデータの同期を行う
(hinted handoff) - 非同期に定期的に各ノードが保持しているデータを同期する
(antientropy)
このため、
この削除用の特別な値のことをトゥームストーン
ただし、
なお、
- サンプルファイル
(サンプルコード5点/7. 50KB)
以上、