Перейти до вмісту
Mihail Leybuk-Galin — головна
Бекенд-інженерія3 хв читання

Приховані витрати абстракції: Коли ORM стають вузькими місцями.

ORM пришвидшують розробку, приховуючи повторювану логіку персистентності. Однак у великих масштабах та сама абстракція може приховувати неефективні запити, надмірну гідратацію об'єктів, шаблони N+1 та стратегії доступу до бази даних, які більше не відповідають робочому навантаженню.

Mihail Leybuk-GalinСистемний архітектор

1. Абстракція має вартість виконання

Об'єктно-реляційні мапери вирішують реальну інженерну проблему. Вони усувають повторюваний код персистентності, нормалізують поширені шаблони доступу до даних і дозволяють командам швидко рухатися без написання SQL від руки для кожної операції.

Небезпека виникає, коли інженери починають вважати, що простий код програми завжди відповідає простій роботі з базою даних. Один виклик методу може викликати десятки запитів, великі об'єднання, непотрібний вибір стовпців, гідратацію вкладених об'єктів або приховані мережеві цикли.

2. Проблеми продуктивності, які приховують ORM

Класичним режимом відмови є задача запиту N+1. Завантаження однієї колекції, а потім окремий виклик бази даних для кожної пов'язаної сутності, перетворює, здавалося б, нешкідливий цикл на десятки або сотні мережевих циклів.

Один виклик ORM може розширитися на кілька операцій з базою даних

Один виклик ORM може розширитися на кілька операцій з базою даних

Відкрити повнорозмірне зображення

Надмірне вилучення створює інше вузьке місце. Кінцева точка каталогу може вимагати лише ідентифікатор, назву, ціну та мініатюру, тоді як ORM обробляє описи, переклади, категорії, атрибути, зв'язки з постачальниками та кожне пов'язане зображення.

Потім вартість поширюється на операції вводу-виводу бази даних, мережеву передачу, декодування драйверів, розподіл, збирання сміття, зіставлення об'єктів, серіалізацію JSON та розмір відповіді.

3. Пошук справжнього вузького місця в базі даних

Рішення полягає не в заміні кожного виклику ORM необробленим SQL. Воно полягає в тому, щоб зробити поведінку бази даних доступною для спостереження. Інженери повинні мати можливість бачити згенерований SQL, тривалість запиту, повернуті рядки, час очікування пулу підключень та загальну кількість запитів, виконаних на запит.

catalog_repository.go

// Generic entity hydration
var products []Product

db.
    Preload("Images").
    Preload("Categories").
    Preload("Attributes").
    Find(&products)

// Purpose-built read projection
type ProductCard struct {{
    ID        string
    Title     string
    Price     float64
    Thumbnail string
}}

db.Raw(`
    SELECT p.id, pt.title, p.price, i.url AS thumbnail
    FROM products p
    JOIN product_translations pt ON pt.product_id = p.id
    LEFT JOIN images i ON i.product_id = p.id AND i.sort = 1
    WHERE p.active = TRUE
    ORDER BY p.popularity DESC
    LIMIT 24
`).Scan(&cards)

Після того, як дороговартісний запит ізольовано, EXPLAIN ANALYZE стає кориснішим, ніж спекуляції на рівні програми. План виконання виявляє послідовні сканування, погані стратегії об'єднання, розливи сортування, погані оцінки кардинальності та відсутні індекси.

Очікування пулу підключень має вимірюватися окремо від виконання запиту. Запит тривалістю 20 мс все ще може створити інтервал бази даних 200 мс, якщо запит очікує вільного з'єднання 180 мс.

4. Знання того, коли потрібно перейти до SQL

Зрілі рівні персистентності рідко обирають між «ORM скрізь» та «сирим SQL скрізь». Вони використовують найкориснішу абстракцію для кожного робочого навантаження.

ORM

CRUD-операції

Транзакційні записи

Проста фільтрація

Адміністративні кінцеві точки

Стандартне завантаження сутностей

SQL / Query Layer

Високопродуктивне читання

Складні агрегації

Звітність

Масові операції

Функції, специфічні для бази даних

Прагматичним підходом є збереження багатих моделей доменів, що підтримуються ORM, для запису, водночас створюючи цільові проекції для шляхів читання з великим обсягом даних. ORM залишається корисним інструментом впровадження; він просто перестає диктувати архітектуру.

5. Результати та показники

Оптимізація лише критичних шляхів збереження може призвести до непропорційного покращення без необхідності повного переписування рівня доступу до даних застосунку.

Запити / Запити
84→6
Цільова база даних p95
95мс
Пропускна здатність кінцевої точки
2.3x

Мета полягає не в тому, щоб усунути абстракцію. Мета полягає в тому, щоб знати, де абстракція більше не відповідає робочому навантаженню, і надати доступ до керування базою даних нижчого рівня лише тим критично важливим для продуктивності шляхам.