CockroachDBのチュートリアルをやってみる

# はじめに

CockroachDBはACIDトランザクションをサポートする分散型のSQLデータベースです。
ちなみにCockroachはゴキブリという意味です。

以下の"Get Started"を参考に、実際にCockroachDBをローカル（3ノード～5ノード）で動かしてみます。

https://www.cockroachlabs.com/docs/stable/install-cockroachdb-linux.html

なお、"Get Started"では以下の操作を実施することになります。

- CockroachDBのインストール
- ローカルクラスターを起動
  - SQLの実行
  - サンプルワークロードの実行
  - Admin UIへのアクセス
  - ノードをダウンさせてみる
  - クラスターをスケール
  - クラスターを停止
- CockroachDBのSQLを学ぶ
  - テーブル一覧を表示
  - テーブルを作成
  - レコードを挿入
  - インデックスを作成
  - テーブルのインデックスを表示
  - テーブルを検索
  - レコードを更新
  - レコードを削除
  - テーブルを削除 

# 環境

使用する環境は以下になります。

- CentOS 7.4
- CockroachDB v19.2.4


# CockroachDBのインストール

Linux用のCockroachDBバイナリをダウンロードし展開します。

```
# wget -qO- https://binaries.cockroachdb.com/cockroach-v19.2.4.linux-amd64.tgz | tar xvz
```

cockroachコマンドをPATH（/usr/local/bin/）が通ったディレクトリにコピーします。

```
# cp -i cockroach-v19.2.4.linux-amd64/cockroach /usr/local/bin/
```

# ローカルクラスターを起動

cockroach startコマンドを使用してローカルクラスターを開始します。

```
# cockroach start --insecure --store=node1 --listen-addr=localhost:26257 --http-addr=localhost:8080 --join=localhost:26257,localhost:26258,localhost:26259 --background
*
* WARNING: RUNNING IN INSECURE MODE!
* 
* - Your cluster is open for any client that can access localhost.
* - Any user, even root, can log in without providing a password.
* - Any user, connecting as root, can read or write any data in your cluster.
* - There is no network encryption nor authentication, and thus no confidentiality.
* 
* Check out how to secure your cluster: https://www.cockroachlabs.com/docs/v19.2/secure-a-cluster.html
*
*
* INFO: initial startup completed.
* Node will now attempt to join a running cluster, or wait for `cockroach init`.
* Client connections will be accepted after this completes successfully.
* Check the log file(s) for progress. 
*
```

先ほどはnode1のノードを起動しました。続いてnode2, node3のノードを起動して、3台構成のクラスターを構成します。

```
# cockroach start --insecure --store=node2 --listen-addr=localhost:26258 --http-addr=localhost:8081 --join=localhost:26257,localhost:26258,localhost:26259 --background
# cockroach start --insecure --store=node3 --listen-addr=localhost:26259 --http-addr=localhost:8082 --join=localhost:26257,localhost:26258,localhost:26259 --background
```

次にcockroach initコマンドでクラスターの初期化を実行します。
コマンドはクラスター内の1台のノードに対して実行します。

```
# cockroach init --insecure --host=localhost:26257
Cluster successfully initialized
```

なお、各ノードの起動情報はログで出力されており、以下のように確認できます。

```
# grep 'node starting' node1/logs/cockroach.log -A 11
CockroachDB node starting at 2020-02-29 23:02:48.899689215 +0000 UTC (took 116.5s)
build:               CCL v19.2.4 @ 2020/02/06 21:55:19 (go1.12.12)
webui:               http://localhost:8080
sql:                 postgresql://root@localhost:26257?sslmode=disable
RPC client flags:    cockroach <client cmd> --host=localhost:26257 --insecure
logs:                /root/node1/logs
temp dir:            /root/node1/cockroach-temp058344301
external I/O path:   /root/node1/extern
store[0]:            path=/root/node1
status:              initialized new cluster
clusterID:           0bd03d0c-e836-4a84-9a18-85b540caa3e3
nodeID:              1
```


## SQLの実行

CockroachDBの組み込みSQLクライアントを使用してクラスタに接続してみます。

```
# cockroach sql --insecure --host=localhost:26257
```

続いて、データベース作成、テーブル作成、レコード挿入、検索までを実行してみます。

```
root@localhost:26257/defaultdb> CREATE DATABASE bank;
CREATE DATABASE

Time: 13.496349ms

root@localhost:26257/defaultdb> CREATE TABLE bank.accounts (id INT PRIMARY KEY, balance DECIMAL);
CREATE TABLE

Time: 17.062985ms

root@localhost:26257/defaultdb> INSERT INTO bank.accounts VALUES (1, 1000.50);
INSERT 1

Time: 18.117957ms

root@localhost:26257/defaultdb> SELECT * FROM bank.accounts;
  id | balance  
+----+---------+
   1 | 1000.50  
(1 row)

Time: 1.435583ms
```

"\q"でクライアントを終了します。

```
root@localhost:26257/defaultdb> \q
```

次にnode2に接続して、同様にテーブルを検索してみます。
node1に接続したときと同じ結果が得られました。


```
cockroach sql --insecure --host=localhost:26258
root@localhost:26258/defaultdb> SELECT * FROM bank.accounts;
  id | balance  
+----+---------+
   1 | 1000.50  
(1 row)

Time: 12.707771ms
```


## サンプルワークロードの実行

CockroachDBには、クライアントトラフィックをシミュレートするための多数のワークロードも組み込まれています。

まず、CockroachDBのワークロードmovrのデータセットをロードします。

```
# cockroach workload init movr 'postgresql://root@localhost:26257?sslmode=disable'
I200229 23:10:02.671365 1 workload/workloadsql/dataload.go:135  imported users (0s, 50 rows)
I200229 23:10:06.086413 1 workload/workloadsql/dataload.go:135  imported vehicles (3s, 15 rows)
I200229 23:10:06.179277 1 workload/workloadsql/dataload.go:135  imported rides (0s, 500 rows)
I200229 23:10:06.275984 1 workload/workloadsql/dataload.go:135  imported vehicle_location_histories (0s, 1000 rows)
I200229 23:10:06.375615 1 workload/workloadsql/dataload.go:135  imported promo_codes (0s, 1000 rows)
```

5分間ワークロードを実行します。

```
# cockroach workload run movr --duration=5m 'postgresql://root@localhost:26257?sslmode=disable'

_elapsed___errors_____ops(total)___ops/sec(cum)__avg(ms)__p50(ms)__p95(ms)__p99(ms)_pMax(ms)__total
  300.0s        0           1389            4.6      4.1      3.4      6.0     31.5     46.1  addUser

_elapsed___errors_____ops(total)___ops/sec(cum)__avg(ms)__p50(ms)__p95(ms)__p99(ms)_pMax(ms)__total
  300.0s        0            463            1.5      7.3      5.8     11.5     39.8    192.9  addVehicle

_elapsed___errors_____ops(total)___ops/sec(cum)__avg(ms)__p50(ms)__p95(ms)__p99(ms)_pMax(ms)__total
  300.0s        0            443            1.5     10.1      8.9     17.8     39.8     62.9  applyPromoCode

_elapsed___errors_____ops(total)___ops/sec(cum)__avg(ms)__p50(ms)__p95(ms)__p99(ms)_pMax(ms)__total
  300.0s        0            136            0.5      4.6      3.8      6.3     32.5     44.0  createPromoCode

_elapsed___errors_____ops(total)___ops/sec(cum)__avg(ms)__p50(ms)__p95(ms)__p99(ms)_pMax(ms)__total
  300.0s        0            323            1.1      4.3      3.8      6.0     28.3     32.5  endRide

_elapsed___errors_____ops(total)___ops/sec(cum)__avg(ms)__p50(ms)__p95(ms)__p99(ms)_pMax(ms)__total
  300.0s        0          87044          290.1      0.9      0.9      1.4      2.5     46.1  readVehicles

_elapsed___errors_____ops(total)___ops/sec(cum)__avg(ms)__p50(ms)__p95(ms)__p99(ms)_pMax(ms)__total
  300.0s        0           1763            5.9      8.5      7.3     13.1     37.7    352.3  startRide

_elapsed___errors_____ops(total)___ops/sec(cum)__avg(ms)__p50(ms)__p95(ms)__p99(ms)_pMax(ms)__total
  300.0s        0           4518           15.1     41.9     35.7     75.5    151.0   2818.6  updateActiveRides

_elapsed___errors_____ops(total)___ops/sec(cum)__avg(ms)__p50(ms)__p95(ms)__p99(ms)_pMax(ms)__result
  300.0s        0          96079          320.3      3.1      0.9     11.5     39.8   2818.6  
```


## Admin UIへのアクセス

CockroachDBのAdmin UIを使用すると、クラスターに関する各種情報を表示することができます。
Admin UIのURLは「http://localhost:8080」です。

[OVERVIEW]で、3つのノードが稼働していることが確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179607/18c24cbc-0f26-8f58-cc29-410e2911a93a.png)


[METRICS]では、SQLクエリの実行状況のグラフなど、さまざまなダッシュボードを表示することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179607/589b48a6-870f-2fbd-8865-bd066b3e3733.png)


## ノードをダウンさせてみる

ノードに対してcockroach quitコマンドを実行して、ノードダウンをシミュレートします。

```
# cockroach quit --insecure --host=localhost:26259
ok
```

1ノードがダウンしていても、Admin UIを見るとSQLトラフィックが継続していることが確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179607/0d520633-18ec-8d5b-a0e2-b4435698d7ed.png)

[OVERVIEW]では1ノードがダウンしているので、LIVE NODESは"2"になっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179607/ab14cc4a-745d-86a0-9103-8518794123a7.png)

最後に先ほどダウンさせたノードを起動させておきます。

```
cockroach start --insecure --store=node3 --listen-addr=localhost:26259 --http-addr=localhost:8082 --join=localhost:26257,localhost:26258,localhost:26259 --background
```

[OVERVIEW]ではLIVE NODESは"3"に戻ります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179607/8b4a4eb9-f78e-1cf2-c485-ca9ceef9f2e7.png)


## クラスターをスケール

現在クラスターは3台のノードで構成されています。
このクラスターに2台のノードを追加します。

```
cockroach start --insecure --store=node4 --listen-addr=localhost:26260 --http-addr=localhost:8083 --join=localhost:26257,localhost:26258,localhost:26259 --background
cockroach start --insecure --store=node5 --listen-addr=localhost:26261 --http-addr=localhost:8084 --join=localhost:26257,localhost:26258,localhost:26259 --background
```

[OVERVIEW]ではLIVE NODESは"3"から"5"になっていることが確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179607/ba6c2cff-7fb4-8fd7-b6e1-b454bbb5be4d.png)

最初はノード4と5のレプリカ数が少なっていますが、すぐに均等になりデータが自動的にリバランスされることがわかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179607/30dbe789-f998-5b30-0691-d21cd59720a7.png)


## クラスターを停止

クラスターを停止させるため、5台全てのノードをcockroach quitコマンドを実行します。

まず、最初にクラスターを構成させていた3台のノードを停止させてみます。
これらはすぐにコマンドが完了しノードが停止します。

```
cockroach quit --insecure --host=localhost:26257
cockroach quit --insecure --host=localhost:26258
cockroach quit --insecure --host=localhost:26259
```

最後の2台のノードでは、シャットダウンに時間がかかり、最終的にノードが強制的に停止します。
これは5台のノードのうち2ノードしか残っていないため、クラスターが動作しなくなるためです。

```
# cockroach quit --insecure --host=localhost:26260
W200229 23:57:53.934909 1 cli/start.go:1405  timed out; proceeding with hard shutdown
ok
# cockroach quit --insecure --host=localhost:26261
W200229 23:58:58.104645 1 cli/start.go:1405  timed out; proceeding with hard shutdown
ok
```

後でクラスターを再起動するには、ノードのデータストアを含むディレクトリからcockroach startコマンドを実行します。
クラスターを利用しない場合は、各ノードのディレクトリ（データストア）を削除します。

```
rm -rf node1 node2 node3 node4 node5
```


# CockroachDBのSQLを学ぶ

参考URLとは違う操作になりますが、ワークロード作成時に使用したmovrデータベースを利用して試してみます。
また、操作が多かったので一部省略しています。


```
cockroach sql --insecure --host=localhost:26257 --database=movr
```

## テーブル一覧を表示

```
root@localhost:26257/movr> SHOW TABLES;
          table_name          
+----------------------------+
  promo_codes                 
  rides                       
  user_promo_codes            
  users                       
  vehicle_location_histories  
  vehicles                    
(6 rows)

Time: 1.664515ms

```

## テーブルを作成

```
root@localhost:26257/movr> CREATE TABLE drivers (
    id UUID NOT NULL,
    city STRING NOT NULL,
    name STRING,
    dl STRING UNIQUE,
    address STRING,
    CONSTRAINT "primary" PRIMARY KEY (city ASC, id ASC)
);
CREATE TABLE

Time: 16.162934ms

root@localhost:26257/movr> SHOW COLUMNS FROM drivers;
  column_name | data_type | is_nullable | column_default | generation_expression |         indices          | is_hidden  
+-------------+-----------+-------------+----------------+-----------------------+--------------------------+-----------+
  id          | UUID      |    false    | NULL           |                       | {primary,drivers_dl_key} |   false    
  city        | STRING    |    false    | NULL           |                       | {primary,drivers_dl_key} |   false    
  name        | STRING    |    true     | NULL           |                       | {}                       |   false    
  dl          | STRING    |    true     | NULL           |                       | {drivers_dl_key}         |   false    
  address     | STRING    |    true     | NULL           |                       | {}                       |   false    
(5 rows)

Time: 10.02457ms
```

## レコードを挿入

```
root@localhost:26257/movr> INSERT INTO drivers VALUES
    ('c28f5c28-f5c2-4000-8000-000000000026', 'new york', 'Petee', 'ABC-1234', '101 5th Ave');
INSERT 1

Time: 21.626055ms

root@localhost:26257/movr> INSERT INTO drivers (name, city, dl, address, id) VALUES
    ('Adam Driver', 'chicago', 'DEF-5678', '201 E Randolph St', '1eb851eb-851e-4800-8000-000000000006');
INSERT 1

Time: 5.400835ms

root@localhost:26257/movr> INSERT INTO drivers VALUES
    ('8a3d70a3-d70a-4000-8000-00000000001b', 'seattle', 'Eric', 'GHI-9123', '400 Broad St'),
    ('9eb851eb-851e-4800-8000-00000000001f', 'new york', 'Harry Potter', 'JKL-456', '214 W 43rd St');
INSERT 2

Time: 6.377982ms
```

## インデックスを作成

```
root@localhost:26257/movr> CREATE INDEX name_idx ON users (name DESC);
CREATE INDEX

Time: 393.752574ms

root@localhost:26257/movr> CREATE TABLE IF NOT EXISTS drivers (
    id UUID NOT NULL,
    city STRING NOT NULL,
    name STRING,
    dl STRING,
    address STRING,
    INDEX name_idx (name),
    CONSTRAINT "primary" PRIMARY KEY (city ASC, id ASC)
);
CREATE TABLE

Time: 553.356µs

```

## テーブルのインデックスを表示

```
root@localhost:26257/movr> SHOW INDEX FROM users;
  table_name | index_name | non_unique | seq_in_index | column_name | direction | storing | implicit  
+------------+------------+------------+--------------+-------------+-----------+---------+----------+
  users      | primary    |   false    |            1 | city        | ASC       |  false  |  false    
  users      | primary    |   false    |            2 | id          | ASC       |  false  |  false    
  users      | name_idx   |    true    |            1 | name        | DESC      |  false  |  false    
  users      | name_idx   |    true    |            2 | city        | ASC       |  false  |   true    
  users      | name_idx   |    true    |            3 | id          | ASC       |  false  |   true    
(5 rows)

Time: 2.334076ms
```

## テーブルを検索

```
root@localhost:26257/movr> SELECT name FROM users LIMIT 10;
        name         
+-------------------+
  William Wood       
  Victoria Jennings  
  Tyler Dalton       
  Tony Ortiz         
  Tina Miller        
  Taylor Cunningham  
  Susan Morse        
  Steven Lara        
  Stephen Diaz       
  Sarah Wang DDS     
(10 rows)

Time: 1.49566ms

```

## レコードを更新

```
root@localhost:26257/movr> UPDATE promo_codes SET (description, rules) = ('EXPIRED', '{"type": "percent_discount", "value": "0%"}') WHERE expiration_time < '2019-01-22 03:04:05+00:00';
UPDATE 669

Time: 3m43.8256279s
```

## レコードを削除

```
root@localhost:26257/movr> DELETE FROM promo_codes WHERE description = 'EXPIRED';
DELETE 669

Time: 42.330774ms

```

## テーブルを削除

```
root@localhost:26257/movr> DROP TABLE drivers;
DROP TABLE

Time: 98.414047ms

```


# 参考

https://www.cockroachlabs.com/docs/stable/install-cockroachdb-linux.html