Бенчмарки кэша PostgreSQL
Измерено на Apple M3 Max с PostgreSQL 16. В каждом сравнении для чтения через кэш и обычного SQL используются один и тот же клиент, набор данных и декодированные результаты строк.
Где кэш помог, а где нет
- SQL с одним ключом: подготовленный SQL превзошёл SQL
mgetв обеих опубликованных конфигурациях клиентов. При 256 соединениях Go он возвращал 253 790 запросов/с против 186 296 дляmget. Добавление кэша строк не улучшило эту SQL-нагрузку. - Пакеты SQL по 64 ключа: Go при 256 соединениях возвращал через
mget43 647 запросов/с против 27 615 для подготовленного SQL — примерно в 1,58 раза больше. Node.js при 64 соединениях показал меньший выигрыш: 16 616 против 15 577. Размер пакета и накладные расходы клиента важны; также проверяйте CPU и задержку. - RESP с одним ключом: Go при 256 соединениях достиг 839 678 запросов/с против базовых 253 790 для SQL. Воркеры RESP используют настроенную роль базы данных и не разделяют SQL-транзакцию или снимок вызывающего сеанса.
Измерения Node.js используют клиент macOS и сервер Docker; измерения Go и RESP запускают оба компонента внутри виртуальной машины Docker. На каждой странице приведены исходные повторы, точные версии и затраты ресурсов сервера. Эти отдельные конфигурации не позволяют ранжировать языки. Примеры соединений см. в разделах Node.js, Go или RESP.
Запустите одно и то же сравнение для каждого клиента
Из корня репозитория, с Docker, Node.js 20+ и Go 1.25+:
./examples/benchmark.sh all > comparison.json
python3 scripts/benchmark_report.py comparison.json
Запуск собирает временный сервер PostgreSQL и выполняет общую матрицу:
| Параметр | Все клиенты и пути чтения |
|---|---|
| Клиенты | Node.js с node-postgres / node-redis; Go с pgx / RESP2 из стандартной библиотеки |
| Пути чтения | Подготовленный SQL ANY, SQL mget, RESP MGET |
| Ключей за запрос | 1, 16, 64; одни и те же фиксированные ключи, начиная с 1 |
| Соединения | 4, 64, 256; постоянные, по одному незавершённому запросу на соединение |
| Выборки | Три повтора по пять секунд для каждого случая; порядок меняется |
| Размещение | Один отдельный контейнер клиента Linux с общим сетевым пространством имён PostgreSQL |
| Перед замером | Подключение, сравнение декодированных строк, затем прогрев каждого соединения |
| Контракт результата | Полные JSON-строки, порядок входных данных, дубликаты, null, отсутствующие ключи, пустой и полностью null-ввод |
| Измерения | Запросов/с, процентили задержки, CPU клиента, CPU/память сервера, счётчики кэша |
По умолчанию выполняется 162 выборки: около 14 минут измерений плюс настройка. Для короткой проверки корректности:
CONNECTIONS=4 BATCHES=1,16,64 REPEATS=1 DURATION_SECONDS=1 \
./examples/benchmark.sh all > smoke.json
Используйте node или go вместо all, чтобы выбрать один клиент с теми же
значениями по умолчанию. Можно переопределить CONNECTIONS, BATCHES,
REPEATS, DURATION_SECONDS и GOMAXPROCS Go. Node.js использует один поток
цикла событий; Go по умолчанию использует восемь потоков. SQL mget в Node.js
оборачивает возвращённый массив в JSON, а pgx декодирует текстовый массив
PostgreSQL. Эти затраты клиентов входят в измерение.
Одинаковые нагрузки не делают протоколы взаимозаменяемыми: воркеры RESP
используют настроенную роль базы данных и не присоединяются к SQL-транзакции или
снимку вызывающего сеанса. См. контракт RESP.
Общее сравнение измеряет прогретые чтения. Диагностика холодного кэша,
смешанных чтений и записей и накладных расходов записи остаётся отдельной, в
node-workload.
Опубликованные результаты за 14–15 сентября ниже появились до общего запуска. Их исходные окружения и ревизии исходного кода сохранены в данных; это не новые измерения единой матрицы.
Тестовое окружение
| Компонент | Конфигурация |
|---|---|
| Хост | MacBook Pro Mac15,10, Apple M3 Max: 10 производительных + 4 эффективных ядра, 36 ГиБ RAM |
| ОС | macOS 26.5.2, сборка 25F84, arm64 |
| ВМ Docker | Engine 29.7.2, Linux 7.0.12-linuxkit, 14 CPU, 7,65 ГиБ RAM; ограничений CPU или RAM контейнера нет |
| PostgreSQL | 16.15, Debian bookworm; 300 соединений, 128 МиБ shared buffers, 256 МиБ /dev/shm |
| Данные | 4 096 строк, значения по 128 байт; 1 024 записи кэша; данные и WAL в tmpfs |
Методика измерений
Клиент и сервер используют CPU Mac вместе с десятью другими контейнерами разработки. Все клиенты кодируют запросы и декодируют полные JSON-строки, сохраняя порядок входных данных, дубликаты и отсутствующие позиции. SQL использует подготовленные операторы; подключения, аутентификация и прогрев не входят в измерение. TLS и конвейерной обработки нет.
CPU сервера берётся из счётчиков cgroup контейнера PostgreSQL. Одно ядро означает одну CPU-секунду за прошедшую секунду; доли загрузки вычисляются от 14. CPU мкс/запрос — это время CPU сервера, разделённое на число завершённых запросов. В окно выборки входят работа мониторинга и короткая пауза формирования отчёта клиента.
Память — это memory.current cgroup, снимаемая каждые 500 мс и в конечных
точках. В таблицах указана медиана пиков каждой выборки, включая общую память,
tmpfs и кэш страниц; это не RSS процесса. JSON-файлы также содержат блочный
ввод-вывод, троттлинг, события памяти и снимки состояния SQL/ожиданий. Сетевые
счётчики исключают loopback и поэтому не учитывают трафик клиента внутри ВМ.
Эти короткие запуски с прогретым кэшем на общем ноутбуке не являются оценкой
производительности production. Данные и WAL используют tmpfs при включённых
fsync, full_page_writes и synchronous_commit; производительность диска не
проверялась.
Неудачный запуск завершается с ненулевым кодом и сохраняет завершённые выборки;
рендерер Markdown отклоняет частичные результаты. Для точного записанного кода
используйте harness_ref и extension_ref каждого JSON. Команды воспроизведения
приведены на страницах клиентов; результаты записываются в JSON-файлы, например
benchmark.json.
Обновлено .