В глаза бросилось не особо позитивное сравнение Java против ГО.
Тестирование с большим количеством пользователей .
Я решил проверить, действительно ли все так плохо с Go. Забегая вперед, скажу, что при кешировании в памяти и генерации JSON на лету нам удалось получить до 120 000 [#/сек] для 8 физических ядер.
Базовый сценарий запроса GET:
- Если данные найдены в кэше памяти и они действительны, мы генерируем JSON из структуры
- Если данных в кеше нет, то ищем их в Bolt DB, если находим, то читаем готовый JSON
- Если данных нет в Bolt DB, то мы запрашиваем их из базы и сохраняем в кэше в памяти.
- Данные в кэше памяти накапливаются в буферном канале; после накопления около 10 000 элементов они сбрасываются одним сохранением в Bolt DB
- Если данные в базе изменились (обновить/вставить), то отправляется уведомление через pg_notify и данные в кеше помечаются как недействительные; при следующем обращении к нему он снова считывается из базы данных
Обновление 06.05.2020
Теперь есть возможность протестировать Облако Oracle .
- стенд собран на 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
Обработчик HTTP Get с пакетом francoispqt/gojayBenchmarkHandler_DeptGetHandler-2 1000000 41277 ns/op 4845.26 MB/s 10941 B/op 259 allocs/op
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 не использовался.
Было интересно померить максимальную производительность на одном диске.
- GET с прямым доступом к PostgreSQL — HDD-диск (каждый запрос читает базу данных, из структуры генерируется JSON).
Один шпиндель
- GET с прямым доступом к PostgreSQL — SSD-диск (каждый запрос читает базу данных, из структуры генерируется JSON).
Один SSD-накопитель
- GET с кешированием в BOLT DB — HDD-диск (ранее JSON сохранялся в BOLT DB)
- GET с кешированием в BOLT DB — SSD-диск (JSON ранее сохранялся в BOLT DB)
- GET с кэшированием структуры в памяти (из структуры генерируется JSON)
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 виртуального процессора)
Результаты тестирования GET с разными типами кэширования
- Размер JSON - 1800 байт. Один главный объект Dept и 10 вложенных объектов Emp.
- Размер таблицы Dept — 33 000 000, таблицы Emp — 330 000 000 строк.
- GET без кэширования с прямым доступом к PostgreSQL
- GET с кэшированием JSON от BBolt (размер 74 ГБ)
- GET с кэшированием памяти (стандартная карта)
Таблица с агрегированными данными доступна по адресу связь График количества запросов в секунду в зависимости от параллелизма.
График времени на запрос.
[мс] из параллелизма (логарифмическая шкала).
График 99%-го процентиля [мс] в зависимости от параллелизма.
График 99%-го процентиля [мс] в зависимости от параллелизма (логарифмическая шкала).
Вот как выглядит загрузка внутреннего сервера при 100 000 [#/сек] и кэшировании данных в BBolt (параллелизм 128).
Из 1600% процессорного времени программа использует 1464%.
Средняя задержка ввода-вывода на диске NVMe составляет 0,02 [мс] (500 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%)
- Чтение входящих запросов — net/http.(*conn).
readRequest — (9,29%).
Входящие запросы не имели тела.
- Запись исходящих ответов - net/http.(*response).
finishRequest - (21,74%).
Запись тела ответа.
- Фактический обработчик запроса — net/http.(*ServeMux).
ServeHTTP — (23,33%).
Из общих затрат на 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%)
Теги: #api #open source #postgresql #Go #golang #json #http #rest api
-
Лучшее Ftp-Приложение Для Mac
19 Oct, 24 -
Прошивка Android Из Личного Опыта
19 Oct, 24 -
Сериализация Статических Объектов В C#
19 Oct, 24