在圣诞节前夜,尝试给CockroachDB施加压力
请允许我在圣诞前夕向您介绍一个带有不吉名字的产品。
由于现在是2016年12月24日,关于这个产品的中文信息并不多,希望能引起您的一丝兴趣。
以下是一个本地的中文释义:
https://github.com/cockroachdb/cockroach
https://www.cockroachlabs.com/ – 这是一个网站地址。
你知道CockroachDB吗?
这个黑色生物名字相关的分布式SQL数据库,在太古时代至今以不屈的生命力在地球上生存。它具备ACID事务,并被归类为所谓的”NewSQL”。
P2P架构具有坚韧性,即使数据中心、云和网络被隔离,也能持续稳定运行,这就像黑色生物的隐喻一样。
三位元Google的软件工程师共同创立了这家公司,并在2016年3月从投资者那里获得了2000万美元的资金。到目前为止(2016年12月24日),他们正在进行多次Beta版本的发布。
以下是关于RDB、NoSQL、NewSQL的参考资料。
-
- 参考:データベース事始め
- http://qiita.com/AkitsuguHirano/items/980de403cc7fe6c941e3
CockroachDB的特点
在官方文档中,有一个与其他RDB/NoSQL进行比较的表格。
具备自动扩展、故障切换和恢复功能,设计能在多个数据中心间保证不丢失,同时也具备了支持事务的SQL数据库,如梦境般的吸引力。
-
- 出典
- https://www.cockroachlabs.com/docs/cockroachdb-in-comparison.html
除此之外,还具备以下特点。
-
- アプリケーションからはPostgreSQLのドライバで接続可能
- 管理画面を搭載
安装
现在,为了验证这个与名字相反的数据库的潜力是否真的像梦一样,我将按照官方网页上的说明进行安装尝试。
我將它安裝在我自己的Macbook(2016年早期款,12英寸)上。
然而,如果是在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/时,在启动时出现在控制台中的内容,会显示出下面这样一个现代化的仪表板。
教程
让我们立即动手尝试一下,虽然事情还没有开始。首先启动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)
当然,数据库和用户都还没有任何记录。
首先,我会按照CockroachDB官方网站上提供的教程做数据库、用户和表的创建。
用户创建
创建一个名为maxroach的用户。
$ cockroach user set maxroach
INSERT 1
创建数据库并授予权限
使用root用户创建名为bank的数据库,并将所有权限授予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
到目前为止和关系型数据库一样,感觉没有什么不同的地方。
数据输入
本网站为每种语言提供了示例程序,请访问https://www.cockroachlabs.com/docs/build-a-test-app.html
我們需要在account資料表中插入兩筆資料,並將這些資料顯示在標準輸出中。
由于CockroachDB是用Go语言编写的,所以我们这次尝试使用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
恭喜你成功插入了两条数据!
尝试进行交易
此刻正在測試兩項內容是否正常。
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)
}
}
因为第二次的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系列方法后会导致错误退出,所以不必特意进行回滚或返回。
只是为了确认,我选择了SELECT,没有问题。
确实能够确认事务可以像关系数据库一样使用。
maxroach@:26257> SELECT * FROM accounts;
+----+---------+
| id | balance |
+----+---------+
| 1 | 1000 |
| 2 | 250 |
+----+---------+
(2 rows)
试着轻轻地加点压力
为了了解性能如何,我们稍微加了点负载。
我们使用了1000个例程,每个例程插入了100条记录,总共创建了10万个记录。
另外,考虑到需要有一些比较对象,我们也在SQLite3中进行了类似的处理。
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
经过多次测试和使用channel限制goroutine的并行度等尝试,我用10万次INSERT大约花费了86秒的时间,最终结果是每秒处理了1100到1200个请求。(这是关于我的Macbook的情况)
顺便说一句,SQLite3在处理10万条记录时可以在1.6秒内完成,这就没什么可比性了。SQLite3在作为内嵌数据库使用时确实非常出色(笑)。当然,CockroachDB组成集群后性能应该会线性提升,无法简单进行比较。
结束
虽然CockroachDB仍然不能使用JOIN,并且在性能方面仍然有待提高,因此它还未达到生产级别。但在NoSQL中,数据表设计往往比较困难,因此很多人渴望出现一种类似关系型数据库的能够进行线性扩展的数据库产品。
我希望明年圣诞夜,名为不吉的CockroachDB能够实现不连续的增长。
请分别取样参考
-
- NewSQLのCockroachDBについて調べてみた|サイバーエージェント 公式エンジニアブログ
-
- http://ameblo.jp/principia-ca/entry-11968223741.html
ユニークな分散型オープンソースデータベースCockroachDBがシリーズA1で$20Mを調達、著名投資家たちが将来性に注目 | TechCrunch Japan
http://jp.techcrunch.com/2016/03/31/20160330cockroachdb-just-raised-20-million-from-benchmark-index-and-gv/