Comments 40
Вот только есть одно НО: факт в том, что 99.9% проектов ни когда в жизни не достигнут таких объемов данных как в выборке у автора (смешные 10млн) и им куда важнее не скорость выолнения запроса, а скорость разработки этого самого проекта. И по классике жанра, все что тормозит — кладется в кэш и спят спокойно)
Легко доходят до таких объемов. И прикол тут в том, что параметров всего 10. jsonb получился выгоднее. Ну и синдексами, кмк, автор слегка намудрил. Как пример "легко" - картографические данные в стиле OSM, где вся семантика хранится в тегах (key-value), и совокупное количество как тегов так и значений не ограничено, и каждый "сам себе картограф" способен нагенерить их "мама не горюй". Десяток тегов - маловато.. Да и для интернет-магазинчика .. скажем "собери себе ноут сам" .. там номенклатура одних только запчастей перевалит за 10тыс легко, а каждая железяка имеет 10-20-50 параметров только по разновидностям интерфейсов и разъемов со "стандартами" (DDR, DDR2,..).
Если сущность имеют 2 атрибуты, то нужно уже 4 join-на!Не нужно миллион джойнов, нужен один PIVOT (он в постгресе называется «crosstab» и работает вполне шустро).
Кроме того, все атрибуты обычно хранятся в виде строк, что приводит к приведению типов, как для результата, так и для условия WHERE.Так заведите отдельные таблицы или столбцы под каждый тип.
Если вы пишете много запросов, то это достаточно расточительно, с точки зрения использования ресурсов.EAV — это в принципе расточительно…
Не нужно миллион джойнов, нужен один PIVOT
Я всегда думал, что нужен один select, в котором в условии where будет id сущности. А джойнов надо всегда два, просто чтобы присоединить к таблице с сущностями таблицы с ключами и значениями. Хотя, честно говоря я не совсем понимаю, почему нельзя это всё сложить в одну таблицу.
Так єтот паттерн применяєтся при динамечских обьектах, где динамически создаются custom fields
, типа как в каких-то CRM сервисах.
Для систем, написанного под одного конкретного клиента — конечно особого смьісла так делать нет, бьістрее и проще все в одной таблице по схеме "1 аттрибут обьекта — 1 колонка в таблице"
EAV, в отличие от JSON, это не полный schem-less, это sheme-as-data. Там можно иметь дополнительные метаданные у полей, проводить некоторые манипуляции со структурой Entity, не перетряхивая все значения и иметь где-то фиксированную доступную для просмотра структуру записей. Еще можно делать множественные привязки, но это штука уже довольно сомнительная.
-- JSONB
UPDATE entity_jsonb
SET properties = jsonb_set(properties, '{"color"}', '"blue"')
WHERE id = 120;
Надо иметь ввиду, что значение в колонке properties заменяется целиком. Теперь представим, что JSON-ы, которые мы храним, достаточно большие. И если типичная нагрузка — это частые обновления отдельно взятых полей JSON-а, то EAV может оказаться и на голову эффективнее.
В общем, в статье незаслуженно обойден вопрос о производительности записи и влиянии на WAL.
Надо иметь ввиду, что значение в колонке properties заменяется целиком. Теперь представим, что JSON-ы, которые мы храним, достаточно большие. И если типичная нагрузка — это частые обновления отдельно взятых полей JSON-а, то EAV может оказаться и на голову эффективнее.
Если мы говорим о PostgreSQL, тип jsonb там хранится в бинарном виде, не парсится каждый раз и позволяет без проблем и быстро выбирать/заменять любое свойство внутри json( www.postgresql.org/docs/9.4/datatype-json.html ) не заменяя его полностью.
Хотя с основным посылом я согласен — jsonb во многих случаях выглядит предпочтительнее, чем EAV. Указываю только на то, что в общем случае нагрузка на БД не сводится к одним только select-ам.
Рассмотрим паттерн EAV (Entity-Attribute-Value), он встречается достаточно часто. Одна таблица содержит сущности (записи), другая таблица содержит имена свойств (атрибутов), а третья таблица связывает сущности с их атрибутами и содержит значение этих атрибутов для текущей сущности.
Небольшое замечание: это не структура данных EAV, а скорее реализация связи «многие-ко-многим». Для EAV достаточно одной таблицы с тремя столбцами: собственно сущность, атрибут сущности, значение атрибута. Может быть ещё справочник атрибутов, но не обязательно.
Я работал так:
1. таблица сущностей: entity
2. таблица атрибутов: attributes
у таблицы атрибутов поля:
— id
— entity_id
— key
— value
Все. Это работает. Храним любые атрибуты и их значения.
По любой сущности можно получить все атрибуты и значения 1 запросом с 1 join. Атрибутов моджет быть хоть 1000 штук. Доходило до 20 000 штук.
Хранить такое в JSONB — мб плачевно.
С другой сторону конечно же EAV это не серебрянная пуля. У нее есть как плюсы так и минусы.
Например чаще всего индекс есть только у ключей, а value хранятся без индекса. У этого тоже есть плюсы, но и весомый минус — при больших данных поиск значений в value становится долгим.
Есть много решений. Что то можно выдернуть в отдельные таблицы. Иногда подключается внешний индекс типа Эластика или Алголии. Например для создания поисковых индексов.
JSONB крутая штука. Где то она будет показывать себя лучше. Но не везде )
Если я не ошибаюсь, вынесения атрибутов в отдельную таблицу требует 3-я нормальная форма. И правильнее задаваться вопросом, что нам даст подобная денормализация.
Нет, 3НФ этого не требует. Вообще, ни одна из НФ не требует создавать отдельные суррогатные ключи для кодирования строк.
Исправьте, если я неправ.
Проблема решена… до того момента, пока ваша таблица не содержит миллионы записей и у вас не возникнет необходимость добавить новую запись.
Я не понимаю, в чём проблема добавить новую запись к миллионам других. Или это опечатка, и автор имел в виду «добавить новую колонку»? Но здесь тоже нет никакой проблемы, потому что в современных движках БД строки не фиксированной длины.
jsonb же выделяется тем, что быстрее json — об этом написано в статье, а также тем, что это все тот же json, в котором можно хранить не просто атрибуты, а целые структуры с вложениями, kv и списками. Быстрый доступ к таким структурам и возможность работать с такими структурами без костылей и является киллер фичей jsonb в сравнении с hstore.
Но это все историческая ремарка, jsonb сейчас ничуть не медленнее hstore и имеет неплохие преимущества перед ним, потому вопрос о том, что использовать в новом продукте уже и не стоит. Буду рад услышать преимущества hstore, если не прав.
Единственное, чего хотелось бы — типизация на уровне бд, а не на логическом уровне, но это уже капризы)
А если свойство может иметь несколько вариантов? {"Ram":[{"ddr3","sdram"}]}. Как проапдейтить или удалить определённое? Если на сотнях, тысячах записей?
В моём пет-проекте используется EAV. Наелся этого по самое нихочу.
Автор ничтоже сумняше не раскрыл один момент. Трёхтабличный EAV это не более чем пример практики, в реальности что-то адекватное можно построить не менее чем на 7. В моём проекте эта схема реализована на 9 и планирую расширить до 12 в ближайшей перспективе.
Благодаря этому я сохранил преимущества EAV плюс добился того что атрибуты хранятся в БД в своём типе, а не в строке. Недостатки остались конечно.
Вопрос производительности остаётся открытым и стоит остро. Метрики этого пока не собираю.
Статья от января 2016 года… У нас в проектах, тянущихся с тех времен, похожие решения используются. Но все-таки уже почти три года прошло с тех пор.
Хочется EAV — попробуй RDF-хранилище.
Тормозят JOIN'ы — используй графовую СУБД.
Любишь JSON — есть документные СУБД.
Хочется EAV — попробуй RDF-хранилище.
Зачем? Ну если только в пет-проекте попробовать…
Тормозят JOIN'ы — используй графовую СУБД.
Может лучше разобраться чего они тормозят?
Любишь JSON — есть документные СУБД.
Ну пусть дальше будут. Если основная СУБД у меня PostgreSQL я знаю что он вполне себе уже лет 10 как «Not Only SQL», и в текущем состоянии вряд-ли чем-то уступает какой-нибудь Монге( www.youtube.com/watch?v=SNzOZKvFZ68 )
Ну и да — Документо-ориентированные СУБД подразумевают немного другие подходы к декомпозиции в принципе, потому для стандартной модели с реляциями не подходят.
Не, ну… Я конечно за то чтобы подбирать инструменты под задачу, но в данном случае это зоопарк технологий непонятно для чего.
Да, здесь не под задачи, а под радости и боли автора статьи. Конечно, из таких соображений строить архитектуру хранения не стоит.
«NoSQL Distilled» Фаулера и Садаладжа в оригинале имеет вовсе не такой заголовок, как в переводе. Зоопарк (polyglot persistence), видимо, неизбежное следствие NoSQL, о чем читатели оригинала были честно предупреждены. Возникающие проблемы авторы предвидели, но недооценили. В связи с чем придумывают мультимодельные СУБД (немного писал о них здесь). PostgreSQL в качестве мультимодельной СУБД скорее не рассматривают, чем рассматривают.
Зачем? Ну если только в пет-проекте попробовать…
Какой был самый большой бюджет у проекта, в котором вам доводилось участвовать? Я наверняка смогу указать «пет-проект» с большим бюджетом.
Может лучше разобраться чего они тормозят?
Иногда в конце концов упираешься в то, что в реляционной модели связи грубо говоря, вычисляются, а в графовой, грубо говоря, хранятся.
apapacy, a Couchbase не пробовали?
По документо-ориентированным DB — много вопросов возникает сейчас. Когда-то на волне хайпа я юзал монгу с nodejs. На тот момент сравнение производительности с теми же postgres+nodejs и mysql+nodejs выглядело сильно в пользу mongo+nodejs. Сейчас выигрыш по insert уже не наблюдается. (Причина неясна, скорее всего драйверы для nodejs были не очень производительные). Плюс у postgresql появился bson. Что имеем в итоге. Постгрес с bson перегрывает весь функционал mongodb и не уступает в производительности. Плюс дает возможность без проблем делать выборки с join, sum, like — чего не очень удобно делать в mongodb
— могут выбираться из списка (цвет товара, класс пожарной опасности).
— могут быть произвольным текстовым полем (название материала подошвы кроссовок)
— могут быть числом с заранее заданным диапазоном (размер шкафа, положительное целое число со значениями от и до)
— хранимое значение и вывод этого значения могут отличаться (водостойкость/морозостойкость в БД хранится, как true/false, а на выходе должно быть отсутствует/присутствует или да/нет или 0/1 или еще как-нибудь)
— может быть обязательным к заполнению или нет
— возможно значение по умолчанию
— единица измерения значения (ширира обоев в см, длина обоев в метрах)
Подскажите, а есть ли какие-нибудь цифры по построению фасетных фильтров при использовании EAV и JSONB?
Это всё конечно хорошо, но одним sql сыт не будешь, а orm поддерживают jsonb плохо. Пытался мигрировать с eav на jsonb, но бросил т. к. каждый раз извлекаешь json целиком и парсишь его, чтобы извлечь/изменить нужный атрибут.
Хотя по месту выигрыш был сладкий — 10+ 2Gb индексов превратились в 2.5 +0.6 Gb.
В принципе статья хорошая в плане направления исследования. Я сам был уверен что индекс по bson работает дольше чем обычный.
Но.
Автор забыл определить индекс для поля value, чем сделал результаты просто неверными.
Я решил перепроверить — уж очень большая была разница и добавил индекс на поле value
Разница была и не очень существенная. Первый запрос по GIN было более продолжительое планирование. При повторных запросах планирование существенно (в 100 раз) сократилось и запрос по GIN был быстрее ( 1,2 мс против 1,3 мс). Но все же это не так у автора 1000-х разница
как снова производительность возрастает в несколько раз, но теперь уже не в пользу jsonb и gin-индекса.
Я не добавлял многоколоночный (добавил одноколоночный), скорость практически одинаковая. Но все же немного ниже на 0,1 мс. Возможно на моем конкретном комьютере и с моими конкретными данными.
Скорее всего скорость может немного просесть если будет большой json объект с большим количеством свойств.
Впрочем это (то есть равноценность) вполне ожидаема, т.к. для чего иначе это было разрабатывать.
Токенами в FullTextSearch для данной цели будут является свойства объектов.
Бонусом — поиск по части названия/описания одновнеменно с фильтрацией по свойствам объектов (EAV — как правило для интернет-магазинов, так что такой поиск лишним не будет)
Замена EAV на JSONB в PostgreSQL