Без рубрики

Оптимизация запросов к базе данных в Битрикс D7: N+1, индексы и кэширование

Оптимизация SQL-запросов — один из главных инструментов ускорения Битрикс при высоких нагрузках. D7 ORM удобен, но неправильное использование создаёт медленные запросы.

Включение профилировщика

// В local/php_interface/init.php (только на dev!)
define("SHOW_SQL_STAT", true);

В низу страницы появится список всех SQL-запросов с временем выполнения.

N+1 запросы — главная проблема

// ПЛОХО: N+1 запросов
$items = ElementTable::getList(['select' => ['ID']]);
foreach ($items as $item) {
    $props = PropertyTable::getList(['filter' => ['ELEMENT_ID' => $item['ID']]]);
}

// ХОРОШО: один запрос с JOIN через runtime reference
$items = ElementTable::getList([
    'select' => ['ID', 'NAME', 'PROP_VALUE' => 'PROPERTY.VALUE'],
    'runtime' => [
        new ORMFieldsRelationsReference(
            'PROPERTY',
            PropertyTable::class,
            ['=this.ID' => 'ref.ELEMENT_ID']
        ),
    ],
]);

Индексы — быстрая оптимизация

EXPLAIN SELECT * FROM b_iblock_element WHERE IBLOCK_ID=5 AND ACTIVE='Y';

Если в EXPLAIN видите type: ALL — нет подходящего индекса. Добавьте:

ALTER TABLE b_iblock_element ADD INDEX idx_iblock_active (IBLOCK_ID, ACTIVE);

Кэширование результатов

$cache   = Cache::createInstance();
$cacheId = md5(serialize($filter));

if ($cache->initCache(3600, $cacheId, '/my/cache/')) {
    $data = $cache->getVars();
} else {
    $cache->startDataCache();
    $data = UserTable::getList(['filter' => $filter])->fetchAll();
    $cache->endDataCache($data);
}

Всегда выбирайте только нужные поля

// Вместо выборки всех полей
$result = ProductTable::getList([
    'select' => ['ID', 'NAME', 'PRICE'],
    'filter' => ['=ACTIVE' => 'Y'],
    'limit'  => 20,
    'offset' => ($page - 1) * 20,
]);