1С-Битрикс. Массовая загрузка элементов в Highload-блоки

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!

При загрузке данных в Highload-блоки возможна ситуация, когда объем загружаемых данных очень велик.

Тем не менее, самый распространенный путь для добавления данных - их перебор в цикле, и последующее добавление построчно в Highload-блок. При большом количестве добавляемой информации этот вариант становится слишком медленным и неэффективным, хотя и остается повсеместно используемым.

На самом деле, основных пути три (привет старым добрым былинам): добавление в цикле, добавление прямым запросом и добавление массово через ORM-коллекции. 

При этом информации о записи данных в Highload через коллекции практически нет, что и побудило нас к написанию данной статьи.


Рассмотрим каждый из способов подробнее. Подчеркиваем, что речь идет именно о массовой записи.

Информационная справка

$arData - массив с данными для загрузки в Highload

В качестве примера взят реальный файл с базой городов мира, в котором содержится ~3.6 млн записей

Под использованием "штатного инструмента" подразумевается внутренний функционал 1С-Битрикс, который был создан специально для работы этой системы, обновляется разработчиками и содержит свои дополнительные проверки и способы защиты

1. Добавление в цикле

use Bitrix\Highloadblock as HL;
use Bitrix\Main\Entity;
 
$hlblockID = 1;		
$hlblock = HL\HighloadBlockTable::getById($hlblockID)->fetch();
$entity = HL\HighloadBlockTable::compileEntity($hlblock);
if (!empty($hlblock)) {
	foreach ($arData as $dataRow) {
		$dataLoad = [
			"UF_PROPERTY_1" => $dataRow["PROPERTY_1"],
			"UF_PROPERTY_2" => $dataRow["PROPERTY_2"]
		]; // массив для добавления записи в HL
 
		$entityDataClass = $entity->getDataClass();
		$entityDataClass::add($dataLoad);
	}
}

Скорость выполнения на ~3.6 млн записей => ~1.5ч

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

Преимущества данного подхода: работа со штатным инструментом, использование ООП подхода

Минусы данного подхода: низкая скорость выполнения, множество отдельных однотипных запросов

2. Добавление прямым запросом

$strFields = "UF_PROPERTY_1, UF_PROPERTY_2";
$strValues = "";
 
foreach ($arData as $dataRow) {
	$strValues .= (!!strlen($strValues) ? ", " : "")
		. "('" . $dataRow["PROPERTY_1"] . "', "
		. "'" . $dataRow["PROPERTY_2"] . "')"
	;
}
 
$DB->Query('INSERT INTO highload_table_name (' . $strFields . ') VALUES ' . $strValues);

Скорость выполнения на ~3.6 млн записей => ~5 мин

Представьте себе, что вы решили собрать пазл. Вы успешно справляетесь с задачей, и в конце решаете его склеить, чтобы повесить на стену. Но после склеивания замечаете, что один кусочек мозаики лег как-то криво, а то и вовсе этот кусочек пазла попал сюда из другой картины, или оказался пропитан ацетоном. Маленький, практически незаметный кусочек может разрушить всю картину. Да, с прямым запросом вы можете делать практически что угодно. Но, цитируя классиков, “с большой силой приходит и большая ответственность”.

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

Минусы данного подхода: работа с прямым запросом к БД может ей навредить, если в запросе будет ошибка, возможность небезопасной передачи данных или в неправильном формате (необходимо добавить дополнительную обработку, часто используется метод forSql).

3. Добавление через ORM-коллекции

use Bitrix\Highloadblock as HL;
use Bitrix\Main\Entity;
 
$hlblockID = 1;		
$hlblock = HL\HighloadBlockTable::getById($hlblockID)->fetch();
$entity = HL\HighloadBlockTable::compileEntity($hlblock);
 
$valuesCollection = $entity->createCollection();
 
foreach ($arData as $dataRow) {
	$collectionObj =
		$entity->createObject()
			->set("UF_PROPERTY_1", $dataRow["PROPERTY_1"])
			->set("UF_PROPERTY_2", $dataRow["PROPERTY_2"])
	;
	$valuesCollection->add($collectionObj);
}
$valuesCollection->save(true);

В примере в метод save передан параметр $ignoreEvents = true, который отменяет выполнение событий ORM во время добавления записей (подробнее - тут).

Результирующий запрос при этом будет выглядеть следующим образом (как и при прямом запросе):

INSERT INTO `table_name` (`UF_PROPERTY_1`, `UF_PROPERTY_2`) VALUES (1, 2), (2, 3), (3, 4), (...), (X, Y)

Скорость выполнения на ~3.6 млн записей => ~9 мин

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

Преимущества данного подхода: использование штатных инструментов, запись всех данных через 1 запрос, использование ООП подхода

Минусы данного подхода: понимание работы механизма требует повышенного уровня знаний

Вывод


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

  1. Какое количество записей вы планируете обработать,

  2. Какой запас прочности имеет ваша система,

  3. Насколько высок ваш уровень программирования (ну или ваши амбиции в его достижении)

Если записей мало (совсем немного, до 1000), то можно воспользоваться добавлением в цикле.

Если производительность системы невысока, и несколько минут могут сильно повлиять на ее работу, а при этом количество обрабатываемых записей большое - то можно производить запись прямым запросом (но стоит помнить: вариант небезопасен).

Ну а для всех остальных вариантов лучшим выходом выглядит добавление через коллекции, которое сочетает в себе использование безопасного штатного встроенного функционала и достаточно высокую скорость обработки.

Надеемся, наша статья была для вас полезна. Успешных всем проектов!

Автор статьи: @DasBinIch

Источник: https://habr.com/ru/articles/753460/


Интересные статьи

Интересные статьи

Привет, друзья! Продолжаю исследовать возможности по работе с медиа, предоставляемые современными браузерами, и в этой статье хочу рассказать вам о возможности захвата и записи медиаданных в пр...
5150CAXX, работающий под эмулированным 64K IBM PC rev. А. Параметры командной строки достаточно хорошо говорят сами за себя. Самый первый персональный компьютер IBM, IBM 5150 – который...
Доброго времени суток, друзья! Хочу поделиться опытом работы с аудио. Под «аудио» я подразумеваю HTMLAudioElement и Web Audio API. Что будем делать? Мы создадим нечто вроде плеера дл...
Доброго времени суток всем. Захотелось мне скачать всю мою музыку со ВКонтакте на флешку, как в старые добрые времена. Немного погуглив и не найдя практически ничего более менее приемлемого, я ...
Использование ленивой загрузки изображений для улучшения производительности веб-проектов — востребованная техника оптимизации. Всё дело в том, что изображения — это «тяжёлые» ресурсы, которыми пе...