то ждем ваше обращение в нашей службе тех поддержки.
Оптимизация выборки дополнительных данных
Использование метода GetList
Мелкие запросы, на первый взгляд, не сильно влияют (в относительной оценке) на работу сайта, если при этом на том же сайте есть какой-нибудь явно тяжелый кусок кода, связанный, например, с фильтрацией товаров, который по определению кешированию не поддается.
Достаточно часто в проектах многие используют вызов CIBlockElement::GetById. Простой, удобный метод, когда надо вытащить какое-то поле для элемента инфоблока. Но этот метод тянет все поля и все свойства элемента. В случае инфоблока с большим количеством свойств и большого числа посетителей на сайте этот простой запрос приводит к снижению производительности. А если таких запросов несколько десятков в различных result_modifier у разных компонентов? Оказывается, что в совокупности, несмотря на кеширование, эти вещи создают пиковые нагрузки в момент обновления кеша.
Если уж надо получить название элемента по ID, то лучше воспользоваться GetList с указанием конкретного вытаскиваемого поля элемента.
Соответственно, разница в скорости выполнения будет сильно зависеть от того, что именно надо вытащить, а потому сравнивать надо не просто два метода, а конкретную бизнес-логику конкретного сайта.
Также GetList позволяет выбрать сразу несколько записей, тогда как GetByID только одну.
Единственное преимущество GetById перед GetList с точки зрения разработчика состоит в том, что GetById можно просто вызвать и все. Прямое использование GetList требует определенной работы с кодом (создание массива параметров).
Важные примеры повышения производительности
- Рассмотрим пример, когда необходимо для каких-либо элементов одного инфоблока (авторы книг) получить дополнительные свойства из другого инфоблока (информация по авторам), например путем изменения шаблона или 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; }
- Если мы можем изменить основной АПИ-вызов, например, используем свой компонент, то данные по авторам из примеров выше можно получить сразу:
// правильный вариант, // информация по книгам и связанная с ними информация по авторам выбирается одним запросом $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; }
- Использование более оптимального кода, который сокращает количество запросов.
Пример: Необходимо проверить для списка определенных пользователей, состоят ли они в заданной группе.
Типичный код:
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()) { // Делаем что то }
Назад в раздел