Когда лучше использовать сложные sql запросы с множеством join, встроенными функциями субд, а когда лучше использовать лаконичные select и всю обработку производить на бэке?
Это вопрос из рубрики #женя_есть_вопрос. Решила обсудить сегодня вместо пятницы, так как вопрос технический.
Расскажу свое видение из практики, приходите тоже поделиться опытом в комментариях!
Основные критерии: скорость обработки (пользователи не хотят ждать долго) и потребляемые ресурсы.
1️⃣ Первое, на что я обращаю внимание — это объем данных в выборке. Тащить из базы сотни тысяч "сырых" строк, чтобы потом их фильтровать/агрегировать в коде — плохая идея на мой взгляд. Будет дольше по времени и требовательнее по ресурсам:
➖ загрузим сетевое соединение между БД и сервисом
➖ нагрузим память микросервиса этими данными
Поэтому, если нам надо сджойнить 2-4 таблицы, фильтрануть и отсортировать результат, конечно, лучше отдать это базе. Особенно если у нас созданы нужные индексы.
2️⃣ Также нехорошо на пустом месте увеличивать количество запросов в базу, то есть делать много вызовов там, где можно было бы упаковать всё в один.
Если вы используете ORM (Hibernate / SQLAlchemy и др.), то при lazy loading как раз можно столкнуться с известной проблемой N+1, когда в базу уходит большое количество запросов вместо одного с JOIN-ом.
3️⃣ Однако если в запросе много JOIN-ов (больше 10), стоит заранее:
👉 оценить, какой объём данных будет склеиваться, насколько он велик.
👉 убедиться, что есть индексы на колонках, по которым идет JOIN, особенно если это большие таблицы
👉 посмотреть план выполнения (через EXPLAIN или EXPLAIN ANALYZE): проверить, что база не делает вложенные переборы по миллионам строк.
Возможно, если данных много и/или индексов не хватает, то базе будет сложно выполнить запрос быстро. Тут в зависимости от ситуации можно:
🔵 разбить запрос на несколько шагов на уровне SQL (view, временные таблицы)
🔵 разделить получение данных на несколько запросов на уровне кода и потом в коде собрать и обработать результаты
Если каждая таблица по несколько миллионов строк и их джойнят 10 раз — точно придётся оптимизировать. Если же у нас 10 таблиц по 1 000 строк, то база справится без напряга.
4️⃣ Сложную логику обработки на мой взгляд лучше не перекладывать на базу. Опытные DBA могут написать на SQL всё что угодно, но мне кажется в большинстве проектов лучше делать это в коде бэке.
Например, на бэке лучше писать логику, когда нужно сопоставить один список результатов с другим, если есть условия, когда нужно построить иерархичный ответ, если нужно сделать пост-обработку результатов и др.
5️⃣ Код чаще читают, чем пишут, поэтому понятность и легкость поддержки тоже важны.
Если часть функциональности активно развивается, то не стоит паковать её в громоздкий SQL, еще и постоянно усложняя его, когда продакт будет приносить всё новые и новые фичи. Лучше отдать базе фильтрацию по простым условиям, а усложнение логики крутить на бэке.
Всё вышеописанное относилось к OLTP базам.
Фокус OLAP баз — строить сводные отчёты, дашборды, исторические выборки и т. д. Тут как раз будут таблицы с сотнями миллионов строк, которые нужно джойнить.
Здесь SQL-оркестр играет на полную: большие и сложные запросы, common table expressions, оконные функции и др. OLAP-базы специально сделаны, чтобы работать с этим. Структуры таблиц в таких базах могут быть оптимизированы под чтение.
Плюс в случае OLAP баз скорость ответа имеет чуть меньшую критичность по сравнению с OLTP, где разгневанный долгим ответом пользователь закроет вкладку и уйдет к конкурентам.
