Несколько простых запросов вместо одного большого для загрузки связей в ORM

в 22:18, , рубрики: orm, php, sql, метки: , ,

Сразу оговорюсь, это не обучающий пост и не провозглашение новой парадигмы )), скорее решение, к которому я пришел, и хочется его обсудить в широкой и честной дискуссии.
Теперь к сути, представьте, что есть некая ORM, написанная на PHP, в которой описана модель Posts, имеющая связи многие-ко-многим через промежуточные таблицы с другими моделями: Comments, Tags, Categories. Вопрос в том, каким способом лучше поднимать связанные данные, всё сразу или с отложенной загрузкой?

В сообществе БД преобладает мнение, что данные лучше поднимать одним запросом с кучей join'ов, мол СУБД умная, она сама разберет, как быстрее всего это сделать и чем меньше запросов к базе тем лучше. В моей же практике были случаи, когда на высоконагруженных проектах с большими таблицами несколько простых запросов работали быстрее, чем один большой с несколькими объединениями.
Со стороны ORM подъем всех данных одним запросом тоже не лучший вариант, потому что, почти всегда, будут подниматься лишние данные, которые в этом месте не нужны (или даже могут помешать, и тогда их еще придется удалять из набора), либо надо иметь набор методов вроде findWithComments, findWithCategoriesAndTags, findWithAllRelations с неизбежным дублированием.
Таким образом, имеем три способа загрузки связей (методы модели):

  • Один метод find($id), который всегда загружает все данные в одном запросе.
  • Несколько методов find($id), findWithComments($id), findWithTagsAndCategories($id) ...
  • Метод find($id), который загружает только текущую модель + явные методы для загрузки связей getComments(), getTags()… при чем последние методы работают одинаково, как для одного объекта, так и для коллекции объектов (похоже на Composite).

Если говорить о проектировании какой-то универсальной ORM, последний вариант мне представляется более правильным, причем другого подхода ORM не должна позволять. Расскажу о преимуществах.

  • Кеширование на уровне БД. Маленькие порции данных должны чаще браться из кеша (из-за переиспользования их в разных запросах), чем большие уникальные запросы.
  • Кеширование на уровне приложения. Как правило, данные из разных таблиц имеют разную скорость протухания, если использовать первый способ, то приходиться ориентироваться на данные, которые быстрее всего становятся неактуальными. Если использовать третий способ, то для каждой модели можно указывать собственное время жизни кеша, плюс это дает более гибкое (по-модельное) управление очищением кеша (по событию или в ручную).
  • При росте объемов данных или количества запросов мы имеем готовую архитектуру для вертикального шардинга БД.
  • В каждом случае мы можем загружать только те данные, которые нам нужны.
  • Готовая архитектура для смешанного использования SQL и noSQL, например какие-то модели мы можем перенести в mongo или redis, переписав для этого заранее предсказуемое количество методов.

Слабое звено такой жесткой изоляции моделей — как делать гибкий поиск сразу по многим критериям, например постов по тегам и категориям.
В общем, приглашаю к дискуссии, какие еще есть минусы, может кто-то приходил / уходил от такого решения, использовали бы вы такую ORM в ваших проектах?

Автор: bardex

Источник

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


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