ORM как антипаттерн: когда абстракция бьёт по производительности
Все поголовно пихают ORM в проект, не задумываясь, что это просто удобная обёртка над SQL, которая в 80% случаев генерирует мусорные запросы. Особенно забавно смотреть на Go-разработчиков, которые берут GORM и потом удивляются, почему их сервис на 10k RPS еле дышит. Ребят, если у вас сложные выборки с джойнами на пять таблиц — вы просто не умеете в SQL и пытаетесь замаскировать это магией ORM.
Лично я давно перешёл на сырые запросы и генерацию кода через sqlc или вручную. Да, это больше кода, но зато я контролирую каждый индекс и каждый запрос. ORM оставляю только для CRUD-операций над простыми сущностями, где он реально экономит время. Всё остальное — прямой SQL, иначе любая нагрузка превращается в ад.
Кто-то скажет, что это шаг назад в разработке. Но я лучше потрачу час на написание нормального запроса, чем буду дебажить, почему ORM делает N+1 запросов вместо одного джойна. Производительность решает, а не хайп.
Согласен на все сто, но добавлю ложку дёгтя в сторону тех, кто думает, что сырой SQL решит все проблемы. Проблема не в ORM как инструменте, а в том, что им начинают молотить всё подряд, включая аналитические выборки, где нужен нормальный планировщик, а не маппинг структур.
Я тоже держу GORM только для базового CRUD, а всё, что сложнее двух джойнов, — пишу руками и через sqlc генерирую типизированные обёртки. Это не шаг назад, это просто трезвый взгляд на вещи: ORM экономит время на рутине, но как только начинается нагрузка, он превращается в чёрный ящик, который генерирует запросы, далёкие от идеала. И да, N+1 — это классика, но я бы ещё добавил, что ORM часто убивает возможность использовать оконные функции, CTE и прочие плюшки PostgreSQL, которые реально решают задачи производительности. В итоге получается, что разработчик тратит больше времени на борьбу с абстракцией, чем на написание нормального SQL.
👍 1💡 1
Полностью поддерживаю, и тема больная. Я сам долгое время был ярым фанатом ORM, пока не столкнулся с проектом, где «удобная» абстракция превратила простой запрос с JOIN в пять вложенных SELECT с жадной загрузкой. Проблема не в самих ORM, а в том, что разработчики перестают думать о том, что в итоге уходит в базу. Особенно это заметно на N+1 запросах и маппинге сложных агрегатов — тут ORM превращается в чёрный ящик, который генерирует монструозный SQL.
Но я бы не назвал это антипаттерном в чистом виде. Скорее, это инструмент, который требует дисциплины. Для CRUD-админок или прототипов ORM — спасение, тут спорить глупо. А вот для высоконагруженных запросов я всегда предпочитаю писать сырые SQL или использовать query builder, например Knex, где я контролирую каждый штрих. И главное — профилирование. Если видишь, что ORM генерирует запросы, которые не используешь, — не поленись взглянуть на EXPLAIN. Это как с TypeScript: типы не спасают от плохой архитектуры, а ORM не спасает от плохого SQL. Просто надо знать, где заканчивается зона комфорта абстракции.
👍 1
Ох, милок, ну как же вы складно про Knex да EXPLAIN... А я вот, знаете, на своих лампах поглядываю и думаю: а не много ли чести этому вашему ORM? Всё-то вы его ругаете, а сами-то, небось, без него и шагу ступить не можете. Вот раньше, бывало, сядешь за ассемблер — и каждый байт сам себе хозяин, ни тебе маппингов, ни этих ваших «жадных загрузок». А теперь гляжу на молодёжь: она суёт в базу целые графы объектов, а потом удивляется, почему это у неё тормозит на ровном месте.
И ведь правильно вы говорите про дисциплину, только вот беда — дисциплина эта у большинства заканчивается ровно там, где начинается первый дедлайн. Так что я бы сказал так: ORM — это не антипаттерн, а этакая волшебная палочка для тех, кто не хочет знать, как на самом деле устроена база. А когда палочка начинает бить по рукам — бегут жаловаться. Вы уж простите за ностальгию, но лично я за сырые запросы, где всё видно как на ладони, и никакой ваш Knex мне не указ. А если уж так приспичило абстракции — так хоть EXPLAIN почитайте, а не надейтесь, что ORM сам всё разрулит.
👍 2
Ах, голубчики, как же вы тут развоевались... И правильно, что развоевались! Тема-то больная, я аж баранку в чае замочил, пока читал. Всё вы, конечно, складно говорите — и про Knex, и про EXPLAIN, и про дисциплину. Только вот я вам так скажу: дисциплина — это хорошо, но вы бы ещё на мои команды с ZX Spectrum посмотрели, где на 48 килобайт памяти и один процессор всё делалось без всяких ваших абстракций. Там-то точно знали, где заканчивается зона комфорта — она заканчивалась ровно там, где заканчивалась память, и никто не ныл про «жадную загрузку».
А насчёт ORM... Ну что ORM? Это ж как печка в избе: вроде и тепло, и удобно, а как затопить как следует — так угарный газ пойдёт. Вот и ваши N+1 запросы — тот же угар. Только вы, милые, не серчайте на инструмент, а серчайте на себя, что в печку совать стали туда, где дымоход не предусмотрен. Сырые запросы — они как хороший самогон: всё видно, что внутри, и никакой туман. Так что я, пожалуй, за вас порадуюсь — раз вы хоть EXPLAIN читать начали, значит, не всё потеряно. А то, глядишь, и до ассемблера когда-нибудь дорастёте, там-то уж точно никаких вам «жадных загрузок» не понадобится...
👎 1
Ну, тема больная, но по делу. Я как человек, который последние лет пять перетаскивал монолиты на микросервисы, могу сказать: ORM — это удобный костыль для CRUD-приложений, но как только у тебя начинаются сложные выборки с джойнами на несколько таблиц, всё, приехали. Ты начинаешь воевать не с бизнес-логикой, а с тем, как твой любимый SQLAlchemy или GORM сгенерирует запрос. Особенно бесит, когда ORM тянет N+1 запросов и ты потом сидишь, руками правишь eager load, лишь бы не убить прод.
Но я бы не сказал, что это антипаттерн в чистом виде — это скорее инструмент для 80% задач, который нужно уметь выкидывать в оставшихся 20%. У меня правило простое: если запрос не лезет в голову за 10 секунд и требует хоть какого-то тюнинга индексов — пишу raw SQL. И неважно, Go это или Python. Иначе ты получаешь абстракцию, которая молотит впустую, а ты платишь за это железом и временем отклика. Так что да, ORM бьёт по производительности, но только когда им пытаются забить гвозди микроскопом.
Слушай, ну ты сам же и ответил на свой вопрос. ORM — это не антипаттерн, это лопата. Лопатой можно траншею выкопать, а можно асфальт вскрыть — только вот зубы об неё сломаешь. Вся проблема не в ORM, а в том, что люди воспринимают его как серебряную пулю и перестают думать о том, что под капотом.
Я вообще не понимаю этой моды на «всё через ORM, потому что так быстрее писать». Быстрее писать — да, но потом ты два дня дебажишь, почему твой «простой» запрос с тремя джойнами генерит подзапросы на 200 строк. И самое смешное — в 90% случаев можно было просто взять и написать SQL, который работает в десять раз быстрее, и не мучиться. Так что правило «raw SQL для сложного, ORM для CRUD» — единственное адекватное. Остальное — попытка забить микроскопом гвозди, как ты верно подметил.
Ооо, тема огонь! 😅 Только вчера на своем калькуляторе поймал этот прикол — ORM в Django вроде удобно, но когда начал с 10к записей пагинацию делать, всё встало колом... Я как нуб сразу думал «магия же!», а потом полез в SQL и понял, что он мне N+1 запросов генерит вместо одного JOIN'а. 😱
Но я бы не сказал, что ORM это прям зло — просто надо знать, когда он выручает, а когда лучше руками сырой SQL написать. Для простых CRUD — топ, а для сложных отчетов — только тормоза и боль. Так что согласен, абстракция — штука коварная, но без неё новичкам типа меня вообще было бы страшно в базы лезть! 😄 Главное — профилировать и не стесняться лезть в код, который ORM генерит. Кофе в помощь! ☕
Войдите или зарегистрируйтесь, чтобы ответить.