Чи може PostgreSQL з його JSONB замінити MongoDB

Мене звати Юрій Івон, я співпрацюю з компанією EPAM як Senior Solution Architect. У цій статті хотів би поділитися спостереженнями та думками на тему доцільності використання деяких особливостей PostgreSQL на заміну повноцінній документоорієнтованій базі даних.

Ця стаття доступна також англійською на Medium.

PostgreSQL — це система управління базами даних, яка зараз надзвичайно популярна. На мою думку, така популярність цілком заслужена, оскільки вона не тільки забезпечує виняткову продуктивність та надійність, але й надає багато унікальних функцій, яких немає в інших реляційних базах даних: тип JSONB, інвертований індекс, масиви та багато інших.

Кілька разів я чув питання: «Якщо PostgreSQL підтримує JSONB і дозволяє створювати індекси на окремих полях документа, який сенс використовувати документоорієнтовані бази, як-от MongoDB?». Це питання може здатися дивним і непрофесійним, оскільки воно не враховує багато важливих факторів, але якщо розробники ставлять подібні питання, дамо відповідь.

Спочатку потрібно визначити, що таке JSONB. Простими словами, це бінарно-серіалізований JSON — він отримує текст JSON як вхідні дані, але зберігає його у двійковому форматі, що робить його дещо повільнішим під час введення через додаткові витрати на перетворення, але значно швидшим для обробки, оскільки повторний аналіз не потрібен. Це робить його схожим на BSON, який походить від MongoDB.

Подивимося, що можна робити зі стовпцем типу JSONB у PostgreSQL.

create table sample_jsonb (
    id serial not null primary key,
    data jsonb
);

insert into sample_jsonb (data) values ('{"name": "First", "count": 12, "orderDate": "2022-03-03T12:14:01", "extra": "some text"}');
insert into sample_jsonb (data) values ('{"name": "Second", "count": 23, "orderDate": "2022-04-03T15:17:01"}');

select data->>'name', data->>'orderDate' from sample_jsonb where (data->'count')::int > 12

Ми створили таблицю з колонкою типу JSONB, вставили туди кілька JSON документів і зробили запит, отримуючи доступ до їхніх окремих полів. Тут можна побачити перший незручний момент: нестрокові поля повинні бути явно приведені до цільового типу для порівняння з відповідними значеннями. Це обумовлено тим, що існує лише два оператори, що дають доступ до окремих полів JSONB документа: -> та ->>, які повертають відповідно JSONB та текстове представлення.

Перше важливе питання, яке тут може виникнути: «Як ці поля можна проіндексувати?». Хороша новина полягає в тому, що вирази доступу до полів, які використовуються у запиті вище, також можуть брати участь у визначеннях індексів (у цьому випадку вираз необхідно взяти в дужки):

create index sample_jsonb_count_idx on sample_jsonb (((data->'count')::int));

Подальші виконання оригінального запиту зможуть скористуватися цим індексом.

Так, PostgreSQL дозволяє зберігати об’єкти динамічної структури та індексувати їхні поля або комбінації полів. Це вже може здатися документоорієнтованою базою даних, але ще зарано робити висновки — у нас ще є кілька пунктів для огляду.

Говорячи про JSONB, я не можу оминути Generalized Inverted Index (GIN), який може вирішити деякі сценарії, які зазвичай становлять проблему для баз даних загального призначення:

  • повнотекстовий пошук;
  • фільтрація рядків за довільними комбінаціями кількох полів.

На високому рівні можна розглядати інвертований індекс як словник, який надає список посилань на документи для кожної окремої комбінації шляху до поля та відповідного значення. Якщо ми проіндексуємо всі поля з JSONB документа, а потім запустимо запит з деякими з них в умові, база зможе швидко знайти посилання на документи для всіх пар «поле — значення» та обчислити їхній перетин. На низькому рівні існує багато оптимізацій, що дають високу ефективність інвертованих індексів під час обробки довільних комбінацій предикатів запиту.

Набір специфічних для GIN операторів, які можна використовувати в запиті, залежить від класу, вказаного під час створення індексу. Для JSONB підтримуються такі класи:

  • jsonb_ops — клас за замовчуванням, який надає дві категорії операторів: перевірка існування атрибутів і перевірка існування значень на основі виразів JSONPath або пар ім’я-значення;
  • jsonb_path_ops — надає лише останню категорію, але з кращою продуктивністю.

Розглянемо кілька прикладів, щоб краще зрозуміти ідею.

create index sample_jsonb_path_ops_idx on sample_jsonb using gin (data jsonb_path_ops);
select * from sample_jsonb where data @> '{"name":"First"}'::jsonb
select * from sample_jsonb where data @@ '$.count > 15'

Перший із цих запитів повертає всі рядки, де атрибут name дорівнює First, а другий — усі рядки, де count перевищує 15. Так, якщо вам потрібно перевірити рівність, оператора @> буде достатньо. Для всіх інших типів умов вам знадобляться вирази JSONPath разом з оператором @@.

Цей синтаксис може виглядати складним, але він справляється із задачею та розширює можливості за межі того, що зазвичай очікується від реляційної бази даних.

З огляду вище може скластися враження, що PostgreSQL надає достатньо «документоорієнтованих» можливостей і є навіть більш гнучким з точки зору запитів, ніж MongoDB, незважаючи на деякі складнощі синтаксису. Однак невідомо, наскільки швидко працюють запити, що взаємодіють з JSONB. Щоб закрити цю прогалину, запустімо кілька тестів для порівняння продуктивності MongoDB і PostgreSQL, за умови, що структури даних і запити еквівалентні в обох базах. Для цього я буду використовувати мою утиліту Database Benchmark, яка є дуже гнучким інструментом для тестування продуктивності запитів.

Умови тесту

Я буду використовувати набір статистичних даних від EUROSTAT про платіжний баланс установ ЄС, завантажений у форматі SDMX-CSV. На момент виконання тестів в ньому було приблизно 11 мільйонів рядків (одне статистичне спостереження на рядок). Спостереження в статистичних даних зазвичай мають багато атрибутів, тому легко продемонструвати, як різні комбінації предикатів впливають на продуктивність.

Індекси та моделі зберігання, які будемо порівнювати:

  • MongoDB — складений індекс країни-партнера та періоду часу.
  • MongoDB — два окремі індекси для країни-партнера та періоду часу — щоб перевірити, чи можуть окремі індекси допомогти в сценаріях, коли створення складених індексів для всіх можливих комбінацій предикатів є недоцільним.
  • PostgreSQL — усі атрибути зберігаються як звичайні стовпці зі складеним індексом країни-партнера та періоду часу.
  • PostgreSQL — усі атрибути зберігаються як звичайні стовпці з двома окремими індексами для країни-партнера та періоду часу.
  • PostgreSQL — усі атрибути зберігаються як один стовпець JSONB зі складеним індексом для країни-партнера та періоду часу — щоб побачити, наскільки добре звичайний складений індекс працює з JSONB-стовпцем.
  • PostgreSQL — усі атрибути зберігаються як один JSONB-стовпець з GIN-індексом класу операторів jsonb_path_ops.

Іншими важливими аспектами тесту є:

  • Параметри запитів рандомізуються під час кожного виконання.
  • Для кожного запиту є два тести — в один потік і в десять паралельних потоків. Останній виконується лише тоді, коли середня тривалість для більшості випадків не перевищує секунди в однопотоковому режимі. Немає сенсу тестувати паралелізм, якщо запит і так повільно працює.
  • Кожен запит виконується 1000 разів в кожному потоці, якщо одне його виконання займає менше секунди, або 100 разів в іншому випадку.
  • Усі ресурси, що беруть участь у тестах, належать до одного регіону Azure (East US).
  • Сервер бази даних і тестова утиліта працюють на окремих віртуальних машинах.
  • Використовуються віртуальні машини типу D4s v3 (4 vCPU, 16 ГБ RAM), за єдиним винятком для тесту вставки даних, де мені довелося запускати утиліту на D8s v3 (8 vCPU, 32 ГБ RAM), щоб уникнути вузького місця на стороні клієнта.
  • Віртуальна машина, на якій розміщені бази даних, має Premium SSD на 128 ГБ та 500 IOPS з пропускною здатністю 100 МБ/с.
  • Використовувалися PostgreSQL 14.6 і MongoDB 6.0.3. Тести проводилися на початку 2023 року. Найновіша на той час 15-та версія PostgreSQL на перших двох тестах показала себе гірше 14-ї на GIN індексі при схожих результатах для інших типів індексу, тому я вирішив брати 14 лінійку як більш стабільну і перевірену.
  • Тестова утиліта використовує найсвіжіші клієнтські бібліотеки для баз даних, що досліджуються. Клієнтські налаштування за замовчуванням не змінюються.
  • Усі вихідні файли для тестів можна знайти тут.

Результати

Вставка даних

У наведених нижче діаграмі та таблиці підсумовано результати тестування вставок для різних баз даних та індексів. Тест записував близько 11 мільйонів рядків у 100 паралельних потоках.


Розмір даних виходить дуже різним для різних механізмів зберігання.

Більш детальну інформацію про результати можна знайти в таблиці нижче.

  • MongoDB значно швидший за PostgreSQL щодо запису нових даних.
  • MongoDB вимагає набагато менше місця для зберігання тих самих даних, ніж PostgreSQL.
  • PostgreSQL JSONB видається дуже неефективним з точки зору зберігання. Однією з причин такої великої різниці є те, що JSONB зберігає імена полів у кожному рядку — це фактично двійковий серіалізований документ JSON. Хоча MongoDB теж використовує подібний підхід серіалізації, він також додає компресію, що забезпечує більш компактний розмір. Через таку модель зберігання, використання коротких імен атрибутів в документах було б розумною технікою оптимізації з будь-яким з цих «документоорієнтованих» механізмів.
  • Незважаючи на те, що зберігання даних у JSONB потребує втричі більше місця та дає помітно більший піковий час додавання даних, середня та медіанна тривалість для JSONB є трохи кращою ніж для тих самих даних збережених в звичайних колонках. Подібний результат стабільно відтворюється, і наразі в мене немає цьому правдоподібного пояснення.

Варто зазначити, що продуктивність запису в MongoDB залежить від так званого write concern — ви можете отримати ще кращу продуктивність, послабивши вимогу підтвердження. За замовчуванням система відповідає на операцію запису тільки коли дані вже зафіксовано в журналі на диску. Цю поведінку можна змінити на більш ранню відповідь — відразу після збереження в оперативній пам’яті. Але це не варіант, якщо гарантоване збереження даних є обов’язковим.

Вибірка даних

1. Знайти всі спостереження для конкретних країни-партнера, індикатора та періоду часу

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

Середній та медіанний час виконання (логарифмічна шкала)

  • Індекс GIN виглядає найкращим в цьому тесті і зберігає ефективність без будь-якого погіршення навіть під час виконання запитів в десяти паралельних потоках.
  • MongoDB має другий найкращий результат у цьому тесті.
  • Зі складеним індексом в PostgreSQL немає великої різниці між звичайними стовпцями та JSONB — останній лише трохи гірший.
  • Окремі індекси не дуже допомагають, коли ви фільтруєте великий набір даних за кількома полями, але PostgreSQL набагато швидший з окремими індексами, ніж MongoDB. Це можна пояснити його здатністю об’єднувати результати сканування кількох індексів в одному запиті, чого немає в MongoDB.

2. Знайти усі спостереження для конкретних країни-партнера та періоду часу

Видалимо один предикат із попереднього запиту, щоб побачити, як зниження вибірковості впливає на результати.

Середній та медіанний час виконання (логарифмічна шкала)

  • Складені індекси PostgreSQL перемагають у цьому тесті. Дивним є те, що в однопотоковому сценарію таблиця зі звичайними стовпцями демонструє значно вищий середній час виконання, ніж таблиця з JSONB, незважаючи на майже однаковий медіанний час. Цей результат стабільно відтворюється, і я не маю цьому пояснення.
  • Результати GIN індексу в PostgreSQL цього разу досить близькі до результатів MongoDB.
  • Спостереження для окремих індексів такі ж, як і для попереднього запиту.

3. Знайти перші 100 спостережень, упорядкованих за спаданням періоду для конкретних країни-партнера та діапазону періодів

Щоб охопити якомога більше реальних сценаріїв, додаймо фільтрацію за діапазоном значень та сортування. У багатьох сценаріях вибірки даних сервер повертає лише сторінку записів, а розбиття на сторінки завжди передбачає певний порядок.

Середній та медіанний час виконання (логарифмічна шкала)


  • Складені індекси перемагають у цьому тесті, оскільки вони повністю охоплюють як критерії фільтрації, так і порядок.
  • MongoDB працює вдвічі гірше, ніж PostgreSQL з подібним складеним індексом.
  • Індекс GIN далекий від лідерів насамперед через його неспроможність допомогти з сортуванням — рушію бази даних доводиться проходити всі відповідні рядки та сортувати їх. Це важливий аспект, який необхідно враховувати при виборі індексу. У цьому тесті критеріям запиту відповідає до кількох тисяч рядків, що не так уже й багато порівняно з повним набором даних. Однак результат вже значно гірший, ніж у складеного індексу, оптимізованого під цей запит.
  • Дивно, але окремі індекси в PostgreSQL працюють набагато гірше, ніж окремі індекси в MongoDB, незважаючи на згадану раніше оптимізацію сканування індексу (bitmap scan). MongoDB використовує індекс TimePeriod для сканування всіх відповідних записів, які вже розміщені в правильному порядку, а потім фільтрує кожен відсканований рядок за країною, доки не буде знайдено достатньо результатів для сторінки.
    PostgreSQL об’єднує результати двох сканувань індексу за допомогою згаданого вище bitmap scan та явно сортує отриманий набір даних. Другий підхід виявляється дорожчим. Звичайно, цю поведінку можна змінити, і ми можемо змусити PostgreSQL поводитися так само, як MongoDB у цьому сценарії, але ідея полягала в тому, щоб перевірити поведінку за замовчуванням.
  • Середня тривалість запиту для окремих індексів PostgreSQL на порядок вища за медіану. Така велика різниця та надзвичайно високе стандартне відхилення вказують на дуже широкий розподіл результатів, де виконання невеликої частини запитів потребує надзвичайно багато часу. Подібна поведінка є вкрай небажаною, і її слід уникати, якщо це можливо.

4. Знайти перші 100 спостережень упорядкованих за спаданням періоду для конкретних індикатора та діапазону періодів

Тепер подивимося, як фільтрація за неіндексованим полем вплине на результати.

Середній та медіанний час виконання (логарифмічна шкала)

  • Індекс GIN виграє в цьому тесті, тому що він фактично має всі поля проіндексованими. І хоча рушій бази даних повинен сортувати весь набір відфільтрованих записів, кількість рядків, які йому потрібно обробити, значно менша в цьому випадку порівняно з іншими варіантами індексу.
  • Очевидно, складений індекс, що використовується в тестах, ніяк не може допомогти з цим запитом, тому ми можемо порівняти продуктивність повного сканування на основі результатів для складеного індексу.
  • І знову MongoDB перемагає PostgreSQL у випадку використання окремих індексів.
  • Хоча я очікував продуктивності на рівні попереднього тесту для окремих індексів MongoDB, вплив різного розподілу індикаторів та країн-партнерів на результат виявився суттєвим. Середня кількість записів з однаковим індикатором приблизно у тричі перевищує кількість записів з однаковою країною-партнером.
  • Тут ми спостерігаємо надзвичайно високу різницю між середнім значенням і медіаною для обох сценаріїв «окремого індексу». Як зазначалося раніше, така поведінка є вкрай небажаною, і її слід уникати, якщо можливо.

5. Знайти перші 100 спостережень, упорядкованих за спаданням періоду часу

Щоб завершити сценарії зі сторінками, перевіримо, як сортування працює на повному наборі даних.

Середній та медіанний час виконання (логарифмічна шкала)

  • Окремі індекси виграють, але це було очікувано.
  • Ще раз підтверджуємо, що повне сканування у звичайній таблиці PostgreSQL набагато швидше, ніж в MongoDB. В той же час повне сканування рядків з JSONB даними стало дещо гіршим порівняно з повним скануванням у MongoDB.
  • Немає різниці між GIN і складеним індексом для JSONB, оскільки цей запит не може використати жоден з них.

6. Отримати максимальне значення для кожної країни серед спостережень, відфільтрованих за діапазоном періодів
Агрегації дуже важливі для багатьох проєктів, тому має сенс протестувати хоча б просте групування разом з агрегатною функцією.

Середній та медіанний час виконання (лінійна шкала).

  • Цього разу виграють звичайні таблиці PostgreSQL. В обох випадках база виконувала повне сканування таблиці. Оскільки план виконання ідентичний в обох випадках, середня тривалість запиту є досить схожою, з майже однаковими середнім і максимальним значенням.
  • У кожному з випадків MongoDB обирає інший план запиту. Для колекції зі складеним індексом було обране повне сканування. Водночас, для колекції з окремими індексами було вирішено знайти всі відповідні документи за допомогою індексу TimePeriod і застосувати агрегацію пізніше. Незважаючи на використання індексу, останній сценарій дав найгірший результат.
  • Що стосується поля JSONB — PostgreSQL не може використовувати GIN для цього запиту. Проте продуктивність повного сканування виявилася помітно вищою порівняно зі сценарієм, що використовує складений індекс для виконання запиту.

7. Отримати всі унікальні країни-партнери
Запити на унікальні значення часто використовуються для визначення списку доступних значень в різних сценаріях фільтрації даних, тому їх теж варто перевірити.

Оскільки утиліта Database Benchmark виконує всі запити для MongoDB за допомогою команди aggregate, мені довелося замінити вбудовану команду «distinct» еквівалентним виразом через групування. Така зміна може вплинути на кінцевий план виконання, але я не очікую різниці для такого простого запиту.

Середній та медіанний час виконання (логарифмічна шкала)

  • Хоча обидві бази даних використовують сканування індексів (за винятком випадку з GIN), MongoDB виявляється значно швидшою. В Інтернеті небагато деталей про її «distinct scan», тому я можу лише припустити, що цей метод доступу не сканує значення індексу для кожного рядка та може швидко переходити між різними значеннями, тоді як PostgreSQL проходить по значенням індексу для кожного рядка в наборі даних.
  • Індекс GIN не допомагає з пошуком унікальних значень, тому для нього маємо повне сканування з подальшим сортуванням, оскільки PostgreSQL може збирати унікальні значення лише з відсортованого списку. Через це існує суттєва різниця між JSONB зі складеним індексом і JSONB з індексом GIN.
  • Тут також можна побачити, як розмір індексу впливає на час його сканування — принаймні для PostgreSQL різниця між складеним та окремими індексами очевидна.

8. Знайти всі спостереження за певний період часу для вказаних списків країн-партнерів і індикаторів
Безпосередньо перед публікацією статті я зрозумів, що не вистачає одного важливого сценарію — запиту, який фільтрує дані за списком значень окремих атрибутів (оператор IN у термінах SQL). В цьому тесті для кожного запиту буде генеруватися 10-елементний список можливих значень для атрибутів «Країна-партнер» і «Індикатор».

Середній та медіанний час виконання (логарифмічна шкала)

  • Додавання 10-елементного списку можливих значень значно сповільнило запит, що використовує інвертований індекс (GIN). І хоча подібний запит лише з одним значенням для країни-партнера та індикатора вигравав тест, тепер він відстає від усіх інших варіантів індексу. На низькому рівні, PostgreSQL вирішила не використовувати предикати для країни-партнера та індикатора під час сканування індексу. Натомість вона застосувала індекс, щоб знайти все, що відповідає предикату по TimePeriod, а потім вже відфільтрувала результат за країнами та індикаторами. Варто зазначити, що серед операторів, які підтримуються індексом GIN, немає еквівалента IN. Отже, утиліта генерує набір предикатів рівності (оператор @>), об’єднаних за допомогою OR.
  • Цього разу MongoDB демонструє кращу продуктивність, особливо у випадку декількох паралельних запитів.
  • Складений індекс на звичайних стовпцях PostgreSQL трохи швидший, ніж складений індекс на стовпці з JSONB.

Висновки

Очевидно, такого тесту недостатньо, щоб сформулювати повні рекомендації щодо вибору між PostgreSQL і MongoDB. Між цими базами є деякі суттєві відмінності, які слід враховувати.

  • Хоча стовпці JSONB у PostgreSQL дозволяють зберігати документи подібно до MongoDB, ми все ще маємо справу з реляційною базою даних із строгою схемою, хоча й дуже багатофункціональною. З одного боку, це потребує підтримки двох рівнів схеми: строгої схеми бази (яка, в найпростішому випадку, являє собою таблицю з первинним ключем та стовпцем JSONB), і схеми документа JSONB на рівні програми. З іншого боку, це дає більшу гнучкість у моделюванні — ви можете поєднувати окремі стовпці простих типів з документами JSONB в одній таблиці та використовувати JOIN між окремими таблицями.
  • Оновлення даних у PostgreSQL може бути не таким ефективним, як у MongoDB. Через використання моделі MVCC (багатоверсійного керування паралелізмом), коли рядок оновлюється в PostgreSQL, система створює нову версію, а не змінює існуючий рядок. MongoDB, навпаки, може частково оновлювати свої документи, що може бути значною перевагою в сценаріях інтенсивного запису, особливо з великими документами. На жаль, моя утиліта поки що не підтримує тести оновлення, тому в статті довелося їх пропустити.
  • MongoDB дає контролювати момент підтвердження запису, причому підтвердження можна отримувати ще до фіксації даних на диску. Це може бути використано для підвищення продуктивності запису в сценаріях, де гарантії збереження не є критично важливими. У PostgreSQL аналогічної можливості немає.
  • Підтримка транзакцій є більш зрілою в PostgreSQL, оскільки це невід’ємна функція реляційних баз даних. Тим не менш, починаючи з версії 4.0, в MongoDB було багато зроблено в цьому напрямку.
  • Деякі називають шардування та вбудований повнотекстовий пошук перевагами MongoDB порівняно з традиційними реляційними базами даних. Однак PostgreSQL є дуже багатофункціональним та також надає ці можливості. Наприклад, повнотекстовий пошук доступний «з коробки», а шардування може бути реалізовано за допомогою розширення Citus. Тут може бути різниця в рівні гнучкості кожної з реалізацій, але з точки зору функцій я не можу назвати щось таке, що MongoDB може робити, а PostgreSQL — ні.

У наведеній нижче таблиці підсумовано середню тривалість запитів у тестах в один потік разом з тестом вставки в 100 потоках. Я не буду додавати таку ж таблицю для медіани тривалостей, оскільки порівняльна «карта кольорів» залишається майже ідентичною в обох випадках. Очевидно, що немає універсального рішення, тому вибір конкретної технології та методу моделювання повинен базуватися на характеристиках запитів та даних, а також пов’язаних з ними нефункціональних вимогах.

Тут ми бачимо, що для аналогічних індексів у колекції MongoDB та на стовпці типу JSONB в PostgreSQL результати дуже схожі, за винятком сьомого запиту. Це свідчить про те, що JSONB в PostgreSQL можна розглядати як альтернативу колекціям MongoDB. Водночас також важливо враховувати інші фактори та застереження, наведені в цьому розділі.

  • Як було зазначено раніше, ці тести не містять сценаріїв оновлення, які можуть бути вирішальними для багатьох систем. Отже, якщо ваша система припускає високу швидкість одночасних оновлень, додаткове дослідження є необхідним у будь-якому випадку.
  • Тести перевіряли лише випадок, коли дані повністю поміщаються в оперативну пам’ять сервера бази. Якщо об’єм всіх даних, за якими у вашій системі часто роблять запити, є набагато більшим, ніж оперативна пам’ять сервера бази, MongoDB може виявити помітну перевагу над PostgreSQL завдяки більш ефективному дисковому вводу-виводу.
  • Тести оцінювали запити до невеликих записів бази даних. Однак важливо зазначити, що розмір запису та розмір індексованих значень можуть значно вплинути на результати.
  • Хоча запити до JSONB в PostgreSQL демонструють продуктивність, порівняну з MongoDB, я б рекомендував використовувати звичайні стовпці, коли це можливо, звертаючись до JSONB лише тоді, коли це абсолютно необхідно. Звичайні стовпці забезпечують кращу продуктивність та більш компактне зберігання. Так, у вас можуть бути таблиці, що поєднують декілька звичайних стовпців для фіксованих атрибутів із додатковим стовпцем JSONB, який містить решту даних у формі документа.
  • Застосовність індексу GIN для стовпців JSONB в PostgreSQL є досить обмеженою, оскільки він не прискорює сортування та показує слабкі результати при запитах з семантикою оператора IN. Схоже, що GIN справді кращий в одному конкретному випадку — коли запитам не потрібно перевіряти кілька значень для того самого поля і коли в результаті є лише невелика кількість рядків. Виходячи з тестових показників, можна сказати, що до декількох сотень буде прийнятно для систем з високим рівнем паралелізму. Однак під час отримання тисяч рядків — особливо коли їх потрібно відсортувати перед поверненням клієнту — продуктивність запиту помітно знижується.
    Для систем з невеликою кількістю одночасних користувачів, що звертаються до однієї колекції бази даних, індекс GIN може все ще бути корисним навіть при більшій кількості рядків, як це показано на четвертому запиті. Той факт, що ви можете охопити всі можливі комбінації предикатів запиту одним індексом, є дуже привабливим. Але знову ж таки, запити, що повертають сотні тисяч рядків або більше, все одно будуть страждати.
  • Індекс GIN не підтримує оператори IN і LIKE. На практиці IN можна замінити декількома предикатами рівності поєднаними через OR, а ось для LIKE еквівалента немає.
  • Немає сенсу зберігати довгі текстові або бінарні атрибути в документах JSONB, індексованих за допомогою GIN. Оскільки він фактично індексує всі значення в документі, це буде марною тратою ресурсів.
  • MongoDB набагато ефективніше використовує місце на диску завдяки стисненню, яке є невід’ємною частиною механізму WiredTiger. Справедливо було б зазначити, що PostgreSQL теж використовує стиснення, але це стосується лише даних, що зберігаються як TOAST — тип зберігання значень стовпців, розмір яких перевищує фіксований поріг (зазвичай 2 КБ).
  • PostgreSQL набагато швидше виконує повне сканування таблиць, особливо якщо в запиті використовуються звичайні стовпці.
  • MongoDB набагато швидше знаходить унікальні значення, якщо може використати індекс.
  • Незалежно від бази даних, окремі індекси для окремих атрибутів не дуже допомагають, якщо вам потрібно фільтрувати великий набір даних за довільними комбінаціями атрибутів. Однак з великою ймовірністю ви можете знайти комбінацію простих і складених індексів, яка охоплює всі основні сценарії.
  • Хоча будь-які тести аналогічні цьому дають ідеї на тему того, що очікувати і на що звертати увагу, я рекомендую виконувати тести запитів, специфічних до вашого проєкту, перед тим як робити технологічне рішення. Моя утиліта Database Benchmark робить такі тести набагато простішими в порівнянні з написанням тестових програм з нуля.

Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті

👍ПодобаєтьсяСподобалось37
До обраногоВ обраному17
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Навіщо, можна їх разом юзати, наприклад, ви можете використовувати PostgreSQL для зберігання структурованих даних, а MongoDB — для зберігання неструктурованих даних.

Робити, навіть для тесту, один і той самий запит 100 разів — зайве.
Так як зазначено що всі дані вміщуються в меморі — майже напевне після першого запиту всі наступні будуть видаватись з кешу.
Буде можливіcть оцінити кеш хіт для кожної бази?
Також в статті зазначено скільки місця на диску зайняли дані. Чи можна ще окремо зазначити скільки з того зайнято саме даними, а скільки індексами?

Було б добре навести повністю (хоча б під катом) тексти виконуваних запитів для кожного тесту.
З мого досвіду продуктивність монги відчутно падає якщо в запиті повертається великий рекордсет і _є_сортування_

з версії 5.щосьТам в монги з’явилась підтримка джоін через $lookup
Було б цікаво побачити як поводитимуться обидві бази з такими запитами в яких є джойн.

Та й взагалі — є стаття як з Пострісу зробити Монгу. А от як з Монги постгріс — ні.

Не агітую за жодну з двох ДБ — обидві хороші кожна на _своєму_ місті.

Стаття — супер.
Дякую за виконану роботу

youtu.be/...​YATWk?si=bAV-9ziCX6w70nmu

Вот здесь 59:00 он описывает пару моментов. Достаточность памяти, параллельность.

Дуже дякую за цю статтю! Якраз на проекті дійшли в спробах змусити Postgres працювати до JSONB + GIN index
І тепер активно думаємо, що пора в MongoDB
Один із пунктів проти JSONB — це достатньо погана підтримка цього функціоналу ORM провайдерами

Один із пунктів проти JSONB — це достатньо погана підтримка цього функціоналу ORM провайдерами

цікавий недолік — розширити ORM провайдер важче чем перейти на іншу БД?

Все залежить від контексту і задач. І іноді така маленька дрібничка накопичується до мільйона інших і постає логічне питання — а як довго ми будемо робити із реляційної бази даних — документну. І навіщо ми це робимо? Бо можемо?
Можливо варто подивитись на інші опції? Можливо для наших сценаріїв в цілому краще підходить DocumentDB?
Є певні дуже конкретні сценарії, де потрібна реляційна база. І конкретні — де DocumentDB

В моєму дуже конкретному випадку немає жодної переваги в реляційності, лише недоліки. В моєму випадку є ледве не книжний приклад чому DocumentDB працюватиме краще.

Так, ми змогли змусити працювати Postgres with JSONB + GIN Indices + GENERATED ALWAYS STORED
А от про те, яка ціна цього рішення — чудово описано в статті.
І я дуже вдячна автору за аналіз

а як довго ми будемо робити із реляційної бази даних — документну

залежить від:
1. що ви називаєте «документами» у вашому домені
2. чи є, які, скільки зв’язкі(relations) між «документами» вашого домену.

Можливо для наших сценаріїв в цілому краще підходить DocumentDB?

можливо. А можливо й Aerospike. А можливо й ..., ..., ...

В моєму дуже конкретному випадку немає жодної переваги в реляційності, лише недоліки.

Можливо. з фрази —

проти JSONB — це достатньо погана підтримка цього функціоналу ORM провайдерами

цього не зрозуміло :)

Так, ми змогли змусити працювати Postgres with JSONB + GIN Indices + GENERATED ALWAYS STORED

Я бачив продукт де використовується одночасно Postgres та Mongo

І я дуже вдячна автору за аналіз

Так, робота автором зроблена чимала.

ORM это зло! Если уж юзать, то нужно использовать JSONB только для тех данных, которые действительно требуют гибкости JSON.
Для структурированных данных лучше юзать реляционные типы данных PostgreSQL.

Используйте расширения PostgreSQL, такие как btree_gist и jsonb_extra, которые могут улучшить работу с JSONB и GIN-индексами. Избегайте использования вложенных запросов JSONB, так как это может негативно сказаться на производительности. И главное используйте операторы JSONB при написании запросов.
вроде так работает
CREATE INDEX idx_gin_my_jsonb_column ON my_table USING GIN (my_jsonb_column); и все!

Минуси постгрі виглядають як простір для оптимізації. Тому було б цікаво побачити, що змінилось за два роки

Використовувалися PostgreSQL 14.6 і MongoDB 6.0.3. Тести проводилися на початку 2023 року.

На поточний момент вже вийшла друга релізна версія у лінійці актуальних PostgreSQL 16.* , в яких окрім оптимізацій по роботі з памятью та дисковою підсистемою було розширення функціональності по роботі з JSON-даними (наприклад «Allow JSON string parsing to use vector operations»).

Так, як буде час, хочу повторити тести для нових версій цих баз.

1. Якщо є жага замінити MongoBD на Postgres, то або ви неправильно користуєтесь MongoBD або не розумієте для чого створені MongoBD, Elasticsearch, etc.
2. 11 мільйонів рядків — це дуже смішно
3. Стверджуєте що Postgres теж може горизонтально масштабуватися але тести проводите на одній ноді
4. Як вже вказали нижче немає ніяких даних про навантаження серверу у момент тесту
5. Навіщо робити запис у 100 паралельних потоках на одній машині вбиваючи її? В реальності так ніхто не робе. Можна горизонтально масштабувати та отримати більший приріст

або ви неправильно користуєтесь MongoBD або не розумієте для чого створені MongoBD, Elasticsearch, etc.

Сучасні пост-реляційні бази вже вміють багато того, що раніше було тільки у різних NoSQL рішень, і різниця стає не такою очевидною (якщо що, з тим же Еластиком через його природу різниця очевидна до сих пір). Цікаво почути вашу думку про те, для чого саме MongoDB і чому PostgreSQL потенційно не може закрити ці потреби.

11 мільйонів рядків — це дуже смішно

Вже відповідав раніше. Як можна бачити з тестів — в багатьох випадках швидкість вже погана навіть на такому об’ємі. З ростом об’єму вона не покращиться. Чи має сенс порівняти швидкість фільтрації за індексом на терабайті даних між двома базами — звісно так, але це матеріал для майбутніх тестів. Я у висновках наголошую на тому, що тести покривають тільки частину сценаріїв, і не кажу що MongoDB не потрібна. Окрім того, навіть на такому об’ємі можна спростувати деякі міфи, які я чув навіть від досвідчених розробників: «GIN індекс в PostgreSQL набагато швидший за звичайні індекси», «MongoDB завжди більш ефективна для роботи з документами, ніж JSONB в PostgreSQL», «MongoDB швидше виконує агрегації» і т.д.

Стверджуєте що Postgres теж може горизонтально масштабуватися але тести проводите на одній ноді
Навіщо робити запис у 100 паралельних потоках на одній машині вбиваючи її? В реальності так ніхто не робе. Можна горизонтально масштабувати та отримати більший приріст

Щоб порівняти базову ефективність енджинів, цього достатньо. Тестування шардінгу і терабайтів даних — це окремий сценарій, в якому і запити можуть бути інші. Якщо у вас є хороші приклади сценаріїв/запитів, для яких на вашу думку має сенс зробити бенчмарк — поділіться, будь ласка.

Вже відповідав раніше. Як можна бачити з тестів — в багатьох випадках швидкість вже погана навіть на такому об’ємі. З ростом об’єму вона не покращиться. Чи має сенс порівняти швидкість фільтрації за індексом на терабайті даних між двома базами — звісно так, але це матеріал для майбутніх тестів.

Чому ви вирішили, що пошук/сортування ведеться на основі індексів? Ви дивились плани ваших запитів? Для розуміння було б корисно привести їх у аналізу результатів. Більш того, має значення розподіл індексованих даних. Наприклад, є поле, яке має тільки 2 значення, у колекції/таблиці дані поля розподілені 50/50, у такому випадку індексування цього поля не додасть якогось перфоменсу. Я розумію, якщо є бажання погратися з індексами, але навіщо для цього робити бенчмарк? Достатньо буде зручний вам клієнт до БД (mongodb або postgresql) і плани ваших запитів.

P.S. у мене є досвід роботи з колекцією mongodb, розмір якої більше 2 млрд документів, в залежності від результату, пошук виконувався мілісекунди/десятки мілісекунд. Авжеж, були і проблемні пошуки, але це більш стосувалося обробки на стороні клієнта, а не mongodb. Тому і виникає багато запитань до 11 млн документів і результатів вашого бенчмарку.

P.P.S. з моєї точки зору для 11 млн простих документів/рядків не має значення, яку БД використовувати (mongodb, postgresql, mysql ..., JSON/regular tables), вибір буде залежати від кошторису використання і експертизи команди підтримки. Але для більш великих даних, я б вибрав щось надійне і перевірене — mongodb shard cluster, а не postgres citus.

Чому ви вирішили, що пошук/сортування ведеться на основі індексів? Ви дивились плани ваших запитів?

Я не стверджую що у всіх сценаріях використовувалися індекси. Так, в кожному з випадків я дивився плани виконання. І далеко не у всіх використовувалися індекси, про що я писав в коментарях для кожного запиту — в яких випадках повний скан, в яких у постреса і монги різні за природою плани і т.д. Мій коментар про «Чи має сенс порівняти швидкість фільтрації за індексом на терабайті даних» — саме про те що в ньому написано, без якогось додаткового контексту. Тестити фуллскан на терабайті — не впевнений що має сенс.

Наприклад, є поле, яке має тільки 2 значення, у колекції/таблиці дані поля розподілені 50/50, у такому випадку індексування цього поля не додасть якогось перфоменсу.

Це очевидно, але все одно не розумію до чого цей коментар.

Я розумію, якщо є бажання погратися з індексами, але навіщо для цього робити бенчмарк?

Різні реалізації індексів в різних базах даних можуть мати доволі неоднакову швидкість навіть на тих самих даних. Тому і цікаво подивитись яка різниця в швидкості двох баз при схожих планах виконання. Подивіться, наприклад, запити 1-3 і 8 із статті — плани для композитних індексів там еквівалентні, а різниця в часі є, хоч все одно мова йде про одиниці мілісекунд. А ось коли даних буде набагато більше ніж оперативи, то різниця імплементацій виявить себе ще сильніше. Хто переможе — ще не знаю.

Інша ситуація — окремі індекси на двох полях. Припустимо я отримав плани і бачу що MongoDB йде по одному з індексів, а PostgreSQL виконує bitmap scan по обом. Звідки я знаю що буде працювати швидше?

P.S. у мене є досвід роботи з колекцією mongodb, розмір якої більше 2 млрд документів, в залежності від результату, пошук виконувався мілісекунди/десятки мілісекунд.

Можете поділитися інформацією про запити? Це може бути корисно для моїх подальших бенчмарків. Чи багато критеріїв, які оператори, чи є агрегації, чи є фільтрація по вкладених масивах, які індекси, приблизний розмір одного документа, і скільки документів в середньому повертає один запит.

з моєї точки зору для 11 млн простих документів/рядків не має значення, яку БД використовувати (mongodb, postgresql, mysql ..., JSON/regular tables), вибір буде залежати від кошторису використання і експертизи команди підтримки.

Тут можна подискутувати на тему того, що факторів існує більше, але основний висновок з цієї статті — «JSONB в PostgreSQL можна розглядати як альтернативу колекціям MongoDB». Я не кажу що MongoDB не потрібна. Але зустрічав упередженість на тему того, що PostgreSQL в будь-якому випадку буде програвати при роботі з JSONB, тому що MongoDB вже багато років оптимізується під документні сценарії, а в PostgreSQL це якийсь «едж кейс». В цій статті можна побачити що при таких сценаріях і даних це не так. Чи треба проводити подальше тестування на більших даних та інших сценаріях — звісно так, про що я теж пишу.

Окрім того, навіть на такому об’ємі можна спростувати деякі міфи, які я чув навіть від досвідчених розробників: «GIN індекс в PostgreSQL набагато швидший за звичайні індекси», «MongoDB завжди більш ефективна для роботи з документами, ніж JSONB в PostgreSQL», «MongoDB швидше виконує агрегації» і т.д.

Ви робите узагальнені висновки на дуже простій колекції/таблиці. Чому ви не розглядаєте роботу/пошук з вкладеними документами, масивами документів і т.п.? Якщо використовувати плоскі документи, то навіщо вам JSON?

Два дуже простих базових сценарія, в яких запити зі статті будуть релевантними:
— Об’єкти з динамічною структурою.
— Вложеність і масиви є, але пошук і фільтрація йде по простим полям.

Другий кейс може дати додаткове навантаження на додавання та оновлення даних.

Я згоден з тим, що пошук по елементам масиву, або пошук по атрибутам об’єктів з масиву — це важливий сценарій для документних баз і його дійсно не вистачає. В PostgreSQL подібні запити реалізуються тільки через GIN індекс, який далеко не в усіх випадках добре працює. Я планую додати ще один пункт з застереженнями в статтю, і додати масиви в майбутні бенчмарки.

Стаття гарна, але є наступні питання до тестів:

* Не зрозуміло, яка була структура даних з індексами, наприклад, SalesTable.json для mongodb можно було б замінити або описати повноцінну схему валідації з індексами. Теж саме можно було б зробити/описати для SQL DBs.

* По індексам не розглянути різні цікаві кейси:
* індекс по полям з масиву
* індекс з NULL значеннями
* індекс з відсутніми полями (для mongodb можна використовувати sparse індекс)

* Не зрозуміло, які використовувалися вибірки. Наприклад, для префоменсу mongodb не має сенсу використовувати декілька роздільних індексів у одному запиті. Тобто не зрозуміло (або зрозуміло), чому така різниця у запитах MongoDB Composite Idx і MongoDB Separate Idxs.

Дивно, але окремі індекси в PostgreSQL працюють набагато гірше, ніж окремі індекси в MongoDB, незважаючи на згадану раніше оптимізацію сканування індексу (bitmap scan). MongoDB використовує індекс TimePeriod для сканування всіх відповідних записів, які вже розміщені в правильному порядку, а потім фільтрує кожен відсканований рядок за країною, доки не буде знайдено достатньо результатів для сторінки.

Результат mongodb може бути значно гірше, все залежить від об’єму даних індексу.

* Як я зрозумів, у деяких запитів використовується сортування. У mongodb я б не використовував фільтрацію і сортування окремими індексами. А якщо у вас невеликий результат вибірки, я б зовсім відмовився від сортування на стороні mongodb.

* Із статті не зрозуміло, як вибираються данні на клієнтської стороні. Наприклад, mongodb використовує курсор з fetch параметром. Якщо у вас велика вибірка, то вам потрібно вичитати усі данні (буде декілька запитів до mongodb).

* У даному аналізі були приведені результати запитів з сторони dotnet клієнта. Це цікаво, але хотілось б зрозуміти, а що там на стороні DB — CPU, Memory, IOPS, час виконання запитів на стороні DB. Тобто, якщо вибирати DB, то хотілось би більше результатів з DB, а не клієнта (скільки там часу займає серіалізація/десеріалізаця, використовується серіалізація/десеріалізаця драйвера/провайдера або щось своє, скільки часу пішло на GC т.п.).

* 11 мільйонів рядків — це не зрозумілий об’єм даних, хотілось б розмір на диску. Більш того, я не бачу сенсу використовувати mongodb для такого об’єму даних. Хотілось б подивитись, як будуть працювати mongodb vs postgresql хоча б для 500 млн рядків.

* Не зовсім зрозуміло, чи враховують JIT компіляцію результати бенчмарку (я не знаю, які є оптимізації зараз у дотнеті, але для джави це має значення). Не зрозуміло, чому було вибрано 1000 запитів на 1 поток, а не, наприклад 10000 і як це вплине не результати.

Мій висновок — для вибору DB немає сенсу використовувати загальні тести з мікробенчмарку, тому що багато чого залежить від структури даних, об’єму, сценаріїв використання, інструментів експлуатації, подальшого розвитку системи. Тому кожна команда/компанія повинна проводити свій аналіз, свої внутрішні тести, включно з довгостроковим навантаженням (наприклад, для шард кластеру mongodb операція «move chunk» може бути болячою у час пік навантаження).

Дякую за розгорнутий відгук, але в мене є що прокоментувати і з чим не погодитись :)
*

Не зрозуміло, яка була структура даних з індексами

— в останньому пункті умов є посилання на github.com/...​ree/main/samples/Eurostat, де є всі сорси бенчмарку і дефініції індексів
*

По індексам не розглянути різні цікаві кейси

— було б цікаво, але обсяг статті і так немаленький :)
*

Не зрозуміло, які використовувалися вибірки. Наприклад, для префоменсу mongodb не має сенсу використовувати декілька роздільних індексів у одному запиті

— все описано в умовах і в кожному підрозділі. З приводу «не має сенсу» — в тому і ідея що треба подивитися не тільки на ті запити, які зручні базі даних, але й на ті, в яких їй буде важко. На практиці може бути не дуже легко покрити всі юзкейси індексами, тому треба дивитися які є компромісні варіанти і як добре вони працюють.
*

Як я зрозумів, у деяких запитів використовується сортування.

— так, це явно написано в кожному відповідномі підрозділі, наприклад «Знайти перші 100 спостережень упорядкованих за спаданням періоду для конкретних індикатора та діапазону періодів». І коли у вас в базі навіть мільйон документів, а треба взяти першу сторінку, то відмовитися від сортування на стороні Монги нереально.
*

Із статті не зрозуміло, як вибираються данні на клієнтської стороні.

- розмір порції за замовченням клієнтської бібліотеки — 101 документ.
*

У даному аналізі були приведені результати запитів з сторони dotnet клієнта. Це цікаво, але хотілось б зрозуміти, а що там на стороні DB — CPU, Memory, IOPS

— згоден, але поки мій інструментарій не настільки розвинений, планую в наступних бенчмарках це покращити.
*

11 мільйонів рядків — це не зрозумілий об’єм даних, хотілось б розмір на диску.

— все є в розділі про вставку даних ;)
*

Більш того, я не бачу сенсу використовувати mongodb для такого об’єму даних.

- тим не менш величезна кількість проєктів використовує монгу і на менших об’ємах. Терабайти даних — звісно цікаво, але це інші сценарії, з якими стикається менший відсоток проєктів, і скоріш за все має інші типи запитів. Можливо в майбутньому і на цю тему щось зроблю.
*

Не зовсім зрозуміло, чи враховують JIT компіляцію результати бенчмарку

— ефект від джиту нівелюється на warm-up етапі бенчмарку.
*

Мій висновок — для вибору DB немає сенсу використовувати загальні тести з мікробенчмарку

— саме про це я у висновках і пишу. Але щонайменше відразу можна побачити, які типи запитів мають шанс на успіх на тій чи іншій платформі бази, а які — ні. Запит який на такому об’ємі даних вже виконується секунди — точно вже не полетить, якщо в вас 5 ТБ даних і 25 шардів :)

Деякі називають шардування та вбудований повнотекстовий пошук перевагами MongoDB порівняно з традиційними реляційними базами даних...
... шардування може бути реалізовано за допомогою розширення Citus

Питання до автора — скількома терабайт даними ви оперували у вашому тестовому шард кластері? У вас були довгострокові навантажувальні тести шард кластера?

Масштабна стаття!
Дякую!

Це стаття для тих хто намагається себе переконати не використовувати монгу як rdbms, і це добре.
Бо як знизу написали про «буханку і тролейбус», багато хто юзає її не по призначенню, й звідси схвальні «о тепер монга не потрібна!».
Але суть в тому, що це документо-орієнтована nosql і створена вона в першу чергу для цього.
Хочете структурованих даних з хорошим перформансом — використовуйте реляційні sql like бази.
Хочете проаналізувати якийсь великий заархівований датасет json like даних де структура іде в різнобій(тіж дампи реддіта чи щось типу того) монга офігенна, в стрімі пишеш в базу пакетами з мінімальними затримками, а потім можна агрегаціями нормалізувати дані і залізо не помирає прожовуючи то все. Для аналізу, деяких кейсів ml\ai доволі зручний інструмент з приємним синтаксисом. Накидати прототип проекту і не морочитись з визначенням структури даних теж топ.
Коротше до чого я веду, до банального «для кожного кейсу свій інструмент», а те що монга підтримує фішкі з sql-like баз скоріше всього приємний бонус, ніж кіллер фіча.
В будь-якому випадку дякую за розгорнуту статтю з тестами, наступного разу спробую використати Postgre на якомусь великому датасеті і подивлюсь що з цього вийде.

та якби зовсім навпаки — коли з’явилась підтримка jsonb, з’явилась можливість використовувати постгре замість монги.

Звісно монга більш заточена й має свою базу вже користувачів і шанувальників. Але по суті стаття демонструє що постгре перформить на рівні плюс/мінус. З часом розрив лише зменшуватиметься по ходу розвитку постгре.
Як на мене якби в мене був вибір підтримувати/тюнити дві технології чи одну, то звісно одну. Хоч база скоріш за все буде все одно окрема в реальному продакшені

Якщо не зважати на інший синтаксис, який взагалі аргумент вибирати монгу на свіжий проект?

залізо не помирає прожовуючи то все

Фор хум хау, то вже залежить від багато чого

в стрімі пишеш в базу пакетами з мінімальними затримками

Так в тому й цимес топіка й питання топіка, бо свого часу psql/jsonb по перформансу положив на лопатки монгу вдвічі чи щось таке, влом шукати. Якби шороху не було, то ніхто б в сторону постгре не дивився для цього кейса. З того часу води утекло правда і монга звісно на місці теж не стоїть

Бо як знизу написали про «буханку і тролейбус», багато хто юзає її не по призначенню, й звідси схвальні "о тепер монга не потрібна!".Але суть в тому, що це документо-орієнтована nosql і створена вона в першу чергу для цього.

Ну добре нехай так і є і використовують її не за призначенням але власне тоді в чому її призначення який з неї зиск в чому полягає профіт від використання? Тобто раніше можна було сказати от монга це круто тому що можна не морочитись зі структурою і мати велику гнучкість. Типовий кейс це різні товари в магазині, наче у всіх є ціна і кількість, але ще купа додаткових кастомних атрибутів які в реляціїній базі складнувато описувати динамічно. Проте цю проблему вирішили і я реально не знаю яку нішу тепер може займати монго і для чого її обирати на новий проект. З одного боку редіс, а з іншого реляційні бази і от де ця ніша для монго зараз я особисто не дуже розумію.

де ця ніша для монго зараз я особисто не дуже розумію

Нібито для задач з інтенсивним записом та оновленням даних в БД монга все ще може бути кращим вибором, бо в постгре версіонування рядків та необхідність дуже коректно налаштовувати vacuum.

задач з інтенсивним записом та оновленням даних в БД

oracle oltp?

Нажаль, з Оракл ніколи не мав справ від слова «зовсім», тому нічого не можу сказати з цього приводу.

Та воно все oltp, але реалізовано по різному. Для апдейтів Oracle/MySQL краще за Postgres. По іншому на фізичному рівні реалізовано зберігання данних для MVCC, працює швидше, не роздуваються данні, та не треба плясок с vacuum, в 99.9% випадків воно просто працює і не треба з цим паритися. В 14/15 версіях постгре вони там оптимізували, але не знаю чи вирішили проблему щоб автовакуум все підчищав і не треба було за ним дивитись коли за добу 100M+ апдейтів.

Нібито для задач з інтенсивним записом та оновленням даних в БД монга все ще може бути кращим вибором

Це скоріше не про реляційні чи не реляційні бази, а про те як власне під капотом база працює з даними. До речі не завадили б якісь критерії що таке інтенсивний запис і оновлення, а також розмір бази.

Вот так с помощью нехитрых приспособлений буханку белого (или черного) хлеба можно превратить в троллейбус. Но зачем?

Оновлення даних у PostgreSQL може бути не таким ефективним, як у MongoDB. Через використання моделі MVCC (багатоверсійного керування паралелізмом), коли рядок оновлюється в PostgreSQL, система створює нову версію, а не змінює існуючий рядок. MongoDB, навпаки, може частково оновлювати свої документи

WiredTiger також використовує MVCC. Тобто якщо є якісь переваги монги в тестах, це не через MVCC як таке, а максимум через різницю в реалізації (тобто, цей пункт варто трохи підкоригувати якось).

MongoDB дає контролювати момент підтвердження запису, причому підтвердження можна отримувати ще до фіксації даних на диску. Це може бути використано для підвищення продуктивності запису в сценаріях, де гарантії збереження не є критично важливими

Тут чесно кажучи трохи не зрозумів — а яка практична користь від підтвердження запису в сценаріях, де гарантії збереження неважливі?

практична користь від підтвердження запису в сценаріях, де гарантії збереження неважливі

Та ніякої користі, та й база не потрібна тоді, може дані просто в зберігати /dev/null, а віддавати рандом з чатгпт на льоту

Цікавий кейс, про який ви думаєте, бо у мене в голові лише кейс TSDB чи подібних серій значень де втрата одного семпла некритична. Ну а це якби слабоватенький юзкейс для монги — є набагато кращі варіанти

Дякую, подумаю як краще переформулювати про можливю різницу в швидкості оновлення даних та MVCC.

Щодо підтвердження запису — я розумію ваше питання не про те в яких саме сценаріях гарантії непотрібні, а про те який сенс мати підтвердження від бази, якщо гарантії не потрібні. Можна в теорії зробити fire-and-forget і асинхронно відправляти дані в базу. Воно в цілому так, але ймовірність втратити з’єднання з базою через мережеві проблеми зазвичай вище ніж ймовірність втратити дані через падіння серверу бази даних, в якому ваш апдейт не був ще зафіксований на диск, але вже був підтверджений. Тобто формально в другому випадку повної гарантії durability немає, але ймовірність втрати досить мала. Чесно скажу, кейс досить теоретичний, але ймовірність застосування існує :)

Шикарний огляд

Собсно проблемне питання не з бухти барахти піднялося, а коли постгре імплементував jsonb

Для psql це не пріорітетний юзкейс, а в монго більше простих інструментів для роботи з їх пріорітетним юзкейсом

Загалом вибір те чи інше буде визначатись іншими факторами, а не маргінальною різницею, наприклад оверхед підтримки

Шановний пане @Yuriy Ivon
спойлерне питання:

Чи може PostgreSQL з його JSONB замінити MongoDB

то може чи ні?

У висновках все написано —

Це свідчить про те, що JSONB в PostgreSQL можна розглядати як альтернативу колекціям MongoDB. Водночас також важливо враховувати інші фактори та застереження, наведені в цьому розділі.

может может а может нет всё не так однозначно ))

Дякую за статтю. Для себе теж не маю розуміння де той юзкейс, коли монга є найкращим вибором. Наче всі дженерал кейси постгре вже підтримує. Так, є якісь специфічно запити де монга перемагає. Але 90% кейсів є доволі загальним і важко передбачити всі типи кверів щоб порахувати що в середньому монга буле швидше.

Підтримка транзакцій є більш зрілою в PostgreSQL, оскільки це невід’ємна функція реляційних баз даних.

Звучит конечно своеобразно это утверждение.

По поводу LIKE postgres поддерживает regexps for JSON data and you can use gin_trgm_ops index for search queries.

Статья хорошая, спасибо. Убедился, что MongoDB не нужен

Дякую, подивлюсь як gin_trgm_ops буде працювати в поєднанні з jsob_path_ops, бо якщо потрібні і базові умовні оператори і можливості триграм — це буде два окремі індекси.

Убедился, что MongoDB не нужен

ЯСНА

you can use gin_trgm_ops index for search queries.

Вибачаюся за «політоту», втім, нібито той модуль пошуку по trigrams для постгресу робила руcня — для когось це саме по собі може стати showstopper (для нас, наприклад, стало)

Надо учесть что не только лишь

trigrams

а вообще SQL/JSONB поддержку и GIN, с другими разработчиками конечно, но core contributors в JSON индексы — русня.

Так что рекомендую вообще не использовать JSONB в Postgres в вашем случае

medium.com/...​am/excellent-65c87fcc8114 — цей коментар вже все непогано написав.

Тестувати PostgreSQL для роботи в шаблонах доступу та моделі даних MongoDB, і при цьому MongoDB все одно не змогла показати успіх у своїй роботі (в десятки і сотні разів), тобто в інших шаблонах роботи в неї (напевно) все ще гірше )

MongoDB, навпаки, може частково оновлювати свої документи, що може бути значною перевагою в сценаріях інтенсивного запису, особливо з великими документами.

Я не був би надто оптимістичним. Нам вдавалося практично вбити MongoDB (точніше її клона DocumentDB) масовими апдейтами (ми заливали в базу кілька терабайт даних, причому залежних одні від одних, тому крім інсертів відбувалась достатня кількість апдейтів) — перформанс суттєво падав (причому на читання також, а читали ми з secondary read інстансів), завантаження процесора виростало до 98% на write (primary) documentdb інстансі, амазоновський саппорт сказав що такими темпами кластер за дві доби помре повністю, бо DocumentDB garbage collector не справляється.
Вертикальний скейлінг (ми скейлили до db.r6g.4xlarge (16CPU/128Gb), в принципі можна було ще, але ефект був не надто помітний — завантаження cpu все ще ~80%) допоміг мало, прийшлося обмежувати кількість інстансів мікросервісу (і кількість потоків в ньому) який писав в базу.
P.S.
Можливо, чиста MongoDB на останніх версіях поводить себе краще, хз.

прийшлося обмежувати кількість інстансів мікросервісу (і кількість потоків в ньому) який писав в базу.

хіба це не протирічить самій ідеї мікросервісів як автономного об’єкту з власною бд включно?

... питаю без «підколу» бо саме вивчаю що воно таке «мікросервіси» порівняно з «класичним soa»

прямо не протирічить: у нас є «ingest» мікросервіс основне завдання якого додавати дані в базу (насправді трохи складніше, там парсінг того що приходить в S3 bucket і додавання його одразу в дві бази (DocumentDB/OpenSearch). І ти просто скейлиш кількість «ingest» інстансів. Але опосередковано, можна сказати що протирічить — інші мікросервіси з цих баз читають.
Чи є цей мікросервіс «канонічним» мікросервісом — хз, мене не надто хвилюють чиїсь уявлення «як правильно», в т.ч. «по базі на мікросервіс».

Всё-таки вы сами породили проблему, не разбив набор данных и не использовав sharding

це не постійне навантаження — умовно в тебе S3 bucket в якому лежить кілька терабайт даних, тобі їх треба залити (з трансформацією) в базу, одноразово. Дані постійно додаються в цей бакет, але малими порціями, тому після початкового процесу заливки кількість інстансів «ingest» мікросервісу і навіть інстанс DocumentDB можна даунгрейднути — такого навантаження по вставці/апдейтам більше не буде.
тому ускладнювати систему шардінгом/розбивкою даних не було сенсу.

т.е. конкретный кейз это видимо ещё и «совсем конкретный кейз» а не какое-либо обобщение «постгре равен или лучше хуже монго вообще»

так, звичайно. я взагалі до результатів бенчмарків відношуся з певною пересторогою: переважно пізніше приходить хтось і каже що «вы просто не умеете их готовить», отут треба було параметри бази змінити, отут квері оптимізувати і т.д.

Нам вдавалося практично вбити MongoDB (точніше її клона DocumentDB)

Це лише умовно клон швидкість цих сервісів може сутєво відрізнятися від бази якщо скажімо її розвернути навіть у віртуалці. Якось був дуже здивований коли запити які я звик вважати дуже швидкими цих сервісах виконувалися на порядки довше.

так, клон умовний (движок інший, не всі можливості навіть MongoDB 4.0 підтримуються), потестити його локально не вийде бо DocumentDB docker image немає, є тільки cloud версія,

Тоді це не клон, я скоріш якась своя розробка з сумісним АПІ. Висновки, що я робив в статті, можуть бути зовсім нерелевантними для цієї технології. Нажаль, я не знаю деталей DocumentDB в AWS, але я багато працював з Cosmos DB, яка в тому числі дає і MongoDB API. І тут я можу точно сказати, що це не клон, а повністю окрема розробка. Незважаючи на деякі цікаві особливості, Cosmos DB мене засмутив в плані ціни, продуктивності, та застосовності для багатьох сценаріїв. Я раніше публікував статтю на цю тему — dou.ua/forums/topic/39281

> Використовувалися PostgreSQL 14.6 і MongoDB 6.0.3. Тести проводилися на початку 2023 року, коли ці версії були останніми.
> October 13, 2022 — PostgreSQL 15 released (improves on its in-memory and on-disk sorting algorithms, with benchmarks showing speedups of 25% — 400% based on which data types are sorted)

No comments

14.6 була останньою в лінійці 14 на той час. 15 лінійці не було ще і півроку і вона давала дещо гірші результати на GIN індексі ніж 14, тому я взяв 14 як більш стабільну і перевірену. Згоден з тим, що моє твердження в статті не зовсім чесне, тому підправлю його. Можна буде зробити ще один бенчмарк де на тих самих сценаріях порівняти 14 і 15 і подивитися чи дійсно анонсовані покращення працюють.

А при чому тут «лінійки» до останньої версії бази?

Останні версії були: PostgreSQL 15.1 — 2022-11-10 (якраз тоді вийшов 14.6 з патчами), PostgreSQL 15.2 — 2023-02-09.

15 лінійці не було ще і півроку

і на що це впливає? PostgreSQL випускає не Bethesda, щоб треба чекати працюючої версії по пів року

вона давала дещо гірші результати на GIN індексі ніж 14

покажіть ці цифри

На момент рану тестів була тільки 15.1. Результати пошукаю. В будь-якому випадку минув майже рік, має сенс повторити тести на свіжих версіях.

Підписатись на коментарі