純粋に非科学的:Tarantool 1.6対Golang(速度)

最近タランツールについて読んだのですが、面白くなってきました。 アイデアは良いです-データベースの隣のコードは、このような高速なRedisのような環境に保存されます。







そして、何かが考えられていました-私たちは現在Golangを職場で積極的に使用しています。実際、Goは、 および組み込みデータベース。 しかし、たとえば、Go + LevelDB(実際には、他のものがあるかもしれません)をTarantoolと比較するとどうなるでしょう。 Go + RocksDBもテストしましたが、すべてが少し複雑であることが判明し、小さなデータでも結果はほぼ同じです。







要求に応じて、単純なタスク(HTTPサーバー)をテストし、キーをデータベースに書き込み、名前で(競合チェックなしで)取得し、この値から単純なJSONを送り返します。







比較: go+leveldb



tarantool



go+go-tarantool



nginx upstream tnt_pass









将来を見据えて-私の非科学的なテストでは、すべてのプロセッサコアの使用によりGo + LevelDBを獲得しました。 最も可能性が高いのは、複数のタランチュラとバランサーを起動した場合、ある程度の利益があるかもしれませんが、それが重要だと言うことではありません...しかし、真実は、ここではすでに複製などを行う必要があります







しかし、全体的に、Tarantoolは非常に印象的なものです。







注:非常に特定のケースを比較していますが、これは他のすべてのケースでGo / LevelDBが勝つか負けることを意味するものではありません。







まあ、それでも:LevelDBの代わりに-RocksDBを使用する方がおそらく良いでしょう。







その結果(簡単に)







4-10



= 4スレッド、10同時接続

10-100



= 10スレッド、100接続







画像







Tarantoolは1つのCPUスレッド(または2つの観点から)のみを使用し、4スレッドCPUでテストされていることに注意してください 。 Goはデフォルトですべてのコアとスレッドを使用します。







dedokOne コメントから取得した nginx lua tnt_pass( 結果







wrk -t 4 -c 10



(4スレッド、10接続):







Golang:







  Latency Distribution 50% 269.00us 99% 1.64ms Requests/sec: 25637.26
      
      





タランツール:







  Latency Distribution 50% 694.00us 99% 1.43ms Requests/sec: 10377.78
      
      





ただし、タランチュラはコアの約半分しか使用しなかったため、おそらくその速度はほぼ同じです。







重い負荷( wrk -t 10 -c 100



)の下では、タランチュラはRPSによって所定の位置に残りました(ただし、レイテンシはGolang、特に上部よりも大幅に低下しました)、そしてGolangは元気になりました(もちろんレイテンシも低下しました)。







行く:







  Latency Distribution 50% 2.85ms 99% 8.12ms Requests/sec: 33226.52
      
      





タランツール:







  Latency Distribution 50% 8.69ms 99% 73.09ms Requests/sec: 10763.55
      
      





Tarantoolには次の利点があります。セカンダリインデックス、レプリケーション...







Goには巨大なライブラリのエコシステムがあり(私の計算によると、組み込みデータベース(およびそうではない)データベースの実装は海です )、例として、同じ文句は全文検索を提供します(私が理解すると、たとえば、タランツールではありません)。







タランチュラの生態系は貧弱だと感じています。 少なくとも提供されるものはすべて、msgpack、httpサーバー、クライアント、json、LRUキャッシュなどです。Goでは、無数のバージョンで実装されています..







つまり、一般的に、クレイジーな速度の向上はありません。







これまでのところ、 私の個人的な選択はGoの方向にとどまっています。なぜなら、タランツールエコシステムが近い将来にそれほど激しくなるという感覚はなく、Go-は長い間積極的に開発されてきたからです。







Tarantoolコードはもちろん短くなりますが、主にエラーが言語によって処理されるためです。 Goでは、すべてのerr



を切り取って、ほぼ同じままにすることもできます。







誰か他の意見がありますか?







また、Tarantoolのアトミックコードの更新に関するコメントにも気づきましたが、HTTPリクエストについて話しているため、(現在の職場で)goに無限に使用し、テストに従って(および1秒あたり数千のリクエストがある)、Goを更新しますHTTPリクエストを失うことなくコードを作成します。 記事の最後の例。







そして、テストについての詳細:







  ➜ ~ go version go version go1.6 darwin/amd64 ➜ ~ tarantool --version Tarantool 1.6.8-525-ga571ac0 Target: Darwin-x86_64-Release
      
      





Golang:







 ➜ ~ wrk -t 4 -c 10 -d 5 --latency http://127.0.0.1:8081/ Running 5s test @ http://127.0.0.1:8081/ 4 threads and 10 connections Thread Stats Avg Stdev Max +/- Stdev Latency 346.71us 600.80us 26.94ms 97.89% Req/Sec 6.54k 0.88k 13.87k 73.13% Latency Distribution 50% 269.00us 75% 368.00us 90% 493.00us 99% 1.64ms 130717 requests in 5.10s, 15.08MB read Requests/sec: 25637.26 Transfer/sec: 2.96MB
      
      





タランツール:







 ➜ ~ wrk -t 4 -c 10 -d 5 --latency http://127.0.0.1:8080/ Running 5s test @ http://127.0.0.1:8080/ 4 threads and 10 connections Thread Stats Avg Stdev Max +/- Stdev Latency 767.53us 209.64us 4.04ms 87.26% Req/Sec 2.61k 437.12 3.15k 45.59% Latency Distribution 50% 694.00us 75% 0.90ms 90% 1.02ms 99% 1.43ms 52927 requests in 5.10s, 8.58MB read Requests/sec: 10377.78 Transfer/sec: 1.68MB
      
      





より大きな負荷の下で:







行く:







 ➜ ~ wrk -t 10 -c 100 -d 5 --latency http://127.0.0.1:8081/ Running 5s test @ http://127.0.0.1:8081/ 10 threads and 100 connections Thread Stats Avg Stdev Max +/- Stdev Latency 3.04ms 1.48ms 25.53ms 80.21% Req/Sec 3.34k 621.43 12.52k 86.20% Latency Distribution 50% 2.85ms 75% 3.58ms 90% 4.57ms 99% 8.12ms 166514 requests in 5.01s, 19.21MB read Requests/sec: 33226.52 Transfer/sec: 3.83MB
      
      





タランツール:







 ➜ ~ wrk -t 10 -c 100 -d 5 --latency http://127.0.0.1:8080/ Running 5s test @ http://127.0.0.1:8080/ 10 threads and 100 connections Thread Stats Avg Stdev Max +/- Stdev Latency 10.65ms 14.24ms 269.85ms 98.43% Req/Sec 1.09k 128.17 1.73k 94.56% Latency Distribution 50% 8.69ms 75% 10.50ms 90% 11.36ms 99% 73.09ms 53943 requests in 5.01s, 8.75MB read Requests/sec: 10763.55 Transfer/sec: 1.75MB
      
      





テストソース:







行く:







 package main import ( "encoding/json" "fmt" "io" "net/http" "github.com/syndtr/goleveldb/leveldb" ) var db *leveldb.DB func hello(w http.ResponseWriter, r *http.Request) { err := db.Put([]byte("foo"), []byte("bar"), nil) if err != nil { w.WriteHeader(500) io.WriteString(w, err.Error()) return } res, err := db.Get([]byte("foo"), nil) if err != nil { w.WriteHeader(500) io.WriteString(w, err.Error()) return } result, err := json.Marshal(string(res)) if err != nil { w.WriteHeader(500) io.WriteString(w, err.Error()) return } w.Write(result) } func main() { var err error db, err = leveldb.OpenFile("level.db", nil) if err != nil { panic(err) } http.HandleFunc("/", hello) fmt.Println("http://127.0.0.1:8081/") http.ListenAndServe("127.0.0.1:8081", nil) }
      
      





タランツール:







 #!/usr/bin/env tarantool box.cfg{logger = 'tarantool.log'} space = box.space.data if not space then space = box.schema.create_space('data') space:create_index('primary', { parts = {1, 'STR'} }) end local function handler(req) space:put({'foo','bar'}) local val = space:get('foo') return req:render({ json = val[2] }) end print "http://127.0.0.1:8080/" require('http.server').new('127.0.0.1', 8080) :route({ path = '/' }, handler) :start()
      
      





Golang(アトミックコード置換、接続損失なし):







 package main import ( "encoding/json" "fmt" "io" "net/http" "syscall" "io/ioutil" "time" "github.com/fvbock/endless" "github.com/gorilla/mux" "github.com/syndtr/goleveldb/leveldb" ) var db *leveldb.DB func hello(w http.ResponseWriter, r *http.Request) { if db == nil { // ()  ,      panic("DB is not yet initialized") } err := db.Put([]byte("foo"), []byte("bar"), nil) if err != nil { w.WriteHeader(500) io.WriteString(w, err.Error()) return } res, err := db.Get([]byte("foo"), nil) if err != nil { w.WriteHeader(500) io.WriteString(w, err.Error()) return } result, err := json.Marshal(string(res)) if err != nil { w.WriteHeader(500) io.WriteString(w, err.Error()) return } w.Write(result) } func main() { var err error mux1 := mux.NewRouter() mux1.HandleFunc("/", hello).Methods("GET") fmt.Println("http://127.0.0.1:8081/") server := endless.NewServer("127.0.0.1:8081", mux1) server.BeforeBegin = func(add string) { ioutil.WriteFile("server.pid", []byte(fmt.Sprintf("%d", syscall.Getpid())), 0755) db, err = leveldb.OpenFile("level.db", nil) for err != nil { time.Sleep(10 * time.Millisecond) db, err = leveldb.OpenFile("level.db", nil) } } server.ListenAndServe() if db != nil { db.Close() } }
      
      





その後、 go build



実行させ、ロード中にgo build



を試すことができますgo build; kill -1 $(cat server.pid)



go build; kill -1 $(cat server.pid)



-テストではデータの損失は観察されませんでした。







コメントでは、go + go-tarantoolを試すことをお勧めします







私が試した:







負荷が少ない







 ➜ ~ wrk -t 4 -c 10 -d 5 --latency http://127.0.0.1:8081/ Running 5s test @ http://127.0.0.1:8081/ 4 threads and 10 connections Thread Stats Avg Stdev Max +/- Stdev Latency 799.14us 502.56us 25.22ms 95.74% Req/Sec 2.55k 248.65 2.95k 85.22% Latency Distribution 50% 727.00us 75% 843.00us 90% 1.02ms 99% 2.03ms 51591 requests in 5.10s, 5.95MB read Requests/sec: 10115.52 Transfer/sec: 1.17MB
      
      





大きな負荷:







 ➜ ~ wrk -t 10 -c 100 -d 5 --latency http://127.0.0.1:8081/ Running 5s test @ http://127.0.0.1:8081/ 10 threads and 100 connections Thread Stats Avg Stdev Max +/- Stdev Latency 7.49ms 4.00ms 65.06ms 81.21% Req/Sec 1.38k 357.31 8.40k 94.61% Latency Distribution 50% 6.78ms 75% 8.86ms 90% 11.77ms 99% 22.74ms 69091 requests in 5.10s, 7.97MB read Requests/sec: 13545.12 Transfer/sec: 1.56MB
      
      





ソース:







tarantool.lua:







 #!/usr/bin/env tarantool box.cfg{ listen = '127.0.0.1:3013', logger = 'tarantool.log' } space = box.space.data if not space then box.schema.user.grant('guest', 'read,write,execute', 'universe') space = box.schema.create_space('data') space:create_index('primary', { parts = {1, 'STR'} }) end print(space.id) print('Starting on 3013')
      
      





main.go:







 package main import ( "encoding/json" "fmt" "io" "log" "net/http" "time" "github.com/tarantool/go-tarantool" ) var client *tarantool.Connection func hello(w http.ResponseWriter, r *http.Request) { spaceNo := uint32(512) _, err := client.Replace(spaceNo, []interface{}{"foo", "bar"}) if err != nil { w.WriteHeader(500) io.WriteString(w, err.Error()) return } indexNo := uint32(0) resp, err := client.Select(spaceNo, indexNo, 0, 1, tarantool.IterEq, []interface{}{"foo"}) if err != nil { w.WriteHeader(500) io.WriteString(w, err.Error()) return } first := resp.Data[0].([]interface{}) result, err := json.Marshal(first[1]) if err != nil { w.WriteHeader(500) io.WriteString(w, err.Error()) return } w.Write(result) } func main() { var err error server := "127.0.0.1:3013" opts := tarantool.Opts{ Timeout: 500 * time.Millisecond, } client, err = tarantool.Connect(server, opts) if err != nil { log.Fatalf("Failed to connect: %s", err.Error()) } http.HandleFunc("/", hello) fmt.Println("http://127.0.0.1:8081/") http.ListenAndServe("127.0.0.1:8081", nil) }
      
      






All Articles