Когда использовать неструктурированные типы данных в PostgresSQL? Сравнение Hstore vs. JSON vs. JSONB

в 7:18, , рубрики: json, nosql, postgresql

С тех пор как Postgres начал поддерживать NoSQL (посредством HStore, JSON и JSONB), вопрос о том, когда использовать Postgres в реляционном режиме, а в каких в режиме NoSQL, стал подниматься достаточно часто. Получится ли у вас полностью отказаться от традиционных структур таблиц и работать с представлениями документов в будущем? Смешивать ли оба подхода? Ответ на этот вопрос не удивителен — все зависит от многих факторов. Каждая новая модель хранения данных включая Hstore, JSON и JSONB имеет свои идеальные варианты применения. Тут мы копнём глубже и узнаем об особенностях каждой из них и посмотрим когда что использовать

HStore

Если исключить XML, Hstore был первым по-настоящему неструктурированным типом данных, добавленным в Postgres. Hstore был добавлен достаточно давно в Postgres 8.3 до upsert, до потоковой репликации, и до оконных функций. Hstore это по существу хранилище ключ/значение непосредственно в Postgres. Используя Hstore вы ограничены в выборе используемого типа данных. По сути у вас есть только строки. У вас даже нет вложенности данных; Короче говоря, это одноуровневый тип данных ключ/значение.

Достоинством Hstore является то, что вам не нужно определять ключи (в отличии от столбцов) заранее. Вы можете просто вставить запись, и она будет хранить все необходимые данные. Скажем, у вас есть пример скрипта на создание таблицы:

CREATE TABLE products (
    id serial PRIMARY KEY,
  name varchar,
  attributes hstore
);

С помощью Hstore вы можете вставить все, что вы хотите в колонку атрибутов. В этом случае запрос на добавление этих ключей и значений будет выглядеть следующимобразом:

INSERT INTO products (name, attributes) VALUES (
'Geek Love: A Novel',
'author    => "Katherine Dunn",
    pages     => 368,
    category  => fiction'
);

Запрос на выборку будет иметь вид:

SELECT name, attributes->'author' as author
FROM products
WHERE attributes->'category' = 'fiction'

Очевидным преимуществом такого подхода является гибкость, но вот где он действительно проявляет себя полностью, так это возможностью использовать различные типы индексов. В частности, GIN или GiST индекс будет индексировать каждый ключ и значение в пределах Hstore. Т.е., при фильтрации будет использован добавленный индекс, в случае если этого потребует планировщик Postgres.

Поскольку Hstore не является полным эквивалентом документа, важно понять выгодно ли использовать его в качестве такового.
Если у вас есть реляционные данные и также некоторые данные, которые не всегда могут существовать в колонке, то такой подход может статьотличным решением. Например, в большинстве случаев атрибуты каталогов продукции могут быть прекрасным примером для такоготипа данных. Тогда, для некоторых продуктов, таких как книги (которые вы храните в отдельной таблице “Products”) могут быть определены такие параметры, как жанр, год издания. В другом случае для продуктов, таких как одежда, которую вы тоже храните в этой же таблице, могут быть определены уже другие параметры — размер и цвет. Добавлять же столбец в таблицу продуктов для каждого возможного параметра избыточно и неоправданно.

JSON

Начиная с версии 9.2 в Postgres реализована поддержка JSON. Теперь, Postgres может составить конкуренцию MongoDB. (Хотя функциональность JSON в Postgres 9.2, конечно, немного преувеличенна. Об этом ниже.)

Тип данных в формате JSON в Postgres, если разобраться в значительной степени просто текстовое поле. Всё что вы получите с типом данных JSON так это валидацию значения при вставке. Postgres обеспечивает соблюдение формата JSON. Одним небольшим потенциальным преимуществом над JSONB (который мы рассмотрим следующим) является то, что JSON сохраняет отступы в данных, поступающих в БД. Так что если вы очень требовательны к форматированию ваших данных или вам необходимо сохранить запись в той или иной структуре, JSON может оказаться полезным.

Кроме того, с течением времени Postgres приобрёл ряд довольно полезных функций. Должны ли вы использовать JSON? Ведь, Postgres-ный тип JSON просто предоставляет проверку на текстовом поле. Если вы храните некоторую форму данных журнала, которую редко запрашиваете, тип данных JSON в этом случае работает хорошо. Так как JSON довольно прост, то он будет иметь гораздо более высокую пропускную способность при вставке. Для чего-либо более сложного, я бы рекомендовал использовать JSONB, который будет описан ниже.

JSONB

Наконец, в Postgres 9.4 мы получили настоящую и правильную поддержку JSON в виде JSONB. B означает “лучше” (Better). JSONB — это бинарное представление данных в формата JSON. Это означает, что данные сжимается и более эффективны для хранения, чем обычный текст. Кроме того, под капотом у него механизм, подобный Hstore. Технически, когда-то при разработке, был почти реализованный тип Hstore2 и отдельный тип JSON и впоследствии они были объединены в JSONB в том виде, как он есть сейчас.

Тип JSONB представляет в значительной степени то, что вы могли бы ожидать от типа данных JSON. Он позволяет реализовывать вложенные структуры, использовать основные типы данных, а также имеет ряд встроенных функций для работы с ним. Лучшей частью такой схожести с Hstore является индексация. Создание индекса GIN на колонке JSONB создаст индекс по каждому ключу и значению в пределах этого документа JSON. Возможность индексации и вложенность данных внутри документа означают, что JSONB превосходит Hstore в большинстве случаев.

Хотя все еще остаётся небольшой вопрос о том, в каких случаях следует использовать исключительно JSONB. Допустим, вы создаете базу данных документов и из всех вариантов выбираете Postgres. С пакетом, наподобие MassiveJS это может быть довольно удобным.

Наиболее распространенные примеры использования:

  1. Отслеживание событий данных, добавляя изменяющийся payload события.
  2. Хранение игровых данные достаточно распространено, особенно там, где у вас есть одиночная игра и изменяющаяся схема данных на основе состояния пользователя.
  3. Инструменты, которые объединяют несколько источников данных, пример здесь может быть инструментом, который интегрирует несколько баз данных клиентов к Salesforce, к Zendesk или к чему-то еще. Сочетание схем делает это более болезненной процедурой, чем она должна быть.

Давайте рассмотрим, другой пример работы с JSONB. Скрипт создает таблицу и вставляет некоторые данные для примера:

CREATE TABLE integrations (id UUID, data JSONB);

INSERT INTO integrations VALUES (
    uuid_generate_v4(),
    '{
        "service": "salesforce",
        "id": "AC347D212341XR",
        "email": "craig@citusdata.com",
        "occurred_at": "8/14/16 11:00:00",
        "added": {
            "lead_score": 50
        },
        "updated": {
            "updated_at": "8/14/16 11:00:00"
        }
   }');

INSERT INTO integrations (
  uuid_generate_v4 (),
  '{
    "service": "zendesk",
    "email": "craig@citusdata.com",
    "occurred_at": "8/14/16 10:50:00",
    "ticket_opened": {
        "ticket_id": 1234,
        "ticket_priority": "high"
     }
   }');

В приведенном выше случае, можно легко найти все события, которые произошли c пользователем с email craig@citusdata.com, а затем делать какие-то действия. Например, можно провести какую-либо форму поведенческой аналитики, и вычислить пользователей которые сделали foo а затем bar, или сделать простой отчет.
Добавив индекс Gin все данные в пределах моего JSONB поля проиндексируются автоматически:

CREATE INDEX idx_integrations_data ON integrations USING gin(data);

Заключение

В большинстве случаев JSONB это, вероятно, как раз то, что вы ищите, когда планируете использовать нереляционый тип данных. Для Hstore и JSON можно также найти хорошее применение хоть и в более редких случаях. JSONB не всегда вписывается в модель данных. В случае если вы можете нормализовать схему, то у вас будет преимущество, но если в схеме, большое количество опциональных столбцов (например, с данными о событиях) или одна схема сильно отличается от другой, то JSONB подходит гораздо лучше.

Резумируя, алгоритм выбора решения:

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

Автор: Vovaka

Источник

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


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