Почему СУБД такие медленные

в 9:01, , рубрики: cassandra, CedarDB, DuckDB, dynamodb, imdb, LeanStore, mongodb, mysql, NVMe, OrioleDB, pcie, postgresql, PostgreSQL 17, redis, ruvds_статьи, SATA, spdk, sqlite, ssd, TPC-DS, TPC-H, UmbraDB, Valkey, резидентная БД, соединение хеш-таблиц, структуры данных
Почему СУБД такие медленные - 1

Недавно на Хабре публиковался перевод статьи «Просто выберите Postgres» (оригинал, англ. яз) с аргументами, что Postgres — оптимальная БД для десктопных и мобильных приложений. Аналогичное мнение высказывают в других популярных статьях вроде «До свидания MongoDB, здравствуй PostgreSQL». Главным недостатком SQLite называют то, что данные хранятся в одном файле, а MongoDB (а также DynamoDB и Cassandra) — низкую производительность:

«Всё это связано с тем, что подобные базы данных, по сути, представляют собой огромную распределённую хеш-таблицу. Единственные операции, работающие без необходимости сканирования всей базы данных — это поиск по секционному ключу и сканы, при которых используется ключ сортировки.

…Если паттерны доступа существенно изменятся, то может потребоваться полная повторная обработка всех данных».

Более производительные резидентные БД хранят данные в памяти (Redis, Valkey), но их использование ограничено объёмом ОЗУ.

После такого заявления интересно посмотреть на независимые тесты производительности разных СУБД.

Разумеется, независимые тесты объективны с той оговоркой, что они выполнены на относительно небольших наборах данных (до 100 ГБ) и со специфическими SQL-запросами. Такие бенчмарки не всегда отражают реальную производительность системы в продакшне.

В открытом доступе есть результаты независимого тестирования Clickbench, которые можно воспроизвести и проверить самостоятельно. Более того, можно взять из репозитория готовый скрипт, запустить у себя — и добавить результат в общую таблицу Clickbench.

Бенчмарки для разных типов СУБД приведены с разными конфигурациями серверов, инстансов и размерами кластера, так что не всегда это честное сравнение, а некая «сборная солянка». В таблице ниже аппаратная конфигурация сервера указана в скобках после названия СУБД.

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

В описании методологии говорится, что набор данных получен из реальных записей трафика «одной из крупнейших в мире платформ веб-аналитики». Он анонимизирован с сохранением всех существенных распределений данных. Набор из 42-х запросов творчески составлен «для отражения реалистичных рабочих нагрузок», при этом запросы не взяты непосредственно из продакшна.

Усреднённые результаты горячего запуска (hot run) разных кластеров и конфигураций приведены в относительных значениях к лучшему показателю (чем меньше значение, тем лучше), хотя этого рекорда нет в таблице. Вот некоторые избранные результаты (лидеры и аутсайдеры), под спойлером полная картина:

Umbra (c6a, metal, 500gb gp2) ×1,62
ClickHouse (базовая оптимизация некоторых запросов, а также оптимизация некоторых запросов по памяти) (c6a, metal, 500gb gp2) ×1,97
ClickHouse (базовая оптимизация) (c6a, metal, 500gb gp2) ×2,06
Apache Doris (c6a, metal, 500gb gp2) ×2,18
ClickHouse (c6a, metal, 500gb gp2) ×2,23
StarRocks (c6a, metal, 500gb gp2) ×2,40
Umbra (c6a, 4xlarge, 500gb gp2) ×2,43
Databend (c6a, metal, 500gb gp2) ×2,49
DuckDB (c6a, metal, 500gb gp2) ×2,70
QuestDB (секционированная) (c6a, metal, 500gb gp2) ×3,07
Redshift (4×ra3, 16xlarge) ×3,62
ClickHouse Cloud (gcp) (708 ГБ) ×3,98
MonetDB (c6a, 4xlarge, 500gb gp2) ×18,53
Elasticsearch (базовая оптимизация) (c6a, 4xlarge, 1500gb gp2) ×35,30
PostgreSQL (базовая оптимизация) (c6a, 4xlarge, 500gb gp2) ×65,49
MariaDB ColumnStore (c6a, 4xlarge, 500gb gp2) ×97,52
Pandas (DataFrame) (c6a, metal, 500gb gp2) ×262,74
MongoDB (c6a, 4xlarge, 500gb gp2) ×659,67
SQLite (c6a, 4xlarge, 500gb gp2) ×1419,83
MySQL (c6a, 4xlarge, 500gb gp2) ×4244,94
MariaDB (c6a, 4xlarge, 500gb gp2) ×27 285,69
Все результаты

Umbra (c6a, metal, 500gb gp2) ×1,62
ClickHouse (базовая оптимизация некоторых запросов, а также оптимизация некоторых запросов по памяти) (c6a, metal, 500gb gp2) ×1,97
ClickHouse (базовая оптимизация) (c6a, metal, 500gb gp2) ×2,06
Apache Doris (c6a, metal, 500gb gp2) ×2,18
ClickHouse (c6a, metal, 500gb gp2) ×2,23
StarRocks (c6a, metal, 500gb gp2) ×2,40
Umbra (c6a, 4xlarge, 500gb gp2) ×2,43
ClickHouse (веб) (c6a, metal, 500gb gp2) ×2,47
Databend (c6a, metal, 500gb gp2) ×2,49
DuckDB (c6a, metal, 500gb gp2) ×2,70
QuestDB (секционированная) (c6a, metal, 500gb gp2) ×3,07
Redshift (4×ra3, 16xlarge) ×3,62
ClickHouse Cloud (gcp) (708 ГБ) ×3,98
SingleStore (S24) ×4,05
ClickHouse Cloud (aws) (360 ГБ) ×4,27
ClickHouse Cloud (azure) (360 ГБ) ×4,32
ClickHouse Cloud (aws) (720 ГБ) ×4,34
ClickHouse Cloud (gcp) (360 ГБ) ×4,42
DuckDB (c6a, 4xlarge, 500gb gp2) ×4,45
ClickHouse (c6a, 4xlarge, 500gb gp2) ×4,50
Databend (c6a, 4xlarge, 500gb gp2) ×4,61
Crunchy Bridge for Analytics (Parquet) (Analytics-256GB (64 vCores, 256  ГБ)) ×4,97
chDB (DataFrame) (c6a, metal, 500gb gp2) ×4,99
Databend (c5,4xlarge, 500gb gp2) ×5,00
ClickHouse (веб) (c5n,4xlarge, 500gb gp2) ×5,09
DuckDB (DataFrame) (c6a, metal, 500gb gp2) ×5,16
ClickHouse Cloud (azure) (192 ГБ) ×5,21
DuckDB (c5, 4xlarge, 500gb gp2) ×5,26
ClickHouse Cloud (gcp) (192 ГБ) ×5,51
Apache Doris (c6a, 4xlarge, 500gb gp2) ×5,80
StarRocks (c6a, 4xlarge, 500gb gp2) ×5,84
Snowflake (64×3XL) ×5,90
ClickHouse Cloud (aws) (192 ГБ) ×5,94
Snowflake (32×2XL) ×6,13
chDB (c6a, metal, 500gb gp2) ×6,32
Snowflake (128×4XL) ×6,52
QuestDB (c6a, 4xlarge, 500gb gp2) ×6,70
chDB (Parquet, секционированная) (c6a, metal, 500gb gp2) ×6,72
Redshift (serverless) ×6,75
ClickHouse Cloud (azure) (96 ГБ) ×6,95
Tablespace (L1 — 16CPU 32 ГБ) ×7,00
Snowflake (16×XL) ×7,19
ClickHouse (Parquet, секционированная) (c6a, metal, 500gb gp2) ×7,77
ByConity (c6a, 4xlarge, 500gb gp2) ×7,84
ClickHouse Cloud (gcp) (96 ГБ) ×7,89
ByteHouse (8×L) ×7,92
Redshift (4×ra3,4xlarge) ×8,12
ClickHouse (Parquet, одиночная) (c6a, metal, 500gb gp2) ×8,67
ClickHouse Cloud (aws) (96 ГБ) ×8,74
Snowflake (8×L) ×9,47
chDB (c6a, 4xlarge, 500gb gp2) ×10,00
ClickHouse Cloud (azure) (48 ГБ) ×10,24
DuckDB (Parquet, секционированная) (c6a, 4xlarge, 500gb gp2) ×10,35
chDB (Parquet, секционированная) (c6a, 4xlarge, 500gb gp2) ×10,82
Oxla (c6a, 4xlarge, 500gb gp2) ×11,03
SingleStore (S2) ×11,14
ClickHouse Cloud (aws) (48 ГБ) ×11,22
ClickHouse (Parquet, секционированная) (c6a, 4xlarge, 500gb gp2) ×11,25
ClickHouse Cloud (aws) (dev) ×11,67
DataFusion (Parquet, секционированная) (c6a, 4xlarge, 500gb gp2) ×11,68
ClickHouse Cloud (gcp) (48 ГБ) ×12,17
ByteHouse (4×M) ×12,35
Snowflake (4×M) ×12,95
Redshift (2×dc2,8xlarge) ×13,20
ParadeDB (Parquet, одиночная) (c6a, 4xlarge, 500gb gp2) ×13,28
ClickHouse (Parquet, одиночная) (c6a, 4xlarge, 500gb gp2) ×13,41
ParadeDB (Parquet, секционированная) (c6a, 4xlarge, 500gb gp2) ×13,50
DataFusion (Parquet, одиночная) (c6a, 4xlarge, 500gb gp2) ×14,08
Redshift (4×ra3,xlplus) ×14,55
ClickHouse Cloud (gcp) (dev) ×15,42
ClickHouse (озеро данных, секционированная) (c6a, metal, 500gb gp2) ×15,67
ByteHouse (2×S) ×15,82
ClickHouse (озеро данных, одиночная) (c6a, metal, 500gb gp2) ×16,43
Motherduck (cloud) ×16,52
Snowflake (2×S) ×18,13
MonetDB (c6a, 4xlarge, 500gb gp2) ×18,53
ClickHouse Cloud (azure) (24 ГБ) ×19,15
SingleStore (c6a, 4xlarge, 500gb gp2) ×19,77
ClickHouse Cloud (aws) (24 ГБ) ×24,17
ClickHouse Cloud (gcp) (24 ГБ) ×24,53
ByteHouse (XS) ×26,39
Snowflake (XS) ×26,40
ClickHouse (озеро данных, одиночная) (c6a, 4xlarge, 500gb gp2) ×28,74
GlareDB (c6a, 4xlarge, 500gb gp2) ×32,21
ClickHouse (озеро данных, секционированная) (c6a, 4xlarge, 500gb gp2) ×32,67
Elasticsearch (базовая оптимизация) (c6a, 4xlarge, 1500gb gp2) ×35,30
Polars (DataFrame) (c6a, metal, 500gb gp2) ×36,87
Cloudberry (c6a, 4xlarge, 500gb gp2) ×41,57
Greenplum (c6a, 4xlarge, 500gb gp2) ×44,05
Pinot (c6a, 4xlarge, 500gb gp2) ×47,40
Athena (одиночная) (serverless) ×51,24
Athena (секционированная) (serverless) ×57,79
Hydra (c6a, 4xlarge, 500gb gp2) ×58,23
Tembo OLAP (колоночная) (c6a, 4xlarge, 500gb gp3) ×59,99
PostgreSQL (базовая оптимизация) (c6a, 4xlarge, 500gb gp2) ×65,49
AlloyDB (16 vCPU 128 ГБ) ×78,26
AlloyDB (8 vCPU 64 ГБ) ×83,34
MariaDB ColumnStore (c6a, 4xlarge, 500gb gp2) ×97,52
GlareDB (c6a, metal, 500gb gp2) ×98,34
CrateDB (c6a, 4xlarge, 500gb gp2) ×100,51
Elasticsearch (c6a, 4xlarge, 1500gb gp2) ×121,55
TimescaleDB (со сжатием) (c6a, 4xlarge, 500gb gp2) ×142,99
AlloyDB (базовая оптимизация) (8 vCPU 64 ГБ) ×167,76
Druid (c6a, 4xlarge, 500gb gp2) ×247,61
Pandas (DataFrame) (c6a, metal, 500gb gp2) ×262,74
HeavyAI (c6a, 4xlarge, 500gb gp2) ×267,94
Kinetica (c6a, 4xlarge, 500gb gp2) ×339,75
Citus (c6a, 4xlarge, 500gb gp2) ×364,09
Aurora for PostgreSQL (16acu) ×629,82
MongoDB (c6a, 4xlarge, 500gb gp2) ×659,67
Infobright (c6a, 4xlarge, 500gb gp2) ×924,60
TimescaleDB (c6a, 4xlarge, 500gb gp2) ×1402,60
SQLite (c6a, 4xlarge, 500gb gp2) ×1419,83
PostgreSQL (c6a, 4xlarge, 500gb gp2) ×1454,28
MySQL (MyISAM) (c6a, 4xlarge, 500gb gp2) ×2176,43
MySQL (c6a, 4xlarge, 500gb gp2) ×4244,94
Aurora for MySQL (16acu) ×6428,63
MariaDB (c6a, 4xlarge, 500gb gp2) ×27285,69

Полная таблица с результатами включает абсолютное время выполнения каждого запроса (в секундах), а также специальные примечания по результатам тестирования, на сайте она постоянно обновляется.

В качестве альтернативных бенчмарков можно посмотреть Brown University Mgbench, UC Berkeley AMPLab Big Data Benchmark, Mark Litwinschik's NYC Taxi, бенчмарк h2o.ai, стандартный набор тестов TPC-H и его более продвинутую версию TPC-DS.

▍ Umbra (CedarDB)

Как видно выше, в тестах производительности с большим отрывом побеждает СУБД Umbra.

Umbra — это исследовательский проект и экспериментальная система, которая заявляет производительность при работе с SSD на уровне резидентных СУБД. В ней реализован прямой доступ к файловой системе, параллельное выполнение запросов и производительность в сотни гигабайт в секунду.

Коммерческая версия Umbra распространяется под названием CedarDB как «расширение» PostgreSQL.

Одна из особенностей CedarDB — использование более эффективных реляционных соединений (relational joins) в качестве соединения хешированием (hash join). Оригинальная реализация алгоритма соединения хеш-таблиц (TUM) описана в научной работе и недавно была представлена на научной конференции по базам данных DaMoN'24.

Дело в том, что существующие реализации хеш-таблиц на С++ вроде
abseil, ankerl, boost и folly не очень хорошо подходят для обработки соединений в БД, в частности, для параллельной обработки запросов без блокировки, эффективной фильтрации несовпадающих строк и для дедупликации. Поэтому разработчики представили собственную оригинальную реализацию, которая и позволила в несколько раз увеличить производительность СУБД.

Была также разработана кастомная хеш-функция на основе CRC, которая помещается всего в несколько инструкций:

u64 hash32(u32 key, u32 seed) {
    u64 k = 0 x8648DBDB;          // Mixing constant
    u32 crc = crc32(seed, key);   // crc32
    return crc * ((k << 32) + 1); // imul
}

Если маленькая хеш-таблица полностью помещается в кеш CPU, то уменьшение количества инструкций напрямую ускоряет скорость выполнения. И даже при обращении к оперативной памяти выигрыш очень большой благодаря эффективному буферу переупорядочивания в современных CPU, который вмещает сотни инструкций. Вот как выглядит самый часто вызываемый фрагмент кода (hot loop) для процедуры поиска в хеш-таблицах:

u64 shift; // Used to reduce a hash to a directory slot
u64 directory[1 << (64 - shift)];
void lookup(K key, u64 hash) {
    u64 slot = hash >> shift;                     // shr
    u64 entry = directory[slot];                  // mov
    if (!couldContain((u16)entry, hash)) return;  // jnz
    // probably found a match, check the key
}

u16 tags[1 << 11] = {15, 23, 27, 29, 30, 39, ...};// Precalculated 4 bit Bloom filter tags
bool couldContain(u16 entry, u64 hash) {
    u16 slot = ((u32) hash) >> (32 - 11);         // shr
    u16 tag = tags[slot];                         // mov
    return !(tag & ~entry);                       // andn
}

Что касается производительности, то разработчики провели сравнительное тестирование в пяти разных наборах бенчмарков с более чем 10 000 запросами на стандартной рабочей станции AMD Ryzen 5950X с 64 ГБ ОЗУ. По сравнению со стандартной схемой хеширования Robin Hood зафиксировано ускорение до 16-ти раз:

Почему СУБД такие медленные - 2

Ниже показана производительность в тестах TPC-H по сравнению с Hyper и DuckDB. Обратите внимание, что диаграмма с масштабным коэффициентом 100:

Почему СУБД такие медленные - 3

На этой рабочей нагрузке с объёмом данных около 100 ГБ система CedarDB работает примерно в шесть раз быстрее.

Насколько ускоряется работа СУБД при параллельном выполнении запросов, можно посмотреть на примере запроса 9.sql из набора тестов TPC-H.

Выполнение запроса в PostgreSQL на наборе данных 100 ГБ занимает 5 мин 32 сек:

Почему СУБД такие медленные - 4

Выполнение того же запроса в CedarDB занимает три секунды:

Почему СУБД такие медленные - 5

▍ Улучшение производительности в PostgreSQL 17

В сентябре 2024 года вышла PostgreSQL 17 с большим количеством улучшений. Среди них можно выделить:

  • существенная оптимизация очистки (vacuuming): новая внутренняя структура памяти потребляет до 20-ти раз меньше ОЗУ (кстати, эту задачу также решает OrioleDB, инновационный движок для хранения данных PostgreSQL со сжатием),
  • улучшенная производительность уровня ввода-вывода: на рабочих нагрузках с высоким уровнем параллелизма пропускная способность записи выросла до двух раз благодаря улучшениям в обработке журнала предзаписи (WAL). Кроме того, новый интерфейс потокового I/O ускоряет последовательное сканирование (чтение всех данных из таблицы) и скорость обновления статистики планировщика с помощью ANALYZE,
  • повышение производительности запросов с конструкциями IN, которые используют индексы B-tree (метод индексации по умолчанию в PostgreSQL); кроме того, индексы BRIN теперь поддерживают параллельную сборку,
  • несколько улучшений для планирования запросов, в том числе оптимизация для ограничений NOT NULL и улучшения в обработке общих табличных выражений (запросы WITH). В этом релизе добавлена поддержка параллелизма на уровне данных SIMD (Single Instruction/Multiple Data) для ускорения вычислений, включая набор инструкций AVX-512 для функции bit_count,
  • очень нужные функции резервного копирования: утилита pg_basebackup теперь поддерживает инкрементальные бэкапы, добавлена утилита pg_combinebackup для восстановления полной резервной копии из инкрементальных бэкапов,
  • обёртка для внешних данных PostgreSQL postgres_fdw, которая выполняет запросы на удалённых инстансах PostgreSQL, теперь может передавать на удалённый сервер подзапросы EXISTS и IN.

Улучшения действительно существенные, а разработка PostgreSQL продолжается полным ходом. Кроме всего прочего, есть идея перехода с модели «много процессов» на модель «один процесс, много потоков», хотя это грозит потерей обратной совместимости со старым кодом. Отдельные патчи уже готовы.

▍ Оптимизация ввода-вывода

Исследования показывают, что реальная производительность любых СУБД существенно уступает максимально возможной теоретической производительности современных SSD типа NVMe. В среднем, разница в производительности составляет 3,5 раза от самых быстрых СУБД:

Почему СУБД такие медленные - 6
Производительность операций случайного поиска в базе данных объёмом 100 ГБ с буфером 10 ГБ на восьми SSD корпоративного класса, источник

Потенциал для улучшений здесь огромный. Исследователи считают, что производительность СУБД можно значительно повысить за счёт оптимизации программно-аппаратной архитектуры под Linux. Они предложили дизайн с параллельными потоками и многопоточностью, как показано на схеме:

Почему СУБД такие медленные - 7

Система параллельно обрабатывает множество входящих запросов. Рабочие потоки (воркеры) совместно выполняют эти запросы, а также берут на себя вытеснение страниц, ввод/вывод данных и опросы. Любой воркер может работать с любым SSD из массива по модели all-to-all:

Почему СУБД такие медленные - 8

Вот примерная последовательность событий в потоке воркера при обработке ошибки страницы в пользовательской задаче:

Почему СУБД такие медленные - 9

Количество операций в секунду и пропускная способность зависят от размера страницы в хранилище. При увеличении размера страницы растёт пропускная способность и задержка, а показатель IOPS снижается после 4 КБ:

Почему СУБД такие медленные - 10

Оптимальным компромиссом оказался размер страницы 4 КБ. Главным результатом исследования стал вывод, что для достижения максимальной производительности рабочие потоки СУБД должны работать параллельно и взять на себя все операции с хранилищем данных в обход ядра Linux, включая ввод-вывод, вытеснение страниц и работу с памятью. «У симметричного дизайна есть концептуальные преимущества и он позволяет построить более надёжную систему», — сказано в научной работе.

Работа с SSD напрямую в обход ядра Linux осуществляется с помощью драйверов Intel Storage Performance Development Kit (SPDK) для высокопроизводительных приложений:

Почему СУБД такие медленные - 11

На диаграмме показана производительность разных систем ввода-вывода, включая три системы ядра Linux и SPDK (в обход ядра).

Почему СУБД такие медленные - 12

Работать в обход в ядра важно, потому что ОС замедляет операции с SSD и выдаёт ложные метрики, сообщая о завершённой записи данных до того, как они реально записаны на физический носитель.

▍ Выводы

За последнее десятилетие SSD на флэш-памяти вытеснили HDD в качестве основного накопителя для СУБД в продакшне. На смену интерфейсу SATA пришёл более производительный PCIe/NVMe, что позволило добиться невиданной ранее пропускной способности систем хранения данных. По четырём линиям PCIe 4.0 один SSD выполняет более миллиона случайных операций ввода-вывода в секунду (IOPS) и выдаёт пропускную способность 7 ГБ/с. А у современных серверов до 128 линий PCIe на сокет, так что на одном сервере можно легко разместить восемь (и более) SSD на полной пропускной способности.

Увеличение производительности железа происходит на фоне роста аналитической сложности БД:

Почему СУБД такие медленные - 13

Тенденция к увеличению пропускной способности систем хранения будет продолжаться: уже появились серверы с PCIe 5.0 и соответствующие SSD со скоростью 12 ГБ/с. Это означает, что массивы NVMe приближаются к пропускной способности DRAM.

В такой ситуации важное значение имеет оптимизация работы SSD. Можно предположить, что в ближайшие годы их производительность может приблизиться к производительности резидентных СУБД, которые работают в памяти, как Redis.

Как видим, уже появляются первые СУБД вроде Umbra/CedarDB, которые заявляют производительность на уровне резидентных СУБД. То есть производительность массива SSD не уступает ОЗУ.

Возможно, в такой ситуации в будущем экономически выгоднее будет вкладываться в покупку массивов NVMe, а не оперативной памяти, которая в 10−50 раз дороже. Так что можно обойтись без Redis, особенно если избегать типичных ошибок в PostgreSQL.

© 2024 ООО «МТ ФИНАНС»

Автор: alizar

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js