318  /  381
Справочник

Оптимизация выборки дополнительных данных

Просмотров: 17767
Дата последнего изменения: 25.06.2021
Роберт Басыров
Сложность урока:
4 уровень - сложно, требуется сосредоточиться, внимание деталям и точному следованию инструкции.
1
2
3
4
5
Недоступно в лицензиях:
Ограничений нет

Использование метода GetList

Мелкие запросы, на первый взгляд, не сильно влияют (в относительной оценке) на работу сайта, если при этом на том же сайте есть какой-нибудь явно тяжелый кусок кода, связанный, например, с фильтрацией товаров, который по определению кешированию не поддается.

Достаточно часто в проектах многие используют вызов CIBlockElement::GetById. Простой, удобный метод, когда надо вытащить какое-то поле для элемента инфоблока. Но этот метод тянет все поля и все свойства элемента. В случае инфоблока с большим количеством свойств и большого числа посетителей на сайте этот простой запрос приводит к снижению производительности. А если таких запросов несколько десятков в различных result_modifier у разных компонентов? Оказывается, что в совокупности, несмотря на кеширование, эти вещи создают пиковые нагрузки в момент обновления кеша.

Если уж надо получить название элемента по ID, то лучше воспользоваться GetList с указанием конкретного вытаскиваемого поля элемента.

Примечание: GetByID внутри вызывает GetList. Разница только в том, что GetByID в любом случае тянет все поля и свойства, а в GetList этот перечень можно контролировать.

Соответственно, разница в скорости выполнения будет сильно зависеть от того, что именно надо вытащить, а потому сравнивать надо не просто два метода, а конкретную бизнес-логику конкретного сайта.

Также GetList позволяет выбрать сразу несколько записей, тогда как GetByID только одну.

Единственное преимущество GetById перед GetList с точки зрения разработчика состоит в том, что GetById можно просто вызвать и все. Прямое использование GetList требует определенной работы с кодом (создание массива параметров).

Примеры повышения производительности

Важно! Множество однотипных запросов создают большую нагрузку. По возможности, необходимо сократить их количество.

  1. Рассмотрим пример, когда необходимо для каких-либо элементов одного инфоблока (авторы книг) получить дополнительные свойства из другого инфоблока (информация по авторам), например путем изменения шаблона или result modifier.

    В большинстве случаев это делается в цикле, например в таком:

     // неправильный вариант выборки, на каждый элемент делается дополнительный запрос
    foreach($arResult['ITEMS'] as $ikey => $ival)
    {
        $avtorID = intval($ival['PROPERTIES']['AVTOR']['VALUE']);
        if($avtorID > 0)
        {
            $rs = CIBlockElement::GetByID($avtorID);
            while($ar = $rs->GetNext())
            {
                $arResulr['ITEMS'][$ikey]['AVTOR_INFO'][] = $ar;
            }
        }
    }
    

    но это не правильно. Такой цикл создает множество запросов, что снижает производительность.

    Правильный вариант, это использовать один единственный запрос, в котором получать данные для требуемых элементов:

    // правильный вариант, информация по авторам выбирается одним запросом и только нужная
    $avtorID = array();
    
    foreach($arResult['ITEMS'] as $ikey => $ival)
    {
        $aID = intval($ival['PROPERTIES']['AVTOR']['VALUE']);
        if($aID > 0)
        {
            $avtorID[] = $aID;
        }
    }
    
    $avtorID = array_unique($avtorID);
    
    
    $rs = CIBlockElement::GetList(
        array('ID' => 'ASC'),
        array(
            'IBLOCK_ID' => XX,
            'ID' => $avtorID,
            'ACTIVE' => 'Y'
        ),
        false,
        false,
        array('ID', 'NAME', 'PREVIEW_PICTURE')
    
    );
    while($ar = $rs->GetNext())
    {
        $arResulr['AVTOR_INFO'][$ar['ID']] = $ar;
    }
    

  2. Если мы можем изменить основной АПИ-вызов, например, используем свой компонент, то данные по авторам из примеров выше можно получить сразу:
    // правильный вариант,
    // информация по книгам и связанная с ними информация по авторам выбирается одним запросом
    $rs = CIBlockElement::GetList(
        array('ID' => 'ASC'),
        array(
            'IBLOCK_ID' => YY,
            'ACTIVE' => 'Y'
        ),
        false,
        false,
        array(
            'ID',
            'NAME',
            'PREVIEW_PICTURE',
            'PREVIEW_TEXT',
            'PROPERTY_AVTOR.NAME',
            'PROPERTY_AVTOR.PREVIEW_PICTURE',
        )
    
    );
    while($ar = $rs->GetNext())
    {
        $arResulr['ITEMS'][] = $ar;
    }
    

  3. Использование более оптимального кода, который сокращает количество запросов.

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

    Типичный код:

    foreach ($arSomeUser as $arUser)
    {
        $arGroups = CUser::GetUserGroup($arUser['ID']);
        if(in_array(5, $arGroups))
        {
            // Делаем что-то
        }
    }
    

    В этом случае у нас будет столько запросов, сколько пользователей в списке + один на выборку нужных пользователей из всех возможных (самый первый запрос).

    Если в группе содержится мало пользователей, то правильнее будет использовать следующий подход:

    $rsUser = $arUser->GetList(($by="ID"), ($order="desc"), array('ACTIVE' => 'Y', 'GROUPS_ID' => 5 ....))
    while($arUser = $rsUser->GetNext())
    {
            // Делаем что то
    }
    
Курсы разработаны в компании «1С-Битрикс»

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