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

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

Просмотров: 23230
Дата последнего изменения: 15.11.2023
Роберт Басыров
Сложность урока:
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())
    {
    	// Делаем что то
    }
    
29
Курсы разработаны в компании «1С-Битрикс»

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