Pull to refresh

Comments 12

Заменить 50к запросов на один - это хорошо, конечно, но как оно в базе выглядит? Может, он там full table scan делает теперь на каждом запросе и под нагрузкой все ляжет.

И на промежуточный вариант тоже было бы интересно посмотреть.

[offtop]

А что это за жуткий микс $snake_case, $camelCase, $StudlyCase, а потом ещё $SNAKE_CASE_CAPS. В Битрикс так принято?

[/offtop]

Причем тут Битрикс? Названия переменным программист придумывает.

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

Идёт девушка – видит - парень косит траву в противогазе:

- Ты что, с ума сошёл - зачем противогаз надел?

- Я комсомолец - не могу без трудностей...

- Кончай фигней страдать, пошли лучше переспим.

- Хорошо - но только в гамаке и стоя...

D7 ORM в большинстве случаев всегда повышает производительность, потому что заставляет подумать разработчика о том, как и какие данные он получает из БД.

Пару узких мест, которые я вижу в вашем решении:

1) Как я понял, вы используете иб 2 версии, соответсвенно, на уровне бд есть две таблицы для значений свойств b_iblock_element_prop_s и b_iblock_element_prop_m , в первой хранятся свойства с одиночными значениям, в последней множественные значения свойства. Обдумывали решение для свойств с множественными значениями?

2) Helper::getIblockPropIDByCode($property_code, $CatalogiblockId); -- вижу как запрос в бд, тоже в цикле. В условиях отсутствия кеша - это вызывает проблему N+1

3) Bitrix\Main\Entity\Base::compileEntity -- я могу ошибаться, но метод достаточно прожорлив, для использования в скрипте в фоне, можно пренебречь, но для выполнения кода на хите, я бы подумал в преимуществе такого подхода. Рассматривали ли альтернативы? Почему отказались от обычного join? (при условии, что вы используете только одиночные свойства)

4) Я бы посмотрел в строну пагинации, потому что объем данных, думаю, будет у вас расти. Вероятно у вас есть фильтры для выборки, которые тоже оказывают влияние на производительность запроса.

Можно пример обычного join?

  1. не знаю насколько это кошерно....

while ($arItem = $obItems->fetch()) {
   
        $result[$arItem['ID']] = getItemRes($arItem);
   
}

function getItemRes($item)
{
    $res = [];
    foreach ($item as $index => $row) {

        if (!empty($row)) {
            $tmp = unserialize($row);

            if ($tmp != false) {
                if (!empty($tmp['VALUE'])) {
                    $res[$index] = $tmp['VALUE'];
                }
            } else {
                $res[$index] = $row;
            }
        }
    }

    return $res;
}
  1. Это просто получить ID свойств по CODE. их в принципе можно и руками прописать. просто привык не пользоваться ID, т.к. на dev и на prod - ID могут различаться

  2. Т.к. скрипт отрабатывает в фоне, альтернативу не рассматривал. Целей оптимизации этого скрипта добился. В качестве академического интереса, можно подумать

  3. Опять же это уже больше исследовательский момент. Но так же можно будет попробовать. Можно и на чистом MySqQL написать.

Спасибо за содержательный комментарий

Мой каммент ниже отвечает на часть ваших вопросов, сначала написал потом прочитал) И да это все для свойства версии 2. Кстати у одного ИБ могут и те и другие свойства, но это вообще мрак, и тот кто хочет с этим заморачивать точно должен знать что он делает и зачем)

Чуть добавлю.
Для получения датакласса одиночных свойств

public function getSinglePropsDataClass($iblockId) {
		$className = 'SProps' . $iblockId;

		if (class_exists($className . "Table")) {
			return $className . "Table";
		}

		$props = \Bitrix\Iblock\PropertyTable::query()
			->setSelect(["ID", "MULTIPLE", "PROPERTY_TYPE"])
			->where("IBLOCK_ID", $iblockId)
			->where("MULTIPLE", "N")
			->where("VERSION", 2)
			->exec()->fetchAll();

		$sProps = [];
		foreach ($props as $prop) {
			$key = "PROPERTY_" . $prop["ID"];
			$type = $prop["PROPERTY_TYPE"] == \Bitrix\Iblock\PropertyTable::TYPE_NUMBER ? 'float' : 'string';
			$sProps[$key] = ['data_type' => $type];
		}
		$sProps['IBLOCK_ELEMENT_ID']= ['data_type' => 'integer'];

		$entitySProps = \Bitrix\Main\Entity\Base::compileEntity(
			$className,
			$sProps,
			['table_name' => sprintf('b_iblock_element_prop_s%s', $iblockId)]
		);

		return $entitySProps->getDataClass();
	}

Ну и для множественных

	private function getMultiplePropsDataClass($iblockId) {
		$className = 'MProps' . $iblockId;

		if (class_exists($className . "Table")) {
			return $className . "Table";
		}

		$entityMProps = \Bitrix\Main\Entity\Base::compileEntity(
			$className,
			[
				'ID' => ['data_type' => 'integer'],
				'IBLOCK_ELEMENT_ID' => ['data_type' => 'integer'],
				'IBLOCK_PROPERTY_ID' => ['data_type' => 'integer'],
				'VALUE' => ['data_type' => 'string'],
				'VALUE_ENUM' => ['data_type' => 'string'],
				'VALUE_NUM' => ['data_type' => 'float'],
				'DESCRIPTION' => ['data_type' => 'string'],
			],
			['table_name' => sprintf('b_iblock_element_prop_m%s', $iblockId)]
		);

		return $entityMProps->getDataClass();
	}

Еще заметил что работа через эти таблицы на пол порядка быстрее чем работа через orm для инфоблоков, ну и вообще orm для инфоблоков еще крайне сырой, часто входит в бесконечные циклы, поглощая всю оперативку.
Про то что $objItem->GetProperties(); использовать не надо, даже особо писать не буду)
Не очень знаю уместно ли тут использовать sprintf('b_iblock_element_prop_m%s', $iblockId) ибо если $iblockId это число, то и париться с искейпом не надо, да и делает ли sprintf искейп... Но влом было все строчки менять)

Достаточно собрать сущность инфоблока с помощью wakeUp и вызывать getList без джойна таблицы свойств, по крайней мере, явного:

$iblock = \Bitrix\Iblock\Iblock::wakeUp($iblockId);
$result = $iblock->getEntityDataClass()::getList([
    'select' => ['ID', 'NAME', 'PROP_1.VALUE', 'PROP_2.VALUE']
]);

А дальше всё стандартно...

Sign up to leave a comment.

Articles