Задача - сформировать фид для выгрузки.
Дано:
кол-во товаров более 50 000
Товары выгружаются с пользовательскими свойствами
Результат эксперимента

Довольно частая задача - это формирование фидов, что подразумевает под собой выгрузку большого кол-ва элементов.
Битрикс часто ругают за то - что при простых операциях - происходит огромное ко-во запросов к БД, я видел на проектах , как для отображения простой страницы каталога, битрикс делал более 5000 запросов к БД. Именно из- за этого проекты на Битрикс иногда очень требовательны к железу на хостинге.
Современное ядро D7 позволяет значительно снизить нагрузку на БД и сильно ускоряет все процессы связанные с обращением к БД.
Долгое время на одном проекте работал скрипт по формированию фида для загрузки на сторонние сервисы. Скрипт работал, никаких сбоев не выдавал, и все всех устраивало, но вот пришло время вносить изменения и в него.
Первое что бросилось в глаза это то. как в этом скрипте происходила выборка товаров из каталога и добавление к ним пользовательских свойств
\Bitrix\Main\Diag\Debug::startTimeLabel('f_time1'); $obj_items=\CIBlockElement::GetList([] ,$filter, false, false, $select); while ($objItem = $obj_items->GetNextElement()) { $arItem=$objItem->GetFields(); $result[$arItem['ID']] = $arItem; $result[$arItem['ID']]['PROPERTIES'] = $objItem->GetProperties(); } \Bitrix\Main\Diag\Debug::endTimeLabel('f_time1'); $f_time=\Bitrix\Main\Diag\Debug::getTimeLabels(); $str_res= "Кол-во эл-в выборки Старое ядро: ".$obj_items->SelectedRowsCount().PHP_EOL; $str_res.= "Время выполонения выборки Старое ядро: ".round($f_time['f_time1']['time'], 8, PHP_ROUND_HALF_UP).PHP_EOL; echo $str_res;

Первое что пришло на ум это изменить добавление свойств к элементу
$obj_items=\CIBlockElement::GetList([] ,$filter, false, false, $select); while ($arItem = $obj_items->fetch()) { $result[$arItem['ID']] = $arItem; $result[$arItem['ID']]['PROPERTIES'] = []; $ids[] = $arItem['ID']; } $chunks = array_chunk($ids, 1000); foreach ($chunks as $key => $chunk) { \CIBlockElement::GetPropertyValuesArray( $result, 2, ['ID' => $chunk], ['CODE' => $properties], ['GET_RAW_DATA' => 'Y'] ); } \Bitrix\Main\Diag\Debug::endTimeLabel('f_time1'); $f_time=\Bitrix\Main\Diag\Debug::getTimeLabels(); $str_res= "Кол-во эл-в выборки Старое ядро: ".$obj_items->SelectedRowsCount().PHP_EOL; $str_res.= "Время выполонения выборки Старое ядро: ".round($f_time['f_time1']['time'], 8, PHP_ROUND_HALF_UP).PHP_EOL; echo $str_res;
Что дало уже приемлемый результат:

Но, обращаться к БД в цикле - это точно нехорошо.
Поэтому решил попробовать Переписать этот код с использованием возможностей ядра D7
//список свойств //CODE свойств изменены $properties = [ 'PROP_1', 'PROP_2', 'PROP_3', 'PROP_4', 'PROP_5', 'PROP_6', 'PROP_7', 'PROP_8', 'PROP_9', 'PROP_10', 'PROP_11', 'PROP_12', 'PROP_13', 'PROP_14', 'PROP_15', 'PROP_16', 'PROP_17', 'PROP_18', 'PROP_19', 'PROP_20', 'PROP_21', 'PROP_22', 'PROP_23', 'PROP_24', 'PROP_25', ]; //ищем IBLOCK_ID по CODE $CatalogiblockId = Helper::getIblockIdByCode("catalog"); foreach ( $properties as $property_code) { //ID свойства по CODE $PROP_ARTICLE_ID = Helper::getIblockPropIDByCode($property_code, $CatalogiblockId); $props['PROPERTY_'.$PROP_ARTICLE_ID ]= ['data_type' => 'string']; } $props['IBLOCK_ELEMENT_ID']= ['data_type' => 'integer']; $entityProps = Bitrix\Main\Entity\Base::compileEntity( 'PROPS', $props, [ 'table_name' => sprintf('b_iblock_element_prop_s%s', $CatalogiblockId), ] ); $select = [ 'ID', 'IBLOCK_ID', 'NAME', 'SORT', 'IBLOCK_SECTION_ID', 'DETAIL_PICTURE', 'PROPS' ]; $result = \Bitrix\Iblock\ElementTable::getList([ 'select' => $select, 'filter' => [ 'IBLOCK_ID' => $CatalogiblockId, ], 'runtime' => [ 'PROPS' => [ 'data_type' => $entityProps->getDataClass(), 'reference' => [ '=this.ID' => 'ref.IBLOCK_ELEMENT_ID', ], ], ], ]); \Bitrix\Main\Diag\Debug::endTimeLabel('f_time1'); $f_time=\Bitrix\Main\Diag\Debug::getTimeLabels(); echo "Кол-во эл-в выборки D7: ".$result->getSelectedRowsCount().PHP_EOL; echo "Время выполонения выборки D7: ".round($f_time['f_time1']['time'], 4, PHP_ROUND_HALF_UP).PHP_EOL;
И результат который очень порадовал

Т.к. получить свойства элементов напрямую нельзя, то сначала создаем сущность $entityProps которая содержит поля из таблицы b_iblock_element_prop_s.IBLOCK_ID где наименование столбцов свойств являются PROPERTY_.ID (ID свойства, поэтому надо их сначала получить по CODE, или добавить посмотрев в свойствах элементов)
Затем мы делаем выборку присоединив сущность к выборке.
Данный пример показывает не только как ускорить выполнение выборки на больших массивах, но и может помочь существенно снизить нагрузку на БД даже в обычных каталогах. Где на первый взгляд время выборки не столь существенно. Ведь на выборке в 30 товаров из каталога разница во времени будет не существенной, и на скорость загрузки это будет влиять минимально.
Но если взять во внимание существенное снижение количества запросов к БД - это может существенно снизить нагрузку на БД. И проведя простую оптимизацию - вы добьетесь потрясающих результатов.
Итог:
Добились ускорения выполнения скрипта с 1333 сек до 0,552, т.е. почти в 700 раз.
Снижение количества запросов к БД с более 50 000. до ОДНОГО.
