Комментарии 28
Отчет о производительности для 200 000 групп,
4 000 000 записей Post,
каждая запись Post находитсья в 40 группах — https://github.com/shhavel/demo_group_ids_performance_test
Возмодно, 4 000 000 записей оказалось мало, так кhttps://github.com/shhavel/demo_group_ids_performance_testак, достаточно быстро работает и без индекса.
Это для случая при котором Post принадлежит многим группам.
Если же Post принадлежит только одной группе,
то способ выбора всех записей Post с использованием массива group_ids
немного проще
в сравнении с использованием третьей таблицы.
Запрос получения записей Post доступных пользователю:
SELECT * FROM posts WHERE id IN (<user.group_ids>);
где <user.group_ids> — список ID групп пользователя.
При использованиии третьей таблицы этот список еще нужно получить,
а при использовании колонки group_ids
список заведома известен (при условии, что выполнен запрос на получение пользователя)
Спасибо
Статья хорошая, спасибо, вот только больно смотреть, когда для объяснения работы с Ecto вдруг оказался нужен Phoenix.
(ее суть — как раз показать использование колонки типа Array в моделях),
если убрать Phoenix статья короче не станет.
Спасибо
Но я так понимаю, Вы считаете использование Phoenix в данной статье неоправданным.
Phoenix был использован для генерации моделей и тестов, и возможно, это уменьшает объем работы для воспроизведения описанного решения. Поэтому я считаю использование Phoenix уместным.
Спасибо
Я думаю, поддержка foreign keys будет еще добавлена в PostgreSQL.
На сегодняшний же день, можно просто удалять ID группы из всей записей posts
при удалении самой группы:
UPDATE posts SET group_ids = group_ids - 4 WHERE group_ids && ARRAY[4];
Несомненно, это создает дополнительное неудобство, но не отбрасывает вариант использования колонки типа Array как альтернативу третьей таблицы.
Спасибо
Это решение подходит только для случая, при котором третья таблица не содержит дополнительный данных.
Например, приложение в котором применяется это решение предусматривает роли пользователей общие для всех групп.
Необходимость добавить дополнительные свойства в третью таблицу возникает не по мере роста базы данных, а по мере добавления бизнес логики.
Желательно пытаться всегда найти наиболее простое решение для данной конкретной задачи и подходить к решению непредвзято.
В любом случае, не сложно изменить структуру базы и выполнить миграцию данных даже при большом их количистве.
Спасибо
И не стоит забывать про проверку существования ключей. Например удаляете вы группу, а кто удалит её из списков у всех юзеров. Foregin key не даст просто так сделать не консистентную базу, а список интов — даст.
Производительность индекса, скорее всего, не будет заметно отличаться от использования третьей таблицы.
Но во многих случаях можно просто выполнять на один запрос меньше (возможно при каждом обращении к приложению).
Рассмотрим пример исользования в rails с исользованием cancan.
Пусть, Post
принадлежит только одной группе
Третья таблица, для связи users
и groups
— memberships
Определение выборки всех записей Post
доступных пользователю с использованием третьей таблицы:
can :read, Post, group_id: user.memberships.pluck(:group_id)
Определение выборки всех записей Post
доступных пользователю с использованием group_ids
:
can :read, Post, group_id: user.group_ids
Спасибо
Пока только это — https://github.com/shhavel/demo_group_ids_performance_test
200 000 групп
4 000 000 постов
каждый пост принадлежит 40-ка группам.
Выборка работает быстро и без индекса.
Постараюсь подготовить еще данные, чтобы было заметно разницу от использования индекса.
Это решение уже используется в production.
Я попытался описать его преимущества в некоторых ситуациях.
Спасибо
«подходит только для случая». Решение может и имеет право на жизнь, но при огромных лимитейшенах, а хорошего бенефита не видно. Видно только убийство масштабируемости.
«На один запрос меньше» при каждом обращении к приложению — это приимущество.
А также:
- упрощение кода приложения при получении списка ID груп пользователя.
- очевидная связь массивва
group_ids
с HTML multi select для выбора групп
и только одинUPDATE
запрос при редактировании.
Спасибо
Если в таблице есть вторичный ключ, то это тожt можно назвать хранением свойств (бизнесс данных) и связок (служебных данных).
Кроме того Вы не вынуждены получать запись с информацией о всех связях если эта информация вам не нужна,
можно же выбирать только те поля которые нужны перечислив их в SELECT
.
Например если нужно выбрать только id
, name
, email
:
SELECT id, name, email FROM users;
И иногда, это вполне может оказаться полезным.
Например, если таблица, которая хранит сущность данных массива, небольшая и из нее редко удаляются данные, то почему нет? :)
Я в связке с Django использую поле-массив для тэгов. Пока вроде все нормально.
Отношение многие ко многим без третьей таблицы в PostgreSQL используя Elixir Ecto