- PVSM.RU - https://www.pvsm.ru -

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

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

HStore

Если исключить XML [1], Hstore был первым по-настоящему неструктурированным типом данных, добавленным в Postgres. Hstore был добавлен достаточно давно в Postgres 8.3 до upsert [2], до потоковой репликации [3], и до оконных функций [4]. 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, конечно, немного преувеличенна [5]. Об этом ниже.)

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

Кроме того, с течением времени Postgres приобрёл ряд довольно полезных функций [6]. Должны ли вы использовать 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 [7] это может быть довольно удобным.

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

  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

Источник [8]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/postgresql/164040

Ссылки в тексте:

[1] XML: https://www.postgresql.org/docs/9.5/static/datatype-xml.html

[2] upsert: http://www.craigkerstiens.com/2015/05/08/upsert-lands-in-postgres-9.5/

[3] потоковой репликации: https://wiki.postgresql.org/wiki/Streaming_Replication

[4] оконных функций: http://postgresguide.com/tips/window.html

[5] немного преувеличенна: http://clarkdave.net/2013/06/what-can-you-do-with-postgresql-and-json/

[6] функций: https://www.postgresql.org/docs/9.3/static/functions-json.html

[7] MassiveJS: http://www.craigkerstiens.com/2015/12/08/massive-json/

[8] Источник: https://habrahabr.ru/post/306602/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best