Упд. Тестирование Golang Rest Api. 120 000 [#/Сек] Без Ограничений?

В глаза бросилось не особо позитивное сравнение Java против ГО.

Тестирование с большим количеством пользователей .

Я решил проверить, действительно ли все так плохо с Go. Забегая вперед, скажу, что при кешировании в памяти и генерации JSON на лету нам удалось получить до 120 000 [#/сек] для 8 физических ядер.

Базовый сценарий запроса GET:

  • Если данные найдены в кэше памяти и они действительны, мы генерируем JSON из структуры
  • Если данных в кеше нет, то ищем их в Bolt DB, если находим, то читаем готовый JSON
  • Если данных нет в Bolt DB, то мы запрашиваем их из базы и сохраняем в кэше в памяти.

  • Данные в кэше памяти накапливаются в буферном канале; после накопления около 10 000 элементов они сбрасываются одним сохранением в Bolt DB
  • Если данные в базе изменились (обновить/вставить), то отправляется уведомление через pg_notify и данные в кеше помечаются как недействительные; при следующем обращении к нему он снова считывается из базы данных
Под катом приведены результаты испытаний и код тестового стенда.

GitHub

Обновление 06.05.2020

Теперь есть возможность протестировать Облако Oracle .



УПД.
</p><p>
 Тестирование Golang REST API. 120 000 [#/сек] без ограничений?

  • стенд собран на 3-х серверах - 8 Core Intel (16 виртуальных ядер), 120 Памяти (ГБ), Oracle Linux 7.7.
  • локальные диски NVMe — твердотельный накопитель NVMe емкостью 6,4 ТБ, минимум 250 тыс.

    операций ввода-вывода в секунду (блок 4 тыс.

    )

  • локальная сеть между серверами - 8,2 Пропускная способность сети (Гбит/с)
  • в режиме прямого чтения из PostgreSQL — до 16 000 [get/sec], параллелизм 1024, медиана 60 [мс].

    Каждый метод Get запрашивает данные из двух таблиц общим размером 360 000 000 строк.

    Размер JSON составляет 1800 байт.

  • в режиме кэширования - до 100 000 - 120 000 [гет/сек], параллелизм 1024, медиана 2 [мс].

  • для вставки в PostgreSQL — около 10 000 [вставка/сек].

  • при масштабировании с 2 до 4 и 8 Core прирост производительности практически линейный.



Краткие выводы

  • Использование кэширования JSON в Bolt DB дает примерно двукратное увеличение скорости как на жестких, так и на твердотельных накопителях.

  • перенос базы данных PostgreSQL с HDD на SDD дает прирост минимум в 100 раз
  • перенос Bolt DB на SDD с HDD тоже дает прирост минимум в 100 раз
  • кэширование структуры в памяти и генерация JSON на лету дает прирост до 10 раз по сравнению с базой данных PostgreSQL на SDD
  • стабильность отличная - ни одного неудачного запроса до 16 384 параллелизма.



Тестирование на локальной машине

Результаты испытаний доступны по адресу связь Все, кого интересует код тестового стенда, могут посетить GitHub .

Кода много, около 4000 строк, но ничего сверхинтересного не использовалось.

Единственное, это заказное Кодер/декодирование JSON — это ускорило генерацию JSON более чем в 2 раза.

Обработчик HTTP Get со стандартным пакетом JSON

  
  
   

BenchmarkHandler_DeptGetHandler-2 1000000 41277 ns/op 4845.26 MB/s 10941 B/op 259 allocs/op

Обработчик HTTP Get с пакетом francoispqt/gojay

BenchmarkHandler_DeptGetHandler-2 1000000 13300 ns/op 15036.97 MB/s 3265 B/op 11 allocs/op

Параметры испытательного стенда:
  • База данных PostgreSQL 11 для Windows 10, размер базы — до 30 ГБ.

  • Две таблицы - главная до 10 000 000 строк, подчиненная таблица 100 000 000 строк.

  • Размер сообщения JSON составляет 1500 байт и состоит из 1 главной строки и 10 подчиненных строк.

  • компьютер — core i7-3770 — 4 ядра (8 потоков), 16 ГБ ОЗУ, HDD (WD 2.0TB WD2000fyyz), SSD (Intel 530 Series).

  • Тестирование проводилось через ApacheBench, уровень параллелизма от 1 до 4096, 1 000 000 запросов случайным образом.

  • Чтобы минимизировать кэширование Windows, перед каждым тестом компьютер перезагружался.

  • RAID не использовался.

    Было интересно померить максимальную производительность на одном диске.

В ходе тестирования были проверены следующие крайние случаи: Падение производительности при повышении Concurrency Level связано с тем, что ApacheBench был запущен на том же компьютере и активно «съедал процессорное время».



GET с прямым доступом к PostgreSQL — HDD (каждый запрос читает базу данных)

Уровень параллелизма Запросов в секунду Время на запрос 99% процентиль
1 36,84 [#/сек] (среднее) 27,145 [мс] (среднее) 116 [мс]
2 38,27 [#/сек] (среднее) 52,262 [мс] (среднее) 165 [мс]
4 41,34 [#/сек] (среднее) 96,755 [мс] (среднее) 268 [мс]
8 45,02 [#/сек] (среднее) 177,687 [мс] (среднее) 420 [мс]
16 47,56 [#/сек] (среднее) 336,428 [мс] (среднее) 813 [мс]
128 49,19 [#/сек] (среднее) 2602,228 [мс] (среднее) 13055 [мс]
512 50,5 [#/сек] (среднее) 10122,343 [мс] (среднее) 19394 [мс]
2048 51,91 [#/сек] (среднее) 39453,681 [мс] (среднее) 57018 [мс]


GET с прямым доступом к PostgreSQL — SSD (каждый запрос читает базу данных)

Уровень параллелизма Запросов в секунду Время на запрос 99% процентиль
1 713,82 [#/сек] (среднее) 1,401 [мс] (среднее) 2 [мс]
2 1914,61 [#/сек] (среднее) 1,045 [мс] (среднее) 2 [мс]
4 3326,52 [#/сек] (среднее) 1,202 [мс] (среднее) 2 [мс]
8 4599,95 [#/сек] (среднее) 1,739 [мс] (среднее) 4 [мс]
16 4599,80 [#/сек] (среднее) 3,478 [мс] (среднее) 9 [мс]
128 5243,76 [#/сек] (среднее) 24,410 [мс] (среднее) 102 [мс]
512 5354,35 [#/сек] (среднее) 95,623 [мс] (среднее) 506 [мс]
2048 5285,83 [#/сек] (среднее) 387,451 [мс] (среднее) 2871 [мс]


GET с кешированием в BOLT DB — HDD-диск (ранее JSON сохранялся в BOLT DB)

Уровень параллелизма Запросов в секунду Время на запрос 99% процентиль
1 81,55 [#/сек] (среднее) 12,262 [мс] (среднее) 38 [мс]
2 67,04 [#/сек] (среднее) 29,832 [мс] (среднее) 97 [мс]
4 72,51 [#/сек] (среднее) 55,167 [мс] (среднее) 183 [мс]
8 92,48 [#/сек] (среднее) 86,502 [мс] (среднее) 291 [мс]
16 89,42 [#/сек] (среднее) 178,923 [мс] (среднее) 550 [мс]
128 86,76 [#/сек] (среднее) 1475,378 [мс] (среднее) 11280 [мс]
512 92,38 [#/сек] (среднее) 2771,145 [мс] (среднее) 10238 [мс]
2048 100,56 [#/сек] (среднее) 20366,847 [мс] (среднее) 18632 [мс]
4096 106,98 [#/сек] (среднее) 38289,063 [мс] (среднее) 37074[мс]


GET с кешированием в BOLT DB — SSD-диск (JSON ранее сохранялся в BOLT DB)

Уровень параллелизма Запросов в секунду Время на запрос 99% процентиль
1 3411,07 [#/сек] (среднее) 0,293 [мс] (среднее) 1 [мс]
2 7468,21 [#/сек] (среднее) 0,268 [мс] (среднее) 1 [мс]
4 9501,15 [#/сек] (среднее) 0,421 [мс] (среднее) 2 [мс]
8 10481,68 [#/сек] (среднее) 0,763 [мс] (среднее) 3 [мс]
16 10052,14 [#/сек] (среднее) 1,592 [мс] (среднее) 5 [мс]
128 10754,02 [#/сек] (среднее) 11,903 [мс] (среднее) 20 [мс]
512 11030,61 [#/сек] (среднее) 46,416 [мс] (среднее) 66 [мс]
2048 10634,72 [#/сек] (среднее) 192,577 [мс] (среднее) 362 [мс]
4096 10659,04 [#/сек] (среднее) 384,275 [мс] (среднее) 720 [мс]


GET с кэшированием структуры в памяти

Уровень параллелизма Запросов в секунду Время на запрос 99% процентиль
1 9178,22 [#/сек] (среднее) 0,109 [мс] (среднее) 1 [мс]
2 22580,40 [#/сек] (среднее) 0,089 [мс] (среднее) 1 [мс]
4 36163,33 [#/сек] (среднее) 0,111 [мс] (среднее) 1 [мс]
8 56109,17 [#/сек] (среднее) 0,143 [мс] (среднее) 1 [мс]
16 43942,75 [#/сек] (среднее) 0,364 [мс] (среднее) 2 [мс]
128 55005,53 [#/сек] (среднее) 2,327 [мс] (среднее) 6 [мс]
512 35338,01 [#/сек] (среднее) 14,489 [мс] (среднее) 25 [мс]
2048 38090,35 [#/сек] (среднее) 53,767 [мс] (среднее) 228 [мс]
4096 30196,47 [#/сек] (среднее) 135,645 [мс] (среднее) 609 [мс]


Тестирование в Oracle Cloud



Испытательный стенд

Тестовый стенд был собран в облаке Oracle на 3-х одинаковых серверах:
  • Конфигурации VM.DenseIO2.8 — 8 ядер Intel (16 виртуальных ядер), 120 памяти (ГБ), Oracle Linux 7.7
  • использовались локальные диски NVMe — твердотельное хранилище NVMe емкостью 6,4 ТБ (1 диск), минимум 250 тыс.

    операций ввода-вывода в секунду (блок 4 тыс.

    )

  • локальная сеть между серверами - 8,2 Пропускная способность сети (Гбит/с)
  • БД — PostgreSQL 12. Настройки базы данных не оптимизированы.

  • тестирование проводилось через ApacheBench:
    • уровень параллелизма от 4 до 16384
    • от 10 000 до 1 000 000 запросов случайным образом.

    • В фоновом режиме работало от 2 до 8 экземпляров ApacheBench (один процесс ab не может загрузить более 1 виртуального процессора)


УПД.
</p><p>
 Тестирование Golang REST API. 120 000 [#/сек] без ограничений?



Результаты тестирования GET с разными типами кэширования

  • Размер JSON - 1800 байт. Один главный объект Dept и 10 вложенных объектов Emp.
  • Размер таблицы Dept — 33 000 000, таблицы Emp — 330 000 000 строк.

Были протестированы следующие сценарии:
  • GET без кэширования с прямым доступом к PostgreSQL
  • GET с кэшированием JSON от BBolt (размер 74 ГБ)
  • GET с кэшированием памяти (стандартная карта)
Первые результаты испытаний доступны по адресу репозитории .

Таблица с агрегированными данными доступна по адресу связь График количества запросов в секунду в зависимости от параллелизма.



УПД.
</p><p>
 Тестирование Golang REST API. 120 000 [#/сек] без ограничений?

График времени на запрос.

[мс] из параллелизма (логарифмическая шкала).



УПД.
</p><p>
 Тестирование Golang REST API. 120 000 [#/сек] без ограничений?

График 99%-го процентиля [мс] в зависимости от параллелизма.



УПД.
</p><p>
 Тестирование Golang REST API. 120 000 [#/сек] без ограничений?

График 99%-го процентиля [мс] в зависимости от параллелизма (логарифмическая шкала).



УПД.
</p><p>
 Тестирование Golang REST API. 120 000 [#/сек] без ограничений?

Вот как выглядит загрузка внутреннего сервера при 100 000 [#/сек] и кэшировании данных в BBolt (параллелизм 128).

Из 1600% процессорного времени программа использует 1464%.

Средняя задержка ввода-вывода на диске NVMe составляет 0,02 [мс] (500 000 операций ввода-вывода в секунду).



УПД.
</p><p>
 Тестирование Golang REST API. 120 000 [#/сек] без ограничений?



Профилирование

По данным Benchmark, обработчик HTTP Get завершается за 13300 нс.



BenchmarkHandler_DeptGetHandler-2 1000000 13300 ns/op 15036.97 MB/s 3265 B/op 11 allocs/op

По данным ApacheBench, лучшее время выполнения запроса — 0,088 мс.

Стало интересно, где разница 0,088 - 0,013 = 0,055 мс.

Включено профилирование - Результаты На верхнем уровне:

  • net/http.(*conn).

    serve — (61,85%)

  • net/http.(*connReader).

    backgroundRead — (7,04%)

  • runtime.gcBgMarkWorker — (18,30%)
Что сделал HTTP-сервер из (61,85%):
  • Чтение входящих запросов — net/http.(*conn).

    readRequest — (9,29%).

    Входящие запросы не имели тела.

  • Запись исходящих ответов - net/http.(*response).

    finishRequest - (21,74%).

    Запись тела ответа.

  • Фактический обработчик запроса — net/http.(*ServeMux).

    ServeHTTP — (23,33%).



УПД.
</p><p>
 Тестирование Golang REST API. 120 000 [#/сек] без ограничений?

Из общих затрат на readRequest, FinishRequest и BackgroundRead (39,66%) на обработку системных вызовов ввода-вывода Windows — Internalpoll.(ioSrv).

ExecIO — пришлось (24,78%).

Давайте посмотрим, что делал наш основной обработчик net/http.(*ServeMux).

ServeHTTP:

  • Разбор URL-адресов и обработка параметров — github.com/gorilla/mux.(*Route).

    Match — (2,29%)

  • Генерация JSON из структуры в памяти — github.com/francoispqt/gojay.marshal — (9,92%)
  • Работа с кэшем в памяти - (4,53%)


УПД.
</p><p>
 Тестирование Golang REST API. 120 000 [#/сек] без ограничений?

Теги: #api #open source #postgresql #Go #golang #json #http #rest api
Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.