さくらVPSでApache Cassandra 0.7でいろいろデータを取得してみる



昨日の「さくらVPSでApache Cassandra 0.7の動的キースペースを作成してみる」の続きで、「get_slice」「multiget」「multiget_slice」「batch_mutate」を確認してみる。 
「multiget」に関しては、Cassandra WikiAPIページを参照していると「Deprecated in 0.6 - use multiget_slice instead」とあるので省きました。
 

「get_slice」を試す(setSlice_range)

「get_slice」は、SliceRangeクラスで抽出範囲を指定する方法と、setColumn_namesメソッドで取得対象カラムを
指定するやり方があるみたいです。(他に取得方法があるかは、調査不足です。)
ここでは、SliceRangeクラスを利用して、カラムの値を取得してみます。
 

// ColumnParent には ColumnFamily 名または ColumnFamily/SuperColumn 名を指定
ColumnParent columnParent = new ColumnParent("Standard2");
 
SliceRange sliceRange = new SliceRange();
 
// "a" から "m" までを取得
sliceRange.setStart("a".getBytes("UTF8"));
sliceRange.setFinish("m".getBytes("UTF8"));
 
SlicePredicate slicePredicate = new SlicePredicate();
slicePredicate.setSlice_range(sliceRange);
 
// get_slice を呼ぶ
List<ColumnOrSuperColumn> results = client.get_slice(ByteBuffer.wrap(key.getBytes("UTF-8")), 
                        columnParent, 													                         slicePredicate, 													                         ConsistencyLevel.ONE);

for (int i = 0; i < results.size(); i++) {
	ColumnOrSuperColumn result = results.get(i); 
	Column col = result.column;
	System.out.printf("[%d] カラム名:[%s] 値:[%s] タイムスタンプ:[%s]\n", 
						i + 1,
						new String(col.getName(), "UTF8"),
						new String(col.getValue(), "UTF8"),
						new Date(col.timestamp));
}

 

「get_slice」を試す(setColumn_names)

 
「get_slice」で、setColumn_namesメソッドを利用するやり方を紹介します。
 

// ColumnParent には ColumnFamily 名または ColumnFamily/SuperColumn 名を指定
ColumnParent columnParent = new ColumnParent("Standard2");
 
List<ByteBuffer> colNames = new ArrayList<ByteBuffer>();
colNames.add(ByteBuffer.wrap("first".getBytes("UTF-8")));
colNames.add(ByteBuffer.wrap("last".getBytes("UTF-8")));
colNames.add(ByteBuffer.wrap("age".getBytes("UTF-8")));

slicePredicate.setColumn_names(colNames);
 
SlicePredicate slicePredicate = new SlicePredicate();
slicePredicate.setSlice_range(sliceRange);
 
// get_slice を呼ぶ
List<ColumnOrSuperColumn> results = client.get_slice(ByteBuffer.wrap(key.getBytes("UTF-8")), 
                        columnParent, 													                         slicePredicate, 													                         ConsistencyLevel.ONE);

for (int i = 0; i < results.size(); i++) {
	ColumnOrSuperColumn result = results.get(i); 
	Column col = result.column;
	System.out.printf("[%d] カラム名:[%s] 値:[%s] タイムスタンプ:[%s]\n", 
						i + 1,
						new String(col.getName(), "UTF8"),
						new String(col.getValue(), "UTF8"),
						new Date(col.timestamp));
}

  

「multiget_slice」を試す

「multiget_slice」は、複数のキーを対象にデータを取得することができます。
 

テストデータを追加する

キーをもう一つ追加しました。

set Standard2['sasakinozomi']['first'] = 'nozomi'
set Standard2['sasakinozomi']['last'] = 'sasaki'
set Standard2['sasakinozomi']['age'] = '22'

 
インサート結果は、下記の通りです。
 

[default@Keyspace1] get Standard2['sasakinozomi'];
=> (column=age, value=3232, timestamp=1294970184260000)
=> (column=first, value=6e6f7a6f6d69, timestamp=1294970172148000)
=> (column=last, value=736173616b69, timestamp=1294970178051000)
Returned 3 results.

 

テストデータ「michibatajessica」「sasakinozomi」を取得する

取得したいデータのキーを指定して、一気に取得します。
 

SliceRange sliceRange = new SliceRange();
// 取得カラムの範囲を指定。全部取得する場合は空の byte 配列を指定
sliceRange.setStart(new byte[0]);
sliceRange.setFinish(new byte[0]);

SlicePredicate slicePredicate = new SlicePredicate();
slicePredicate.setSlice_range(sliceRange);

List<ByteBuffer> rowKeys = new ArrayList<ByteBuffer>();
rowKeys.add(ByteBuffer.wrap("michibatajessica".getBytes("UTF-8")));
rowKeys.add(ByteBuffer.wrap("sasakinozomi".getBytes("UTF-8")));

Map<ByteBuffer, List<ColumnOrSuperColumn>> results = client.multiget_slice(rowKeys, 
columnParent, 
slicePredicate, 
ConsistencyLevel.ONE);

for (Map.Entry<ByteBuffer, List<ColumnOrSuperColumn>> entry : results.entrySet()) {
ByteBuffer key = entry.getKey();
List<ColumnOrSuperColumn> list = entry.getValue();
for (int i = 0; i < list.size(); i++) {
ColumnOrSuperColumn result = list.get(i); 
Column col = result.column;
System.out.printf("key:[%s] [%d] カラム名:[%s] 値:[%s] タイムスタンプ:[%s]\n",
key,
i + 1,
new String(col.getName(), "UTF8"),
new String(col.getValue(), "UTF8"),
new Date(col.timestamp));
}
System.out.println("--------------------");
}

ここで気づいたのは、「cassandra-cli」でデータをsetすると、何故か時刻がおかしくなる模様。
(どこのデータを取得しているのか分かりません。)

よって、複数データを一気に投入できる「batch_mutate」メソッドも試してみた。
 

「batch_mutate」を試す

「batch_mutate」メソッドの引数は、下記の通りです。

void batch_mutate(Map<ByteBuffer, Map<String, List<Mutation>>> mutation_map, ConsistencyLevel consistency_level)

引数は、2つですが、「Mutation」のMAPを作成するのが一苦労です。
 

// key と カラムファミリの Map の Map を作成
Map<ByteBuffer, Map<String, List<Mutation>>> mutaionMap = new HashMap<ByteBuffer, Map<String, List<Mutation>>>();

// レコードを挿入
long timestamp = System.currentTimeMillis();

// 登録・更新したい値から Mutation オブジェクトの List を作成
List<Mutation> columns = new ArrayList<Mutation>();
columns.add(toMutation("first", "Jessica", timestamp));
columns.add(toMutation("last", "Michibata", timestamp));
columns.add(toMutation("age", "25", timestamp));

List<Mutation> columns2 = new ArrayList<Mutation>();
columns2.add(toMutation("first", "nozomi", timestamp));
columns2.add(toMutation("last", "sasaki", timestamp));
columns2.add(toMutation("age", "22", timestamp));


// カラムファミリと Mutation のリストの Map を作成
Map<String, List<Mutation>> innerMap = new HashMap<String, List<Mutation>>();
Map<String, List<Mutation>> innerMap2 = new HashMap<String, List<Mutation>>();

innerMap.put(cfName, columns);
innerMap2.put(cfName, columns2);

// 取り合えず同じ内容を key だけ別にして追加。
mutaionMap.put(ByteBuffer.wrap("michibatajessica3".getBytes("UTF-8")), innerMap);
mutaionMap.put(ByteBuffer.wrap("sasakinozomi3".getBytes("UTF-8")), innerMap2);

client.batch_mutate(mutaionMap, ConsistencyLevel.ONE);

 
キー「michibatajessica」「sasakinozomi」は、timestampが現在時刻では無くなっていたので
removeメソッドを利用して一度、削除しました。

その後、「batch_mutate」メソッドを利用しても新規にデータが挿入されず。
「cassandra-cli」を実行してもデータを挿入できず。

「remove」「insert」は、うまく動作するのですが、なぜかうまくデータが挿入できなくなりました。
(理由調査中)

とりあえず、キーを変更したところうまく「batch_mutate」で挿入できるようになりました。
 

まとめ

ここまで試したThrift APIをまとめてみます。

 
表1. APIまとめ

No メソッド名 説明
1 get 特定キーからカラムまたはスーパーカラム1つを取り出す
2 insert 特定キーで1カラムデータを作成する
3 remove 特定キーで1カラムデータを削除する
4 get_slice(setSlice_range) スライスされたカラムまたはスーパーカラムの集合を返す(範囲抽出)
5 get_slice(setColumn_names) スライスされたカラムまたはスーパーカラムの集合を返す(カラム指定)
6 multiget_slice 複数キーに対して複数のget_sliceを並列にこなす
7 batch_mutate 複数キーに対して複数のデータを作成/更新/削除する