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

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


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


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


    Сравнил: 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 соединений


    image


    Обратите внимание Tarantool занимает только 1 поток CPU (вернее по виду 2), а тестировалось на 4-поточном CPU. Go использует по умолчанию все ядра и потоки.


    nginx lua tnt_pass взят из комментария dedokOne (результат)


    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 и останется примерно столько же.


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


    Еще в комментариях заметили про атомарные обновления кода в Tarantool, но раз уж мы говорим про HTTP запросы — то мы (на текущем месте работы) используем endless для go и по нашим тестам (а у нас там тысячи запросов в секунду) — обновляем мы 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

    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"
    )
    
    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)
    
    }

    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()

    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; 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)
    
    }

    Similar posts

    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 101

      +2
      Я об этом говорил авторам тарантула чуть ли не с самого начала: посмотрите на го :).

      С другой стороны, они все же немного о разном. В tarantool хранимые процедуры на lua и их можно (атомарно) обновлять без перезагрузки сервера и не теряя кеши. Если бы такое можно было делать в golang, то наверное ниши для тарантула не осталось бы. А пока что go не может целиком заменить ни nginx+lua, ни tarantool+lua, к сожалению :(.
        0
        Как понял здесь роль хранилища выполняет leveldb, а go используется лишь как быстрый веб сервер, поэтому не надо все перезапускать
          +1
          Не совсем, leveldb встроен как библиотека и по сути хранит все в файле. Т.е. перезапускается все.
            0
            Да, увидел в коде подключение реализации leveldb на go. Не специалист по go, возможно стоило использовать обертку на go для leveldb или вынести в отдельную программу и общаться с ней. Понятно, что это будет чуть медленней, но решило бы проблему атомарности.
          0
          Я, кстати, вот про это атомарное обновление не нашел, хороший момент. С другой стороны — раз уж мы говорим про http — то есть https://github.com/fvbock/endless который как раз и делает атомарное обновление — им мы на работе и пользуемся чтобы там, имея тысячи запросов в секунду к бэкэнду — без потерь запросов обновлять код.
            +2
            Как вы понимаете, кеши вы всё равно потеряете, да и вряд ли это хорошая идея — работать с leveldb базой из двух процессов. Так что с рестартом так просто не выйдет.
              0
              Верная мысль, в теории можно попробовать RocksDB или какие-то другие которые более хорошо умеют с lockами работать. Там в принципе, лок-то нужен только на время рестарта.
                0
                Добавил в конец статьи пример замены кода.
              +1
              Есть вариант подключить интерпретатор LUA и менять скрипты атомарно уже в Golang сервере с LevelDB или любой другой встраиваемой базой.

              Готовые интерпретаторы LUA пока не попадались, а вот javascript уже пару раз видел. Похоже что с такой целью его (js) в телефонной платформе ITooLabs используют (судя по докладу www.youtube.com/watch?v=HoEn7lXNQOU )
                0
                Так и есть, используем.

                Причем, начинали мы как раз с LuaJIT, но потом отказались — слишком велики были потери на сериализацию/десериализацию.
                +2
                Обновлять код без перезагрузки сервера можно в Erlang ;)

                Кстати, в Tarantool можно писать хранимки на C, а также уже есть прототип хранимок на Swift.
                  0
                  > Обновлять код без перезагрузки сервера можно в Erlang ;)

                  Ну я абсолютно лично за то, чтобы это было во всех языках :)
                    0
                    это очень легко делается в языках, где код и данные разнесены, но практически нерешаемая задача в языках, где код и данные живут в одном пространстве.

                    Даже в Эрланге есть ситуация, когда обновление кода становится проблемой из-за склейки кода и данных и её решают брутально.
                      +1
                      В Tarantool как раз код и данные живут в одном процессе, и при этом код можно менять без перезагрузки Tarantool :)
                        0
                        я не про процесс, а про логическое разделение.

                        Например, в С++ нет внятного механизма пройтись по всем экземплярам класса A и поменять им пойнтеры на методы класса. А в эрланге это сильно проще, потому что нет классов и их методов.
                    0
                    Может вы достанете птицу феникса? (я про javascript)
                    Его знают многие, его изучение оправдано с так же с использованием на клиент сайде, да и нода-пользователи…
                      0
                      Тогда уж и php7 достать, его тоже многие знают.
                        +1
                        php7 был вторым языком, а js идет первым потому, что они это уже делали, только не поддерживают.
                  +1
                  Слава, оперативненько.Вот если бы код лежал бы в отдельных нодах и еще бы в нотации JSON я бы сказал, что это перфекшн.
                    0
                    Можно подробнее? Что имеется в виду под «отдельные ноды»? Ноды серверов? И что именно в нотации JSON нужно?
                    +1
                    В первую очередь, Слава, спасибо вам огромное за тест!

                    Я же правильно понимаю, что тест запускался на той же машине, что и тестируемая система?
                      0
                      Все верно, как и сказано — сугубо ненаучно :) Моя задача была понять для себя — есть ли в Tarantool такое огромное преимущство, что стоит отказаться от Golang или использовать рядом с ним. Для этого вполне достаточно на одной машине сравнить — ведь нагружающий `wrk` одинаковое количество ресурсов скушает, так что отличие по производительности будет зависеть только от тестируемой стороны. Какая быстрее — та и быстрее :)
                        +1
                        Согласен с вами. Я просто уточнил :)
                      +10
                      На самом деле, тест хороший. И хоть и назван «ненаучным» он показывает явно, что golang + leveldb в каких-то кейсах быстрее Tarantool.

                      При этом у Tarantool перед goland + leveldb есть вполне себе очевидные преимущества:

                      1. Обновление кода без перезагрузки сервера
                      2. Хранимые процедуры
                      3. Репликация master-slave и master-master (важное преимущество — ибо с leveldb придется данные куда-то как минимум rsync'ать, ибо иначе стремно)
                      4. Другие СУБД-фишки (транзакции, например)
                      5. Поддержка других языков кроме Lua (сейчас это C и Swift, в будущем появятся и другие)

                      Кроме того, очевидно, что выставлять http-сервер на go в интернет плохо, т.к. надо терминировать SSL, надо делать различные административные действия (типа как rewrite и прочие), надо хорошо обрабатывать медленных клиентов, надо отдавать статику в конце концов. Со всем этим как мы прекрасно знаем прекрасно справляется nginx. Т.е. ставить nginx перед своим любимым http-сервером — это сейчас самый массовый кейс. А раз спереди ставится nginx, то его можно пускать в Tarantool не по медленному http, а по быстрому бинарному протоколу, например, как это было сказано тут: https://habrahabr.ru/company/mailru/blog/272141/

                      Кажется, что такая связка должна уделать golang, мы это обязательно протестируем, используя ваши тесты и выложим результаты.

                      Кроме того, если в app server'е есть внутри хранилище, то надо чтобы кто-то сверху роутил запросы на слейв, если упал мастер. В случае Тарантула есть стандартный nginx-модуль, который умеет это делать (см. статья выше). А в случае go + leveldb что делать? Плюс, как я сказал уже выше, репликацию leveldb придется писать руками.

                      В целом же, спасибо, что показали, что http-сервер в Тарантуле еще далек по производительности от совершенства. Мы стремимся к тому, чтобы даже на одном ядре работать быстрее чем все аналоги на всем сервере. Будем улучшать! :)
                        +1
                        Отличные комментарии. Пару вещей хотелось бы правда, уточнить:

                        > 1. Обновление кода без перезагрузки сервера

                        Это я показал уже в статье после комментария — это более чем возможно.

                        > 2. Хранимые процедуры

                        Собственно говоря, Golang это и есть в данном случае «хранимая процедура». Просто склейка получается в другом направлении — к языку мы приклеиваем базу (golang + leveldb/rocksdb), а не к базе приклеиваем язык (tarantool.box + lua), но результат-то тот же получается — быстрый язык рядом с базой.

                        > 3. Репликация master-slave и master-master

                        Это абсолютно верно и это я и указал в статье — репликация, пока что, это важное достоинство, что есть у Tarantool. Хотя master-master я что-то не нашел (плохо искал?) А вот master-slave меня напугал когда прочитал что-то в духе, что изменение одних и тех же данных на двух разных slave полностью останавливают базу и требуется вмешательство админов… Вот тут у меня наступил некий ступор с тем как Mail.Ru справляется с этим на огромных объемах.

                        > 4. Другие СУБД-фишки (транзакции, например)

                        LevelDB, вроде, не поддерживает (не специалист тут, к сожлению), но вот RocksDB (следующее поколение от LevelDB) уже поддерживает.

                        Еще, к списку я бы добавил secondary index — это действительно тоже сильная сторона Tarantool.

                        > Кроме того, очевидно, что выставлять http-сервер на go в интернет плохо

                        Моя задача на самом деле не написание серверов для интернета, а бэкэндовых серверов для внутреннего потребления. Но я не скажу что выставлять go http это плохо или прямо драматично хуже чем nginx. Да, мы используем nginx почти везде и его преимущества очевидны, но, не все описанное Вами верно. Некоторые из них:

                        > делать различные административные действия (типа как rewrite и прочие)

                        Собственно, через gorilla/mux это решается примитивнейше в go.

                        > хорошо обрабатывать медленных клиентов

                        Golang с этим справляется ничуть не хуже nginx, в общем-то. Такие же мультиплексированные потоки на несколько тредов + асинхронная обработка.

                        > надо отдавать статику в конце концов

                        Тоже, в общем-то, достойно работает в Go.

                        Я не говорю что nginx это плохо, опять же — мы его используем почти везде, но я бы не был так категоричен с «выставлять http-сервер на go в интернет плохо».

                        > мы это обязательно протестируем, используя ваши тесты и выложим результаты.

                        Будет очень интересно прочитать. Я, если честно, вообще удивился что тестирования golang+X vs tarantool не нашел сходу.

                        > А в случае go + leveldb что делать?

                        Не очень понятна сложность, это вроде стандартный nginx upstream + proxy_next_upstream умеют делать?

                        > Будем улучшать! :)

                        :thumbs_up:

                        Ну и как бы мое мнение не является тут окончальным для всех — всегда нужен контекст. Наш контекст что у нас уже есть специалисты на Go + много кода и если бы вдруг Tarantool показал бы результаты там в 10 раз лучше — ну тогда бы имело смысл, конечно. А так переход не имеет большого смысла.
                          0
                          > Обновление кода без перезагрузки сервера

                          Чуть ошибся. Имел в виду, что возможно это делать «без потери запросов», а не без перезагрузки :) Пардон.
                            +1
                            Перезагрузка сбрасывает все кэши в LevelDB и приводит к холодному старту. Холодный старт — это одна из главных болей on-disk storage engine.
                              0
                              В Вашем примере новый инстанс сервера будет ждать (не обрабатывать новые запросы) пока старый инстанс не обработает ВСЕ текущие запросы (и не отпустит leveldb чтобы новый инстанс мог ее взять).
                              Это в общем случае может приводить к большому лагу во время обновления кода.
                              Или я неправильно понял пример?
                                0
                                Все верно. Для моего случая это скорее недостаток, для кого-то, вероятно, — критично.
                                  +1
                                  Можно написать код по передаче горячих данных от одного инстанца к другому. А так же сделать прослойку из goprotobuf, net/rpc поверх unix socket или любого другого протокола для связи с приложениями которым только доступ к данным нужен без вмешательство в работу самой базы.

                                  Репликацию (sync) данных между двумя и более инстансами (даже сетевыми) делать на Golang не так сложно — это лишь всеголиш данные которые можно пушить сразу на два и более серверов, можно даже помечать в мастере те данные что успешно запушились куда-то ещё и при старте смотреть какие данные не помечены и отправлять их на реплику.

                                  Думаю в статье основной посыл в следующем — разрабатывают Tarantool только +- десять человек, по большей части из Mail.Ru Group для себя или для корпоративных клиентов которым нужна поддержка, в то же время Golang пилится тысячей-другой человек по всему миру по всем возможным направлениям и то чего нет в Golang сейчас может совершенно спокойно появиться в течении пары дней, а не месяцев.
                                    +1
                                    Это распространенное заблужение, что golang разрабатывают много человек. Контрибьютят, возможно. много, но именно активную разработку ведут все те же 10 человек плюс-минус лапоть (пруф предоставить не могу, ибо по «google dev team» ничего внятного не находится, но о размере своей команды они довольно часто говорят на разных презентациях).
                                      +2
                                      Я имел в виду разработку 100500 всевозможных пакетов на Golang и для Golang. Понятно что в сам рантайм коммитят не многие. Но всё что вокруг — это уже тысячи людей. Другой вопрос — как много людей пишут плагины и прочий код сопутствующий к Tarantool?

                                      Понятно что сюда входят все кто пишет на LUA и для LUA, но… тогда надо сравнивать экосистему и сообщество LUA и Golang разработчиков как таковых.

                                      Так же не маловажно — хочется ли конкретным разработчикам разбираться и использовать LUA только ради базы данных или же у них вся инфраструктура на LUA. Не видел ни одной компании у которых всё пишется на LUA. В тоже время есть не мало компаний в которых пишут исключительно на Golang.
                                +1
                                3. master-master: http://tarantool.org/doc/book/replication/index.html, п. 4.8. Насчет master-slave — данные меняются только на master, поэтому такой проблемы нет. Однако на master-master такая проблема может быть, и если ваш application не может хэндлить этот кейс, то лучше использовать master-slave. При этом не надо вмешательство админов и ничего не останавливается. Могу рассказать детально как это работает, например, позвонив по скайпу. Мой скайп danikin2. Стучитесь, не стесняйтесь :)

                                4. Кстати, да. Соглашусь :)
                                5. Если у вас go смотрит в интернет и сам все это делает, включая терминацию SSL, статику и все остальное, то это очень круто! Было бы интересно почитать статью про это, а также про сравнение с nginx!
                                6. «nginx upstream + proxy_next_upstream» — ну, не совсем. Если, к примеру, у вас локально все затормозило, и в этот конкретный сервер ходить нельзя, то nginx это не запомнит, насколько я знаю, и будет пытаться ходить туда каждый раз. Но, в целом, если такое решение устраивает, то да, почему бы нет. Вообще, я неоднократно это говорил — я за мир во всем мире и за те решения, которые решают проблемы. Если у вас с вашим решением проблем нет, то все круто и менять его не на что не надо! :)
                                  0
                                  Раз уж речь зашла не только о быстродействии, но и о эксплуатационных качествах, то есть вопрос. Как правильно бекапить приложение, базирующееся на leveldb?
                                    0
                                    Я тут без понятия — я просто знал что для Go есть несколько KV хранилищ, взял первые два попавшиеся ради интереса :) Никогда не эксплуатировал ее. Насколько я понимаю, авторы (Facebook) рекомендуют все же идти дальше и пользовать RocksDB (который у меня просто не скомпилировался в виде embedded, только в виде shared библиотеки, что не очень мне понравилось, поэтому открыл им issue и оставил за рамками статьи)

                                    Думаю гугление на тему backup leveldb должно раскрыть тайну :)
                                  0
                                  Для бинарного протокола можно же взять http/2 до go.
                                  +2
                                  Сам тестил leveldb, sophia, rocksdb на Go.

                                  C leveldb всё было отлично, но как только повышал количество документов с нестандартно большой длиной, он начинал тупить, это было предсказуемо и понятно.

                                  sophia реально удивила, очень быстро, и при возрастании нагрузки деградирует очень медленно, но при длительных тестах заметил, что (WAL был отключён) терят некоторые записи. Свежих биндингов я не нашёл, сварганил их быстро сам, поэтому есть подозрение, что виной всему кривые руки.

                                  rocksdb начали тестировать когда было окончательно принято решение, что сервак будет ssd-шным. Перетестировав предыдущие решения и сравнив их с rocksdb — последний остался победителем.

                                  Но sophia, оставила очень тёплые впечатления и я обязательно попробую расковырять причины моих неудачных.
                                    +2
                                    А можете чуть детальней расписать про софию? Например, мне в скайп danikin2. Нам было бы очень интересно поисследовать ваши кейсы, особенно с потерей записей? С выключенным WAL, кстати, записи и должны теряться, софия же в памяти все не хранит, лучше WAL включить, он особой нагрузки не делает, а если делает, то нам было бы интересно взглянуть на это :)
                                      0
                                      Отписал вам в скайп.
                                        +1
                                        Не вижу вашего приглашение в скайпе. Давайте попробуем Телеграм: +7 916 709 81 11, Денис Аникин, @DennisAnikin
                                    0
                                    я не очень понял сравнение. Тарантул — это кортежная БД с кучей фич. leveldb — это примитивный keyvalue с заявленным хранением хуже чем в тарантуле.

                                    Чего сравнивали то?
                                      +6
                                      С таким подходом вообще ничего в мире сравнивать нельзя. Как пример: MySQL это база со многими engine, а Redis это key-value кэш с другими характеристиками. Но неужели это автоматически обозначает что я не могу одну и ту же задачу решить на MySQL и на Redis? В одном случае мне одни плюшки предоставит система, а другие придется делать руками, в другом — другие предоставит другая система, а в первой их придется делать руками.

                                      Тот же случай и тут. Я взял общий делитель — операцию которую можно сделать в обоих случаях. И сравнил ее по скорости. В чем проблема?
                                        +1
                                        Решение поставленной задачи сравнивали. Её еще на mysql можно решить, а еще на perl скрипт написать и в файлах хранить, а ещё на node.js + mongodb. Задача оттого не изменится.
                                          0
                                          ИМХО Сравнение очень даже интересно, с учетом что тут не только DB тестировалось, но и способы доставки.
                                          +5
                                          Я вот думаю, а почему не совместить приятное с полезным? HTTP-сервер писать на Go, а при этом в качестве СУБД оставить Tarantool. Скорее всего это будет быстрее чем Go + LevelDB. И при этом все свойства СУБД автоматом останутся.
                                            0
                                            На один комментарий ниже отписался. https://habrahabr.ru/post/282299/#comment_8867187
                                              +1
                                              Добавил сравнение в конец статьи, оказалось, к сожалению, не быстрее. Думаю что это сильно связано с тем, что постоянно приходится типы из interface{} в строки переводить — но тут так реализован клиент — процессоры горят на полную.
                                                +1
                                                Да, библиотеки, что есть на го для тарантула, отвратительные.
                                              0
                                              А что если попробовать провести тесты не с leveldb, а с tarantool? Т.е. Go+go-tarantool и замерить.
                                              https://github.com/tarantool/go-tarantool
                                                0
                                                Собственно, меня от этого остановило то, что хотелось попробовать именно «сервер БД прямо рядом с кодом» — как это и сделано в tarantool.
                                                  +1
                                                  Добавил сравнение в конец статьи.
                                                  0
                                                  tarantool http не оптимизирован под большие нагрузки, для этого есть nginx upstream http://github.com/tarantool/nginx_upstream_module
                                                  Надо собрать nginx с этим модулем, взять master пока, тег не добавил.

                                                  Немного под хачил lua, чтобы через nginx можно было ходить
                                                  https://gist.github.com/dedok/bd6b32914dbbedf3d9a78bd50ad83f48

                                                  nginx.conf — он с новыми фичами
                                                  https://gist.github.com/dedok/d84b82b2863b778f47ca05c17cbf3b25

                                                  Ну и разбалансировать либо на 1-н, как ты сделал, но выставить go runtime.GOMAXPROCS(1)
                                                  Либо балансировать на некое кол-во cpu.
                                                  С nginx CPU подлетит, но, как правило, у всех стоит nginx перед backend'ом.
                                                  Так что его в потребителях можно учесть :)

                                                  Думаю, так будет немного по научному.
                                                  Кстати спасибо автору, благодаря статье, первый раз по трогал go, очень понравилось.
                                                    0
                                                    Ну ок, у всех сейчас многоядерные сервера и машины и как бы в один поток ограничивать go — смысла большого нет, ибо на бою хочется именно использовать по-максимуму ресурсы системы, и вот тут вопрос.

                                                    А как использовать все ядра-то на Tarantool? Т.е. я так понял что для этого надо запускать несколько tarantool и включать на них master-master репликацию и потенциально связываться с конфликтами записи? Master-slave я так понимаю не решит тут, если речь идет о равном количестве записей и чтений (т.е. условно write-bound нагрузка)
                                                      +1
                                                      Согласен — насчет ядер.
                                                      Да надо несколько tarantool запускать, из либо sharding(https://github.com/tarantool/shard) в tarantoolы ходить, или делать хитрый балансинг на уровне nginx upstreams (писать на lua/ngscript).
                                                      Репликация скорей всего не подойдет, мы же не про put(const, const) говорим?:)
                                                        +1
                                                        И я с этим согласен.
                                                        Тарантул не потребляет все ядра эффективно, на серверах у нас их 12*2HT.
                                                        В этом случае частый ответ разработчиков: запустить несколько инстанцев.
                                                        Такое разделение
                                                        — резко снижает объем памяти одного инстанца, т.к. они его между собой не шарят,
                                                        — требуется писать код шардинга, и, иногда, стыковать данные из разных шардов.

                                                        Проблему неиспользованных ядер в целом можно решить, если на тот же сервер сажать еще какие-то демоны, тот же Nginx, например.
                                                        0
                                                        Да, там еще нюанс упущен

                                                        https://gist.github.com/dedok/d84b82b2863b778f47ca05c17cbf3b25

                                                        Я финальным шагом `bar` в json конвертирую, т.е. в `«bar»` — конвертация в JSON и обратно зачастую тоже довольно медленная вещь. Поэтому и в Go, и в Tarantool проверял это последним шагом.
                                                          +1
                                                          Зачем конвертировать в JSON? Обработка массивами самая быстрая. Рутировать массивы в JSON имеет смысл тогда, когда нужно зафиксировать связи в течении рабочей сессии, или для исполнения кода, допустим.Достаточно иметь схемы и плоские буферы.
                                                            0
                                                            конвертация в JSON и обратно зачастую тоже довольно медленная вещь

                                                            я пытался в минимальном самом простом виде воспроизвести JSON API доступный по HTTP. последним шагом в JSON API идет конвертация в JSON

                                                        +1
                                                        Если необходим действительно быстрый http на Go:
                                                        https://github.com/valyala/fasthttp

                                                        Кстати, у Go есть достойная Lua VM:
                                                        https://github.com/yuin/gopher-lua

                                                        Сам сейчас в раздумьях, какую k-v использовать в своих проектах для решения различных задач. На работе мы используем свои биндинги к LMDB, для меня это не подходит, т.к. мне нужна кросс-компиляция в некоторых случаях. Посмотрел на tarantool — ручной шардинг мне не понравился, хочется чтобы «оно само».

                                                        Из интересных проектов, есть еще tikv:
                                                        https://github.com/pingcap/tikv

                                                        Распределенная key-value c транзакциями на Rust, которая будет использоваться в качестве бекенда для другого проекта (tidb) на го.
                                                          0
                                                          https://github.com/valyala/fasthttp выглядит интересно, но как-то не очень понятно как это скрестить с zero-downtime restarts — а это, в общем-то, довольно важно.
                                                            0
                                                            Может быть получится с помощью «reuseport»
                                                          +1
                                                          Как я понял, тест был многопоточный и запросы шли один за другим.
                                                          1. При таком подходе сетевой стек линукса довольно быстро ограничит RPS еще до того как все CPU будут использованы (речь про средние сервера, а не десктоп).
                                                          2. Тарантул хорошо себя показывает в batch запросах, где пачками грузятся запросы, и так же пачками получаются ответы. Но при этом сами запросы должны быть простые: не более одного меняющего данные операции (в CALL) для 1.5 или же нужно использовать транзакции в 1.6.
                                                          3. Плохое использование ядер окупается ускорением за счет отсутствия локов. На практике это надо мерить, но любители корутин/файберов будут бить в грудь даже без тестов (что обычно бывает оправданным).

                                                          Протестируйте на таких кейсах как batch запросы, используя его бинарный протокол. На таких кейсах он наверняка обгонит что-то самописное.

                                                          Надо понимать, что дает конкретное СУБД и чем за это придется платить. Для каждой базы есть как свои плюсы, так и свои минусы.
                                                            0
                                                            Тест через wrk = многопоточный + мультиплексирование (4 потока, 10 соединений и 10 потоков, 100 соединений)

                                                            К сожалению, уже не осталось времени тестировать что-то подобное. Вполне возможно, хотя последний добавленный тест go+go-tarantool, в общем-то, надежды большой не дал.
                                                              0
                                                              Жаль. т.к. в текущем виде этот тест больше смахивает на тест http серверов go и tarantool
                                                                0
                                                                P.S. Бинарный протокол-то я протестировал из Go (там добавил в статью) и результат примерно на уровне. Т.е. теории 2 у меня — либо клиент go-tarantool тормозит (что не исключено из-за использования interface{}, но оно тут неизбежно), либо все же дело не в http сервере tarantool.
                                                            0
                                                            Хотелось бы увидеть тест где вы реализуете ВЕСЬ функционал tarantool. А то так можно и с фотошопом сравнивать Go, реализовав только какой либо один фильтр картинки =)
                                                              0
                                                              Ну, я думал это само собой разумеется, что тест для отдельного случая и я вовсе не говорю что Tarantool надо забросить и он никуда не годится.

                                                              Просто в обратную сторону тоже можно также бросить — реализуйте в Tarantool ВЕСЬ функционал всех библиотек Go, а потом сравнивайте…

                                                              Сравнение вполне конкретное, более того — Tarantool выглядит достойно. Я описал минусы по которым он мне в моем случае не подходит, но это ж не значит что это случай всех и вся.
                                                                0
                                                                Так вы же начали сравнивать, потому сравнивайте не тёплое с мягким, а реализацию ПОЛНОГО функционала Tarantool на C/C++ с реализацией этого же функционала на Go. А то какая-то манипулирование неокрепшими умами получается…
                                                                  +1
                                                                  Я уже отвечал на этот аргумент тут

                                                                  Если мне нужен Фотошоп для одного фильтра — я имею право сравнивать Фотошоп и программу, имеющую только этот один фильтр.

                                                                  Реализация k-v хранилища тут примерно одинаковая. Да, в Tarantool есть другие возможности — я это написал в статье и в комментариях несколько раз. Да, сравнивается конкретный случай — я это тоже написал и в статье, и в комментариях.
                                                                    0
                                                                    Судя по коду ваша задача заключается в том, чтобы класть bar в foo, десятки тысяч раз общаясь через HTTP протокол, да тут вряд ли какие-либо коробочные решения помогут =).

                                                                    Мало того даже в сравниваемом коде разный функционал, в tarantool, например используется лог файл, что по определению создаёт неслабую нагрузку на дисковую подсистему.

                                                                    Да и понятно бы было если бы хотя бы свою реализацию KV хранилища показали бы, а то получается что тестируем кастрированный веб-сервер на Go + KV хранилище на C vs целую базу данных и сервер аппликаций.
                                                                      0
                                                                      Вы задали ровно тот же вопрос, на который я уже ответил.
                                                                        +1
                                                                        Да и понятно бы было если бы хотя бы свою реализацию KV хранилища показали бы

                                                                        хехе, мощно
                                                                +2
                                                                Забавный момент, пустил go(во все потоки) через nginx(1 worker) proxy_pass с keepalive получил:
                                                                wrk -t 10 -c 100 -d 5 --latency http://127.0.0.1:8081/go
                                                                Running 5s test @ http://127.0.0.1:8081/go
                                                                10 threads and 100 connections
                                                                Thread Stats Avg Stdev Max ± Stdev
                                                                Latency 23.76ms 21.26ms 165.45ms 94.74%
                                                                Req/Sec 477.19 106.88 594.00 87.35%
                                                                Latency Distribution
                                                                50% 19.23ms
                                                                75% 19.93ms
                                                                90% 21.46ms
                                                                99% 153.41ms
                                                                16361 requests in 5.10s, 2.59MB read
                                                                Requests/sec: 3206.89 (максимум ~5к было — скачет CPU — вырубать все лень, мак домашний!)
                                                                Transfer/sec: 519.87KB

                                                                Пусканул tarantool (tnt_pass)
                                                                Running 5s test @ http://127.0.0.1:8081/tnt
                                                                10 threads and 100 connections
                                                                Thread Stats Avg Stdev Max ± Stdev
                                                                Latency 7.48ms 2.44ms 18.02ms 74.22%
                                                                Req/Sec 1.33k 386.69 5.49k 86.45%
                                                                Latency Distribution
                                                                50% 7.71ms
                                                                75% 8.76ms
                                                                90% 9.80ms
                                                                99% 14.88ms
                                                                66431 requests in 5.10s, 13.12MB read
                                                                Requests/sec: 13017.25(максимум ~17к было, скачет CPU — вырубать все лень, мак домашний!)
                                                                Transfer/sec: 2.57MB

                                                                PS keepalive выкрутил, даже dtrace accept сделал
                                                                  +1
                                                                  Копаюсь почему — самому объяснить интересно стало.
                                                                    +1
                                                                    Заинтриговали, пойду tnt_pass попробую :)
                                                                      +1
                                                                      надо на epoll попробовать это все с kqueue, завтра вчером с epoll, сегодня нет возможности, протестирую скину все результаты и trace будем считать accept и т.д.
                                                                      А вообще гоу к нам в чат :)
                                                                        0

                                                                        Вроде поставил все, но результат что-то немного другой:


                                                                        ➜  nginx git:(master) ✗ curl 127.0.0.1:8081/tnt
                                                                        {"id":0,"result":[["bar"]]}

                                                                        а должен быть


                                                                        "bar"

                                                                        хотя вроде


                                                                          return { var[2] } -- только bar

                                                                        А где чат?

                                                                          +1
                                                                          угу из-за протокола, там все всегда в result возвращается(пока никто по другому не хотел), и с id для мультепликсирования.
                                                                          telegram, могу ссылку скинуть в личку.
                                                                            0

                                                                            давайте, только не обещаю сильно активно участвовать — работа тоже есть :)

                                                                          0

                                                                          В целом (у меня тоже мак, так что оставил kqueue) nginx_upstream получилось так (код взят из gist)


                                                                          ➜  nginx git:(master) ✗ curl 127.0.0.1:8081/tnt
                                                                          {"id":0,"result":[["bar"]]}%                                                                                                                                                                                                                                                  ➜  nginx git:(master) ✗ wrk -t 4 -c 10 -d 5 --latency http://127.0.0.1:8081/tnt
                                                                          Running 5s test @ http://127.0.0.1:8081/tnt
                                                                            4 threads and 10 connections
                                                                            Thread Stats   Avg      Stdev     Max   +/- Stdev
                                                                              Latency   550.88us  144.06us   2.28ms   74.29%
                                                                              Req/Sec     3.61k   536.23     4.10k    81.37%
                                                                            Latency Distribution
                                                                               50%  516.00us
                                                                               75%  610.00us
                                                                               90%  766.00us
                                                                               99%    0.96ms
                                                                            73337 requests in 5.10s, 14.55MB read
                                                                          Requests/sec:  14381.01
                                                                          Transfer/sec:      2.85MB
                                                                          ➜  nginx git:(master) ✗ wrk -t 10 -c 100 -d 5 --latency http://127.0.0.1:8081/tnt
                                                                          Running 5s test @ http://127.0.0.1:8081/tnt
                                                                            10 threads and 100 connections
                                                                            Thread Stats   Avg      Stdev     Max   +/- Stdev
                                                                              Latency     9.14ms    2.49ms  18.70ms   66.40%
                                                                              Req/Sec     1.09k   437.60     9.36k    93.01%
                                                                            Latency Distribution
                                                                               50%    9.31ms
                                                                               75%   10.88ms
                                                                               90%   12.11ms
                                                                               99%   13.64ms
                                                                            54184 requests in 5.10s, 10.75MB read
                                                                          Requests/sec:  10626.29
                                                                          Transfer/sec:      2.11MB
                                                                            +1
                                                                            А если на go backend через proxy_pass с keepalive?
                                                                              +1

                                                                              nginx.conf


                                                                              upstream go {
                                                                                server 127.0.0.1:8080;
                                                                                keepalive 10000;
                                                                              }
                                                                              
                                                                              upstream tnt {
                                                                                server 127.0.0.1:10001; # ходим в 1 tarantool -- можно балансировать в больше
                                                                                keepalive 10000;
                                                                              }
                                                                              
                                                                              server {
                                                                                  listen 8081 default;
                                                                              
                                                                                  server_name tnt;
                                                                              
                                                                                  location = /tnt {
                                                                                    tnt_pass_http_request on; # пропускам http данные
                                                                                    tnt_http_rest_methods get; # только get
                                                                                    tnt_method 'handler'; # вызываем function handler()
                                                                                    tnt_pass tnt;
                                                                                  }
                                                                              
                                                                                  location = /go {
                                                                                    proxy_pass go;
                                                                                  }
                                                                              }

                                                                              Test:


                                                                              ➜  nginx git:(master) ✗ curl 127.0.0.1:8081/go
                                                                              "bar"%                                                                                                                                                                                                                                                                        ➜  nginx git:(master) ✗ wrk -t 4 -c 10 -d 5 --latency http://127.0.0.1:8081/go
                                                                              Running 5s test @ http://127.0.0.1:8081/go
                                                                                4 threads and 10 connections
                                                                                Thread Stats   Avg      Stdev     Max   +/- Stdev
                                                                                  Latency   297.07us  188.09us   5.74ms   92.84%
                                                                                  Req/Sec     6.79k   353.23     7.91k    79.80%
                                                                                Latency Distribution
                                                                                   50%  268.00us
                                                                                   75%  352.00us
                                                                                   90%  444.00us
                                                                                   99%  841.00us
                                                                                137159 requests in 5.10s, 15.83MB read
                                                                              Requests/sec:  26896.15
                                                                              Transfer/sec:      3.10MB
                                                                              ➜  nginx git:(master) ✗ wrk -t 10 -c 100 -d 5 --latency http://127.0.0.1:8081/go
                                                                              Running 5s test @ http://127.0.0.1:8081/go
                                                                                10 threads and 100 connections
                                                                                Thread Stats   Avg      Stdev     Max   +/- Stdev
                                                                                  Latency     3.11ms    1.33ms  17.15ms   76.48%
                                                                                  Req/Sec     3.24k     1.05k   25.94k    99.60%
                                                                                Latency Distribution
                                                                                   50%    3.02ms
                                                                                   75%    3.73ms
                                                                                   90%    4.60ms
                                                                                   99%    7.18ms
                                                                                161791 requests in 5.10s, 18.67MB read
                                                                              Requests/sec:  31711.10
                                                                              Transfer/sec:      3.66MB
                                                                                +1
                                                                                А ты число workers уменьшил до 1-го?
                                                                                  0

                                                                                  Брал все из gist. Воркеров чего nginx или golang? Если golang — то я в самом начале статьи сказал, что на одно ядро производительность сравнима (или, может, даже лучше :) ).

                                                                                    +1
                                                                                    угу все уже увидил что тут 1 там N :)
                                                                                    0

                                                                                    nginx.conf


                                                                                     worker_processes 1; # ходим в 1 процесс - увиличивем если балансируем в N tnt
                                                                                +1

                                                                                Добавил это в графики

                                                                                  +1
                                                                                  Единственно чтобы было еще неплохо в графиках указать данные по CPU. А в коментах народ не найдет
                                                                                    +1

                                                                                    Какие?

                                                                                      +1
                                                                                      Что go (N — core) tnt (1 — core), nginx (1 — core).
                                                                            +1
                                                                            tnt_pass ожидаемо дает больше, т.к. в этом случае:
                                                                            1. http часть работ вынесена в nginx
                                                                            2. запросы идут мультиплексом, т.е. batch

                                                                            Это и показывает, что тесты из статьи в основном
                                                                            — тестируют реализацию http, а она там в том же потоке луа работает.
                                                                            — не загружают сам тарантул, утыкаясь в узкое горлышко на http уровне.

                                                                            Вывод: для http лучше использовать nginx модуль с tnt_pass.
                                                                            0

                                                                            [удален, не туда ответил]

                                                                              0
                                                                              Да, если еще актуально)

                                                                              У библиотеки goleveldb, которую вы подключаете есть mem storage, было бы тоже интересно посмотреть в сравнении с tarantool, которая позиционирует себя как in-memory db
                                                                                0
                                                                                Если нужно in-memory, то можно взять обычный Map.
                                                                                  0
                                                                                  1. Обычный map не thread safe
                                                                                  2. Различные KV-хранилища зачастую показывают производительность даже выше чем встроенный map.
                                                                                    0
                                                                                    2. Различные KV-хранилища зачастую показывают производительность даже выше чем встроенный map.

                                                                                    Зачастую? Вот я сейчас сделал тест на питоне, выдает более 7Млн «записей» в сек на моем ноутбуке в один поток (на Го будет больше), приведите пример хранилища которое хотя бы отдаленно дотягивает до этих цифр.
                                                                                      0
                                                                                      Я имею в виду KV-хранилища, реализованные в виде Go-библиотек. С сетевым оверхедом, пусть даже на локалхосте, естественно никто не обгонит map.

                                                                                      Например, вот: https://github.com/coocood/freecache
                                                                                      Set-операция в два раза быстрее встроенного Map, но Get в два раза медленнее :) Но т.к. оно thread-safe, то в реальном использовании будет на порядок быстрее блокируемого целиком mutex-ом map-а.
                                                                                      Минус — память нужно выделять заранее, map же растет пока не кончится RAM.

                                                                                      Или вот еще — https://github.com/allegro/bigcache
                                                                                        0
                                                                                        Это тот же хешмап со своими фичами, хоть и назвали кешем, но явно не хранилище.
                                                                                        Но мысль я вашу понял.
                                                                                          0
                                                                                          Да, хешмап. По сути-то все современные тарантулы и прочие редисы — это хешмап, иногда со всякими плюшками вроде secondary-индексов и шардинга.
                                                                                  0

                                                                                  Только вот характеристики тут тогда принципиально отличаются. Тарантул (насколько я понимаю) если сказал "сделано" — то оно сохранено на диске. А вот in-memory не сохраняют на диск ничего.

                                                                                    0
                                                                                    Да, верно, tarantool без commit на диск (в режиме wal_mode=write|sync) «сделано» не скажет.

                                                                                Only users with full accounts can post comments. Log in, please.