AerospikeのCLIでデータをいじってみた時のメモ

# 概要
データの構成や使い勝手の理解を深める為、以前構築した[Aerospike](http://www.aerospike.com/)の環境（[Aerospike環境構築メモ on GCP](http://qiita.com/toremo/items/b3b19831a4235f259301)）を使ってCLIベースでいじってみたのでそのメモ。

# Aerospikeのデータ構成
![Configuring Aerospike - Part 2 2015-10-07 18-40-31.png](https://qiita-image-store.s3.amazonaws.com/0/63069/f3eb4955-2557-82ba-780d-f774eb433b28.png "Configuring Aerospike - Part 2 2015-10-07 18-40-31.png")

|区分|定義|
|:--|:--|
|Cluster|Aerospikeのクラスター|
|Node|クラスターの構成要素|
|Namespace|独立したストレージ領域。複数定義可能だがNamespaceを跨いだ参照はできない。RBDでいうところの`database`や`scheme`に近い。|
|Set|Recordをグルーピングした領域。RDBの`Table`に近い。|
|Record|データを出し入れする単位。ValueはJSON形式。RDBの`Raw`に近い。|
|Bin|データの最小単位。Recordを構成するパーツ。Jsonの要素に当たる。|

## namespace
* [公式ドキュメント](http://www.aerospike.com/docs/operations/configure/namespace/)
* namespaceはConfigファイル（`/etc/aerospike/aerospike.conf`）を編集して再起動すれば追加や変更ができる
* 設定可能な項目は[Configuration Reference](http://www.aerospike.com/docs/reference/configuration/)を参照
* 設定の反映にはNodeの再起動が必要
* 全てのNodeに設定を反映しないとクラスター間の整合が取れなくなる
* 全Nodeに一括反映みたいのはなさそう
* よって、そんなに頻繁に作ったり消したりするものではなさそう

# データを操作する(AQL版)
[Aerospike　Query Language(AQL)](http://www.aerospike.com/docs/tools/aql/)というSQLライクにデータを操作できるコマンドラインツールが用意されているのでそれを使ってデータを操作してみる。

## AQLのプロンプトを開く
```
$ aql
```
デフォルトのAerospikeサーバ内にいるならこれでOK。
外部からアクセスする場合はマニュアルに則って`-h`や`-p`などのオプションを指定する。

## データの挿入
```
INSERT INTO <ns>[.<set>] (PK, <bins>) VALUES (<key>, <values>)
```

* SQLの構文とほぼ同じだけど、スキーマレスなので`(PK, <bins>)`部分の省略ができないのと、（たぶん）1コマンドで1レコードしか入らない。

例：

```
aql> insert into test.person (PK, name, age, gender, phone_number) values (1, 'Taro', 20, 'M', 09011112222)
OK, 1 record affected.
```

## データの参照
```
SELECT * FROM <ns>[.<set>]
```

例：

```bash
aql> select * from test.person
+----------+-----+--------+--------------+
| name     | age | gender | phone_number |
+----------+-----+--------+--------------+
| "Hanako" | 22  | "F"    | 9011113333   |
| "Taro"   | 20  | "M"    | 9011112223   |
+----------+-----+--------+--------------+
2 rows in set (0.022 secs)
```
namespaceまたはsetのデータを参照する。

```bash
aql> select name, age from test.person
+----------+-----+
| name     | age |
+----------+-----+
| "Hanako" | 22  |
| "Taro"   | 20  |
+----------+-----+
2 rows in set (0.022 secs)
```
取得項目をBinで絞ることも可能

```bash
aql> select * from test.person where PK = 1
+--------+-----+--------+--------------+
| name   | age | gender | phone_number |
+--------+-----+--------+--------------+
| "Taro" | 20  | "M"    | 9011112223   |
+--------+-----+--------+--------------+
1 row in set (0.001 secs)
```
WHERE句はデフォルトではPK（Record-Key）の等価比較でのみ可能
Bin単位で絞るにはインデックスを貼らないといけない(後述)

**[note]
AQLでは現状Record-Keyを参照できないらしい
更新や削除にはKeyが必要なことを考えるとAQLでデータ操作するには限界がある**

## データの更新
* UPDAET構文はない。
* 同じKey名でINSERTすれば上書かれるので更新になる。
* ただし、更新範囲はRecord単位ではなくBin単位で実行される為、更新しなかったBinはそのまま残る。

例：

```bash
aql> select * from test.person where pk = 1
+--------+-----+--------+--------------+
| name   | age | gender | phone_number |
+--------+-----+--------+--------------+
| "Taro" | 20  | "M"    | 9011112222   |
+--------+-----+--------+--------------+
1 row in set (0.001 secs)

aql> insert into test.person (PK, age) values (1, 21)OK, 1 record affected.

aql> select * from test.person where pk = 1
+--------+-----+--------+--------------+
| name   | age | gender | phone_number |
+--------+-----+--------+--------------+
| "Taro" | 21  | "M"    | 9011112222   |
+--------+-----+--------+--------------+
1 row in set (0.000 secs)
```

## データの削除
```
DELETE FROM <ns>[.<set>] WHERE PK=<key>
```
削除にはPK（Record-key）が必要

例：

```bash
aql> delete from test.person where PK = 1
OK, 1 record affected.
```

## インデックスを貼る
```
CREATE INDEX <index> ON <ns>[.<set>] (<bin>) <type>
```

* インデックス名はnamespaceでユニークでないければならない
* typeは`NUMERIC` or `STRING`

例：

```bash
aql> create index idx_person_age_numeric on test.person (age) numeric
OK, 1 index added.
```

インデックスを貼るとインデックスが貼られたBinでwhere句を使える。

例：

```bash
aql> select * from test.person where age = 18
+--------+-----+--------+--------------+
| name   | age | gender | phone_number |
+--------+-----+--------+--------------+
| "Jiro" | 18  | "M"    | 9022223333   |
+--------+-----+--------+--------------+
1 row in set (0.002 secs)
```

```bash
aql> select * from test.person where age between 18 and 20
+--------+-----+--------+--------------+
| name   | age | gender | phone_number |
+--------+-----+--------+--------------+
| "Jiro" | 18  | "M"    | 9022223333   |
| "Taro" | 20  | "M"    | 9011112222   |
+--------+-----+--------+--------------+
2 rows in set (0.002 secs)
```

# データを操作する（ascli版）
単純にKey指定でデータの出し入れや参照をするだけなら[Aerospike CLI](http://www.aerospike.com/docs/tools/ascli/)
を使っても可能。

## データ操作系
### put
recordの挿入

```
ascli put <ns> <set> <record-key> <record-value>
```
* record-valueはJSON形式。
* 成否は終了コードが0かどうか
* namespace/set/record-keyが全て同一のデータがある場合は上書かれる

例：

```bash
$ ascli put test settest keytest '{"name":"hoge", "age":30, "array":[1,2,3], "map":{"mapkey":"mapvalue"}}'
$ echo $?
0
```

### get
record-valueの取得

```
ascli get <ns> <set> <record-key>
```

例：

```bash
$ ascli get test settest keytest
{"map": { "mapkey": "mapvalue" }, "array": [ 1, 2, 3 ], "name": "hoge", "age": 30}
```

### exists
record-keyの存在確認

```
ascli exists <ns> <set> <record-key>
```

* 存在の有無は終了コードが0かどうか

例：

```bash
$ ascli exists test settest keytest
Key keytest exists.
$ echo $?
0

$ ascli exists test settest keytest2
Key keytest2 does not exist.
$ echo $?
2
```

### remove
recordの削除

```
ascli remove <ns> <set> <record-key>
```

* 成否は終了コードが0かどうか

例：

```bash
$ ascli remove test settest keytest
$ echo $?
0

$ ascli remove test settest keytest
ERROR 2 AEROSPIKE_ERR_RECORD_NOT_FOUND
$ echo $?
2
```

## Scripting
### eval
一連のascliのコマンドを実行できるシンプルなスクリプティング環境を呼び出す。

```
ascli eval
```

* 標準入力からのコマンドを読み込むので適当なFileに書いたコマンドをパイピングして実行もできる
* eval独自のコマンドもある
* コマンドはスタックに保持される
* スタックは1コマンド1000文字までで、1000コマンドまで保持する。

**[Note]
evalはシングルスレッドで動作し、全ての呼び出しをブロックすることに注意する**

evalの独自コマンドは以下。

|コマンド|説明|
|:--|:--|
|:print|最後のコマンドからの統計情報を表示する|
|:reset|蓄積された統計情報をリセットする|
|:repeat n|スタック上の全てのコマンドをn回繰り返す|
|:clear|スタックを空にする|
|:quit|evalから抜ける|

例：

```bash
$ ascli eval
put test evaltest test1 '{"name":"hoge"}'
:print
>>
  time:    0.94500 ms
  success: 1
  failure: 0
:reset
:print
>>
  time:    0.00000 ms
  success: 0
  failure: 0
:clear
get test evaltest test1
{"name": "hoge"}
:reset
:repeat 10
{"name": "hoge"}
{"name": "hoge"}
{"name": "hoge"}
{"name": "hoge"}
{"name": "hoge"}
{"name": "hoge"}
{"name": "hoge"}
{"name": "hoge"}
{"name": "hoge"}
{"name": "hoge"}
:print
>>
  time:    4.98200 ms
  success: 10
  failure: 0
:quit
```

ファイルから実行する場合は以下のような感じでOK。

```bash
$ cat {evalfile} | ascli eval
```

# 所感
単純なデータ操作ならいいけど、ちょっと複雑なことやろうとすると手詰まりそうな感じ。AerospikeはUDFが定義できるみたいなので複雑な処理はそっちでやれってところかな、と思いました。
とりあえず今回の目的はデータ構成の理解なので一旦こんなもんで。
次はClientを使ってごにょごにょやる予定。