Комментарии 18
Эх, я думал будет сравнение производительности. Заголовок заманил, а внутри как на конференциях - "ну мы не проверяли другие варианты, сделали только такой".
Я обожаю постгрес, когда mvp по быстрому надо сделать, одной таблицы достаточно.
CREATE TABLE IF NOT EXISTS DataObject (
ids JSONB,
extra JSONB,
space VARCHAR(50) NOT NULL,
id BIGSERIAL,
PRIMARY KEY (space, id)
) PARTITION BY LIST(space);
Аля NoSql, в ids джейсон на эту же таблицу {space: id} также и связи с внешними сервисами могут хранится, вся логика и описание extra данных на уровне выше в бекенде реализуется, в базе остаётся только тригеры на спейсы настроить для обработки ON DELETE/UPDATE событий.
Ужас. Никогда не делайте так, пожалуйста. JSON в РСУБД - это плохо. EAV, как принцип хранения всех основных данных в РСУБД - тоже плохо. В JSON хранят данные, которые нативно сохраняются с источника так: кликстримы, показатели с оборудования, конфиги и тд, с последующим преобразованием его в нормальный вид для аналитики, например. EAV используют в основном для хранения: сложных справочников многоуровневых, быстро меняющихся структур или плохо структурированных разряженных данных. Хранение данных по ФЛ/ИП/ЮЛ хорошо решается стандартными способами.
JSON в РСУБД - это плохо. EAV, как принцип хранения всех основных данных в РСУБД - тоже плохо.
Такая аргументация нормальна при общении с трехлеткой. Для людей постарше желательно давать какое-то обоснование.
Как по мне, так наоборот, разветвленные сущности с переменным составом полей, которые обычно извлекаются целиком, намного удобнее и эффективнее хранить в JSON-е, чем делать 20 left join-ов при сильно нормализованной форме или танцевать с таблицами атрибутов, пытаясь хоть как-то выжать из них перформанс. Ваш опыт может отличаться от моего, но на доводах "это плохо, потому что плохо" дискуссию не построить.
Я и не планировал дискуссию, но давайте попробуем
Модель данных:
Основные данные по ИП/ЮЛ/ФЛ в большинстве случаев не нуждаются в нормализации и могут хранится либо в одной таблице (как по мне самый плохой вариант), так и в нескольких, например: organization, person, person_relation (для связи ФЛ c ЮЛ или с другими сущностями при необходимости). Возможно понадобится несколько сателитных сущностей для хранения данных "один ко многим" - но можно и без них обойтись.
Если Вас пугает большое кол-во джойнов - то тут можно воспользоваться view - которые замечательно профилируются и кэшируются в памяти и использовать их, как инфослой.
СУБД:
При использовании атрибутов (вместо JSON) - мы можем в БД использовать PK/FK c constraints - для обеспечения целостности данных.
Типизация данных позволяет экономить ресурсы при хранении и запросах + дополнительная валидация данных на уровне БД.
Масштабируемость - с нативными таблицами проще организовывать партицирование или шардирование.
Сортировка и группировка по нативным таблицам явно выигрывает по скорости у варианты с JSON (даже при наличии GIN).
С большей вероятностью часть JSON уйдут (при их разрастании) в TOAST - что создаст дополнительные расходы на их чтение и изменение.
О какой версии postgresql (или её форка) вы говорите, касательно скорости и производительности?
В 14 версии в последний раз тестировал JSONB - но вроде бы и в новых версиях ничего не поменялось. Архитектурно в pg работа с jsonb всегда будет медленнее, чем с нативными таблицами.
в каждой версии они улучшают работу с JSONB. но не факт, что именно на ваших сценариях будет заметно улучшение.
в своих сервисах мы решили использовать JSONB для входящих неструктурированных данных + как способ "безболезненного" расширения атрибутивной модели - все то, что не требует ни индексации, ни участия в запросах.
Отвечу, как один из основных разработчиков и идеологов системы, описанной выше. Неправильно говорить, что какие-то данные не нуждаются в нормализации, не понимая какие задачи перед нами стояли о каких данных идет речь.
Целостность и типизацию мы обеспечили через описанную выше инфраструктуру, в которую вложили несколько лет разработки. Эта инфраструктура гораздо более гибкая и масштабируемая, чем нативные возможности СУБД. Да, через нативные таблицы, многие вещи было бы проще реализовать, но проще - не значит лучше. И одна из основных идей статьи, как раз продемонстрировать, что грамотная реализация подобного хранения требует существенных вложений.
На счет скорости вопрос дискуссионный, нужны пруфы. Однако текущие потребности наша реализация вполне удовлетворяет, а ускорение ради ускорения не имеет смысла.
Если грамотно декомпозировать хранение данных в json, проблема с toast не возникнет примерно никогда. Я недавно смотрел ситуацию на нашей БД, там на десятки миллионов строк всего около 30 штук, у которых данные вылезли в toast, и их можно считать выбросами.
"Поиск произвольного набора клиентов по строчным данным, например, по фамилии или по адресу проживания укладывается в 40 секунд" - погодите, это где такой расслабленный SLA допускается? Обычно поиск клиента это 1-2 секунды
Добрый день! Речь в статье про 40 секунд шла в контексте наших технических ограничений.
То есть 40 секунд это максимально допустимое время ожидания ответа фронта от бэка.
Фактически запросы выполняются быстрее. Скорость зависит от количества заполненных данных для поиска.
речь не про поиск клиентА, а про произвольную выборку клиентов по неселективному набору атрибутов. Попробуйте за 2 секунды найти всех Ивановых в Москве, у которых отчество заканчивается на ..вич :)
Что вы будете делать, если вам понадобится атрибут, который ссылается на другую сущность в БД? К примеру, атрибут клиента, который ссылается на услугу или другого клиента. Судя по статье вы храните там только скалярные типы (в перспективе составные), но потребность в ссылках на другие сущности рано или поздно появится. Опять же к примеру, для ЮЛ нужно будет указывать ФЛ лицо руководителя или аффилированные ЮЛ. Если идентификаторы хранить в JSONB, то теряется целостность БД, а если городить таблицы связей для отдельных атрибутов, то теряется динамичность самих атрибутов.
Поиск по атрибутам в 40 сек. это явно не повод для гордости и сущее мучение для пользователей вашей системы. Я, когда столкнулся с подобной задачей, вынес поиск и листинг в отдельный поисковый индекс, а операции CRUD оставил за БД. Такой подход усложняет поддержание целостности данных в системе, но значительно сохраняет время её пользователям.
У каждого подхода есть свои плюсы и минусы. Кто-то предпочитает использовать классический подход и на каждый атрибут заводить таблицу, мы пошли альтернативным путем, который тоже работает. Ответ про 40 сек. дали выше.
Ситуация, когда отдельные атрибуты должны ссылаться на другие сущности системы возникают достаточно редко, однако если бы возникло, то есть два решения. Для разовых случаев храним ссылку в json в качестве очередного атрибута, а целостность поддерживаем функциональными проверками, они кратко описаны в статье. Для более системного случая есть повод задуматься над выделением данного атрибута в отдельную сущность и разработки под него своего набора нативных аблиц со своим собственным атрибутным составом в json.
Использование JSONB-полей вместо EAV в PostgreSQL