LoginSignup
76
36

More than 5 years have passed since last update.

クリスマス・イブにCockroachDBに負荷をかけてみる

Last updated at Posted at 2016-12-23

クリスマス・イブ:christmas_tree:に不吉な名前のプロダクトを紹介。
現時点(2016/12/24)だとあまり日本語情報がないので、少しでも興味を持ってもらえれば幸いです。

cockroach_db.png

https://github.com/cockroachdb/cockroach
https://www.cockroachlabs.com/

キミはCockroachDBを知っているか?

そう火星にいる不気味な生命体・・・ではなく、地球に太古から現在に至るまで不屈の生命力で生き長らえ、
あの黒い生物の名前を関した分散SQLデータベースです。
ACIDトランザクションを備えており、いわゆる「NewSQL」という分類になります。

データセンター、クラウドとネットワーク分断されてもしぶとく動き続けるというP2Pアーキテクチャの強靭性が特徴で、これが黒い生物のメタファとなっています。

元Googleのソフトウェアエンジニア3名が創業しており、2016年3月には2000万ドルを投資家から調達しており、現時点(2016/12/24)では、ベータリリースを繰り返しているというステージです。

※RDB、NoSQL、NewSQLについては下記が参考になります。

CockroachDBの特徴

他のRDB/NoSQLとの比較表が公式ドキュメントにあります。

オートスケール・フェイルオーバー・リカバリに対応し、マルチデータセンターでもロストしない設計、
そして、トランザクションサポートしつつもSQLが使えるという夢のようなDB・・・というアピールになってます。

CockroachDB MySQL PostgreSQL Oracle SQL Server Cassandra HBase MongoDB DynamoDB
Automated Scaling Yes No No No No Yes Yes Yes Yes
Automated Failover Yes Optional Optional Optional Optional Yes Yes Yes Yes
Automated Repair Yes No No No No Yes Yes Yes Yes
Strongly Consistent Replication Yes No No Optional Optional Optional No No Yes
Consensus-Based Replication Yes No No No No Optional No No Yes
Distributed Transactions Yes No No Yes Yes No No No No*
ACID Semantics Yes Yes Yes Yes Yes No Row-only Document-only Row-only*
Eventually Consistent Reads No Yes Yes Yes Yes Yes Yes Yes Yes
SQL Yes Yes Yes Yes Yes No No No No
Open Source Yes Yes Yes No No Yes Yes Yes No
Commercial Version No Optional No Yes Yes Optional Optional Optional Yes
Support Limited Full Full Full Full Full Full Full Full

他には、下記の特徴があります。

  • アプリケーションからはPostgreSQLのドライバで接続可能
  • 管理画面を搭載

インストール

builder_craig.png

さて、そんな名前に反して夢のようなDBの実力はどんだけかを試すべく、
公式ページの記載に従い、インストールしてみます。

筆者のMacbook(12inch Early 2016)に入れてみます。
なお、Windowsの場合はDockerが必要です。

Macの場合、バイナリ、Homebrew、Dockerによるインストールが選べます。
今回はHomebrewで入れてみました。

$ brew install https://raw.githubusercontent.com/cockroachdb/cockroach/master/build/cockroach.rb

16分ほどかかってインストール完了です。

$ cockroach version
Build Tag:   beta-20161208
Build Time:  2016/12/10 13:29:14
Platform:    darwin amd64
Go Version:  go1.7.4
C Compiler:  4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)

起動してみる

$ cockroach start --background
CockroachDB node starting at 2016-12-10 22:47:06.030195226 +0900 JST
build:      beta-20161208 @ 2016/12/10 13:29:14 (go1.7.4)
admin:      http://localhost:8080
sql:        postgresql://root@localhost:26257?sslmode=disable
logs:       cockroach-data/logs
store[0]:   path=cockroach-data
status:     initialized new cluster
clusterID:  3c53d4f4-5065-4e46-9d8b-747f07e54b6f
nodeID:     1

デフォルトでは、DBポートが26257、管理画面用Webアプリケーションが8080を利用します。
起動は一瞬ですね。

管理画面

起動時のコンソールにも出力されている通り、http://localhost:8080/ にアクセスすると、下図のような今風なダッシュボードが表示されます。

Cockroach_Console.png

チュートリアル

SCENE_superhero_profile_craig.png

無事起動したところで、早速触っていきましょう。
まずはSQL Shellの起動して、今の状態を確認します。

$ cockroach sql
# Welcome to the cockroach SQL interface.
# All statements must be terminated by a semicolon.
# To exit: CTRL + D.

root@:26257> show database;
+----------+
| DATABASE |
+----------+
|          |
+----------+
(1 row)

root@:26257> show users;
+----------+
| username |
+----------+
+----------+
(0 rows)

当然、まだDBもユーザーも0件です。
とりあえず、CockroachDBの本家サイトに掲載されているチュートリアルに従って、
DB、ユーザー、テーブル作成までやってみます。

ユーザー作成

maxroachというユーザーを作ります。

$ cockroach user set maxroach
INSERT 1

DB作成と権限付与

rootユーザーでbankというDBを作り、全権限をmaxroachユーザーに付与します。

$ cockroach sql
root@:26257> CREATE DATABASE bank;
CREATE DATABASE

root@:26257> GRANT ALL ON DATABASE bank TO maxroach;
GRANT

テーブル作成

maxroachユーザーとbankデータベースを指定して接続し、
accountsテーブルを作成します。

$ cockroach sql --database=bank --user=maxroach

maxroach@:26257> CREATE TABLE accounts (id INT PRIMARY KEY, balance INT);
CREATE TABLE

ここまではRDB同様で違和感ないですね。

データ投入

本家サイトにサンプルプログラムが各言語ごとに用意されています。
https://www.cockroachlabs.com/docs/build-a-test-app.html

accountテーブルに2件INSERTして、そのデータを標準出力に表示するというものです。

CockroachDBはGo製ということもあり、今回はGo言語で試してみましょう。

main.go
package main

import (
    "database/sql"
    "fmt"
    "log"

    _ "github.com/lib/pq"
)

func main() {
    db, err := sql.Open("postgres", "postgresql://maxroach@localhost:26257/bank?sslmode=disable")
    if err != nil {
        log.Fatalf("error connection to the database: %s", err)
    }

    // Insert two rows into the "accounts" table.
    if _, err := db.Exec(
        "INSERT INTO accounts (id, balance) VALUES (1, 1000), (2, 250)"); err != nil {
        log.Fatal(err)
    }

    // Print out the balances.
    rows, err := db.Query("SELECT id, balance FROM accounts")
    if err != nil {
        log.Fatal(err)
    }
    defer rows.Close()
    fmt.Println("Initial balances:")
    for rows.Next() {
        var id, balance int
        if err := rows.Scan(&id, &balance); err != nil {
            log.Fatal(err)
        }
        fmt.Printf("%d %d\n", id, balance)
    }
}

実行します。

$ go get github.com/lib/pq

$ go run main.go

Initial balances:
1 1000
2 250

無事2件のINSERTに成功しましたね!!

トランザクションを試す

現在上記で試した2件が入ってる状態です。

maxroach@:26257> SELECT * FROM accounts;
+----+---------+
| id | balance |
+----+---------+
|  1 |    1000 |
|  2 |     250 |
+----+---------+
(2 rows)

エラーでロールバックされることを確認

サンプルプログラムをトランザクションを利用する形に変えてみます。

package main

import (
    "database/sql"
    "fmt"
    "log"

    _ "github.com/lib/pq"
)

func main() {
    db, err := sql.Open("postgres", "postgresql://maxroach@localhost:26257/bank?sslmode=disable")
    if err != nil {
        log.Fatalf("error connection to the database: %s", err)
    }

    // start transaction
    tx, err := db.Begin()

    if _, err := tx.Exec(
        "INSERT INTO accounts (id, balance) VALUES (3, 100), (4, 200)"); err != nil {
        log.Fatalf("error insert: %s", err)
    }
    // expect error
    if _, err := tx.Exec(
        "INSERT INTO accounts (id, balance) VALUES (1, 100), (2, 200)"); err != nil {
        log.Fatalf("error insert: %s", err)
    }
    // commit transaction
    tx.Commit()

    // Print out the balances.
    rows, err := db.Query("SELECT id, balance FROM accounts")
    if err != nil {
        log.Fatal(err)
    }
    defer rows.Close()
    fmt.Println("Initial balances:")
    for rows.Next() {
        var id, balance int
        if err := rows.Scan(&id, &balance); err != nil {
            log.Fatal(err)
        }
        fmt.Printf("%d %d\n", id, balance)
    }
}

2回目のINSERTにて、すでに存在する主キーのINSERTになるのでエラーになる想定ですね。
それでは実行してみましょう。

$ go run main.go

2016/12/23 21:13:44 error insert 2: pq: duplicate key value (id)=(1) violates unique constraint "primary"
exit status 1

想定通りエラー終了となりました。
ちなみに、Goのlog.Fatal系メソッドはコールした後にエラー終了するので、あえてロールバックしたり、returnする必要はありません。

念のためSELECTしてみましたが、問題なし。
確かにトランザクションがRDBと同じように利用できていることが確認できました。

maxroach@:26257> SELECT * FROM accounts;
+----+---------+
| id | balance |
+----+---------+
|  1 |    1000 |
|  2 |     250 |
+----+---------+
(2 rows)

軽く負荷をかけてみる

パフォーマンスはどんなものか?ということで、軽く負荷をかけてみます。
1000goルーチンでそれぞれ100件INSERTして、合計10万レコードを作成してみました。
なお、何か比較対象があったほうがよいかということで、SQLite3でも同様の処理を入れてみました。

rush.go
package main

import (
    "database/sql"
    "log"
    "sync"
    "time"

    _ "github.com/lib/pq"
    _ "github.com/mattn/go-sqlite3"
)

func main() {
    cockroachdb, err := sql.Open("postgres", "postgresql://maxroach@localhost:26257/bank?sslmode=disable")
    if err != nil {
        log.Fatalf("error connection to the cockroach database: %s", err)
    }
    log.Println("# CockroachDB")
    stressTest(cockroachdb)

    sqlite3db, err := sql.Open("sqlite3", "./bank.db")
    if err != nil {
        log.Fatalf("error connection to the sqlite3 database: %s", err)
    }
    log.Println("# SQLite3")
    stressTest(sqlite3db)
}

func stressTest(db *sql.DB) {
    defer db.Close()
    // start transaction
    tx, _ := db.Begin()
    var wg sync.WaitGroup

    start := time.Now()
    log.Println("Start test.")

    for i := 1; i <= 1000; i++ {
        wg.Add(1)
        go func(i int) {
            for j := 0; j < 100; j++ {
                if _, err := tx.Exec(
                    "INSERT INTO accounts (id, balance) VALUES ($1, 1000)", i*100+j); err != nil {
                    log.Fatalf("error insert: %s", err)
                }
            }
            wg.Done()
        }(i)
    }
    wg.Wait()
    tx.Commit()
    log.Println("End test. ", time.Since(start))
}

結果

$ go run rush.go
2016/12/23 23:13:42 # CockroachDB
2016/12/23 23:13:42 Start test.
2016/12/23 23:15:08 End test.  1m26.310939943s
2016/12/23 23:15:08 # SQLite3
2016/12/23 23:15:08 Start test.
2016/12/23 23:15:10 End test.  1.653004371s

10万件のINSERTで約86秒ということで、何度かテストしたり、goルーチンの並列度をチャネルを使って制限かけたり等もしてみたのですが、結局1100〜1200QPSという結果でした。
(上述の私のMacbookでの話です)

Cockroach_Console 2.png

ちなみに、SQLite3だと10万件ごときでは1.6秒で終わってしまうので、あんまり比較にならないです。SQLite3はやはり組込DBとして使うには優秀です(笑)
もちろんCockroachDBはクラスタを組んでリニアに性能が上がるはずですし、単純比較はできませんが。

終わりに

まだJOINが使えなかったり、性能面もまだまだ?と、まだプロダクションレベルではないCockroachDBではありますが、NoSQLではテーブル設計が難しく、やはりRDBライクなテーブル設計ができて、ノード追加でリニアにスケールアウトしてくれるDB製品の登場を欲している人は決して少なくないはず。

クリスマス・イブに、不吉な名前?を持つCockroachDBが来年非連続な成長をしてくれることを期待したいと思います。

参考

76
36
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
76
36