Рейтинг: 1  
Новая
Предложил Ефименко Евгений 04.07.2018 07:09:22

Фасетная индексация - оптимизация кода

Дело в том, что у нас проект разделен по серверам, на одном сервере крутится php, на другом сервере полноценный MySql. Общая локальная сеть. То есть данные от MySql в PHP приходят по сети. Как только мы разделили по серверам работу сайта столкнулись с очень долго работой создания индексов.
Виной этому, при создании индекса $index->continueIndex(); происходит циклическая беготня по элементам и формирования для отдельно каждого индекс. В итоге мы получаем огромное количество запросов к MySql и задержку из-за работы протокола сети TCP.
Если бы, во время индексации не производить обмен данными с PHP, и уменьшать тем самым количество запросов, было бы гораздо быстрее и красивее.

Поясню на примере:
Сейчас создание индекса занимает: Elements: 124 Query count: 4912 Time: 9.2134962081909 сек.
Было до разъединения PHP и MySql в секундах 2.91 s при том же количестве элементов и количестве запросов.

Я прекрасно понимаю, что это наша проблема, что мы решили разделить, но теоретически ваш код тоже можно было бы оптимизировать. Даже у вас в документации и рекомендациях четко написано придерживаться правила - не делать запросов в цикле, однако сами нарушили свои рекомендации: https://dev.1c-bitrix.ru/learning/course/?COURSE_ID=32&LESSON_ID=3594

public function continueIndex($interval = 0)
{
   if ($interval > 0)
      $endTime = microtime(true) + $interval;
   else
      $endTime = 0;

   $indexedCount = 0;

   if ($this->lastElementId === null)
      $lastElementId = $this->storage->getLastStoredElementId();
   else
      $lastElementId = $this->lastElementId;

   $elementList = $this->getElementsCursor($lastElementId);
   while ($element = $elementList->fetch())
   {
      $this->indexElement($element["ID"]);   <-----------
      $indexedCount++;
      $this->lastElementId = $element["ID"];
      if ($endTime > 0 && $endTime < microtime(true))
         break;
   }
   return $indexedCount;
}
public function indexElement($elementId)
{
   $element = new Element($this->iblockId, $elementId);
   $element->loadFromDatabase();

   $elementSections = $element->getSections();
   $elementIndexValues = $this->getSectionIndexEntries($element);
   
   foreach ($element->getParentSections() as $sectionId)        <-------------
   {
      foreach ($elementIndexValues as $facetId => $values)      <-------------
      {
         foreach ($values as $value)                            <-------------
         {
            $this->storage->addIndexEntry(
               $sectionId,
               $elementId,
               $facetId,
               $value["VALUE"],
               $value["VALUE_NUM"],
               in_array($sectionId, $elementSections)
            );
         }
      }
   }

   foreach ($elementIndexValues as $facetId => $values)
   {
      foreach ($values as $value)
      {
         $this->storage->addIndexEntry(
            0,
            $elementId,
            $facetId,
            $value["VALUE"],
            $value["VALUE_NUM"],
            empty($elementSections)
         );
      }
   }
}
public function addIndexEntry($sectionId, $elementId, $facetId, $value, $valueNum, $includeSubsections)
{
   $connection = \Bitrix\Main\Application::getConnection();

   try
   {
      $connection->query("                                      <-----------
         INS ERT IN TO ".$this->getTableName()." (
            SECTION_ID
            ,ELEMENT_ID
            ,FACET_ID
            ,VALUE
            ,VALUE_NUM
            ,INCLUDE_SUBSECTIONS
         ) VALUES (
            ".intval($sectionId)."
            ,".intval($elementId)."
            ,".intval($facetId)."
            ,".intval($value)."
            ,".doubleval($valueNum)."
            ,".($includeSubsections > 0? 1: 0)."
         )
      ");
   }
   catch (\Bitrix\Main\DB\SqlException $e)
   {
      return false;
   }

   return true;
}

В этом случае необходимо собрать все элементы в кучу, обработать что надо, и потом разом воткнуть в MySql
Спасибо за внимание, возможно учтете это в разработке.
Рейтинг: 0  
На голосовании
Предложил Кирсанов Анатолий 05.04.2018 22:19:55

Нужно нормальное совмещение поиска и фильтрации

За все эти годы ничего не изменилось. Смотрел в последний раз в components/bitrix/catalog/templates/.default/bitrix/catalog.search/.default/template.php от iblock 17.5.4.

Выполняется поиск в два шага:
  1. bitrix:search.page
  2. bitrix:catalog.section

Это ненормально. Причины всего-то две:
  1. Техническая: странно собирать ИД-шки элементов, чтобы потом использовать их для второй выборки уже не из поискового индекса, а из инфоблока.
  2. Функционал: пользователи давно ждут подсветки поисковых фраз в результатах поиска, но при таком подходе ее не будет в принципе.

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

Давно уже пора научить Битрикс делать один запрос (поиск и фильтрация одновременно, учитывая фасетный индекс, если ищем по инфоблокам). Также давно пора (1С УТ такое умеет, к примеру) делать подсветку того, что искал пользователь. Так интуитивно проще оценивать релевантность.
Рейтинг: 0.2686  
На голосовании
Предложил Шихмагомедов Руслан 25.06.2015 13:30:04

Фвсетные индексы для обычных инфоблоков

Сделайте возможность использования фасетных индексов не только для торговых каталогов, но и для обычных инфоблоков.
Рейтинг: 2.3983  
На голосовании
Предложил Александр 23.06.2015 08:41:06

Фасетные индексы инфоблоков через cron

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