Чи може 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-елементного списку можливих значень значно сповільнило запит, що використовує інвертований індекс (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 робить такі тести набагато простішими в порівнянні з написанням тестових програм з нуля.
59 коментарів
Додати коментар Підписатись на коментаріВідписатись від коментарів