Ремонт, сервис, услуги » Информация » Как ускорить выборку в 1с Битрикс в 20 раз




Как ускорить выборку в 1с Битрикс в 20 раз

Автор: addministr от 9-05-2022, 20:21

Категория: Информация



Задача - сформировать фид для выгрузки.Дано:
кол-во товаров более 50 000
Товары выгружаются с пользовательскими свойствами
Результат эксперимента
ускорение в 40 разДовольно частая задача - это формирование фидов, что подразумевает под собой выгрузку большого кол-ва элементов. Битрикс часто ругают за то - что при простых операциях - происходит огромное ко-во запросов к БД, я видел на проектах , как для отображения простой страницы каталога, битрикс делал более 5000 запросов к БД. Именно из- за этого проекты на Битрикс иногда очень требовательны к железу на хостинге.Современное ядро D7 позволяет значительно снизить нагрузку на БД и сильно ускоряет все процессы связанные с обращением к БД.Долгое время на одном проекте работал скрипт по формированию фида для загрузки на сторонние сервисы. Скрипт работал, никаких сбоев не выдавал, и все всех устраивало, но вот пришло время вносить изменения и в него. Первое что бросилось в глаза это то. как в этом скрипте происходила выборка товаров из каталога и добавление к ним пользовательских свойств
BitrixMainDiagDebug::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();
}
BitrixMainDiagDebug::endTimeLabel('f_time1');
$f_time=BitrixMainDiagDebug::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']
);
}
BitrixMainDiagDebug::endTimeLabel('f_time1');
$f_time=BitrixMainDiagDebug::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;

Что дало уже приемлемый результат:

Уже улучшил время выборки в 30 раз

Но, обращаться к БД в цикле - это точно нехорошо.

Поэтому решил попробовать Переписать этот код с использованием возможностей ядра 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 = BitrixMainEntityBase::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 = BitrixIblockElementTable::getList([
'select'  => $select,
'filter'  => [
'IBLOCK_ID' => $CatalogiblockId,
],
'runtime' => [
'PROPS'
=> [
'data_type' => $entityProps->getDataClass(),
'reference' => [
'=this.ID' => 'ref.IBLOCK_ELEMENT_ID',
],
],
],
]);
BitrixMainDiagDebug::endTimeLabel('f_time1');

$f_time=BitrixMainDiagDebug::getTimeLabels();

echo "Кол-во эл-в выборки D7: ".$result->getSelectedRowsCount().PHP_EOL;
echo "Время выполонения выборки D7: ".round($f_time['f_time1']['time'], 4, PHP_ROUND_HALF_UP).PHP_EOL;
И результат который очень порадовал
прирост производительности 80 разТ.к. получить свойства элементов напрямую нельзя, то сначала создаем сущность $entityProps которая содержит поля из таблицы b_iblock_element_prop_s.IBLOCK_ID где наименование столбцов свойств являются PROPERTY_.ID (ID свойства, поэтому надо их сначала получить по CODE, или добавить посмотрев в свойствах элементов)Затем мы делаем выборку присоединив сущность к выборке.Данный пример показывает не только как ускорить выполнение выборки на больших массивах, но и может помочь существенно снизить нагрузку на БД даже в обычных каталогах. Где на первый взгляд время выборки не столь существенно. Ведь на выборке в 30 товаров из каталога разница во времени будет не существенной, и на скорость загрузки это будет влиять минимально.Но если взять во внимание существенное снижение количества запросов к БД - это может существенно снизить нагрузку на БД. И проведя простую оптимизацию - вы добьетесь потрясающих результатов.Итог:
Добились ускорения выполнения скрипта с 1333 сек до 0,552, т.е. почти в 700 раз.
Снижение количества запросов к БД с более 50 000. до ОДНОГО.


Источник: https://habr.com/ru/post/664950/





Уважаемый посетитель, Вы зашли на сайт как незарегистрированный пользователь.
Мы рекомендуем Вам зарегистрироваться либо войти на сайт под своим именем.

Архив | Связь с админом | Конфиденциальность

RSS канал новостей     Яндекс.Метрика