Сугубо ненаучно: Tarantool vs Golang (по скорости)

от автора

Зачитался я последнее время про Tarantool, интересно стало. Идея хорошая — код рядом с базой данных, хранимка в такой быстрой Redis-подобной среде.

И что-то задумался — мы вот сейчас используем активно на работе Golang, собственно, мысль пришла что на Go написано много всего, в т.ч. и встраиваемых баз. А что если сравнить, например, Go+LevelDB (собственно, можно было бы и любую другую) против Tarantool. Тестировал еще Go+RocksDB, но там оказалось все немного сложнее, а результат примерно тот же на небольших данных.

Забегая вперед — в моем ненаучном тесте выиграл Golang за счет использования всех ядер процессора. Скорее всего, если запустить несколько Тарантулов и балансировщик — выигрыш может какой-то и будет, но не сказать чтобы значительный… Но, правда, тут уже надо будет репликацию делать или что-то подобное.

Итак результат (кратко) wrk -t 4 -c 10 (4 потока, 10 соединений):

Golang:

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

Tarantool:

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

Но, Тарантул занял примерно только половину ядер, так что, вероятно, скорость у них — примерно одинаковая.

Под бОльшей нагрузкой (wrk -t 10 -c 100) Тарантул остался на месте по RPS (а вот latency просела значительно заметнее чем у Golang, особенно верхняя часть), а Golang даже приободрился (но latency тоже просела, разумеется).

Go:

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

Tarantool:

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

У Tarantool есть свои примущества: secondary index, репликация…

У Go же есть огромная экосистема библиотек (около 100 тыс по моим подсчетам, среди них и реализаций встроенных (и не очень) баз данных — море), и, как пример, тот же bleve дает полнотекстовый поиск (чего, насколько я понял, например, нет в Tarantool).

По ощущениям экосистема Тарантула беднее. По крайней мере все, что предлагается — msgpack, http server, client, json, LRU cache,… в Go реализовано в бессчетных вариантах..

Т.е., в общем-то, безумного выигрыша скорости нет.

Пока что мой личный выбор остается в сторону Go, потому что нет ощущения что экосистема Tarantool выстрелит настолько сильно в ближайшее время, а Go — уже давно активнейше развивается.

Код на Tarantool, конечно, короче, но в основном, за счет того что ошибки обрабатываются языком. В Go можно тоже вырезать все err и останется примерно столько же.

Может у кого-то есть другие мнения?

И если подробнее про тест.

Тестировал простую задачу — HTTP сервер, при запросе — записать ключик в базу, достать его же по имени (без всяких проверок на race), отправить назад простенький JSON из этого value.

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

Tarantool:

➜  ~ 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

Под большей нагрузкой:

Go:

➜  ~ 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

Tarantool:

➜  ~ 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

Исходники тестов:

Go:

package main  import (     "encoding/json"     "fmt"     "io"     "net/http"      "github.com/syndtr/goleveldb/leveldb"     "github.com/tecbot/gorocksdb" )  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      opts := gorocksdb.NewDefaultOptions()     opts.SetCreateIfMissing(true)     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)  }

Tarantool:

#!/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()

ссылка на оригинал статьи https://habrahabr.ru/post/282299/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *