Main > Magento > Magento 2: Как работает индексация

Magento 2: Как работает индексация

02.06.2019 0 comments » Views: 2,296

Magento 2

Для решения одной из проблем, пришлось разобраться с индексацией в Magento 2. В этой статье расскажу о том, что мне удалось узнать.

Индекс и индексатор

В Magento 2 довольно сложная система хранения продуктов, категорий и связанных с ними данных, таких как цены товаров, цены групп товаров, скидки и т.д. Для того, чтобы ускорить выборку этих данных, используются индексные таблицы. Без индексации, пришлось бы все это рассчитывать "на лету", что значительно отразилось бы на производительности. Индексные таблицы представляют собой набор данных, оптимизированный для быстрой выборки. Такой подход позволяет заранее произвести все нужные расчеты, например учесть скидки и в дальнейшем использовать уже обработанные данные вместо того, чтобы делать это для каждого запроса.

За создание индексов отвечают индексаторы. По сути, это классы отвечающие за то, чтобы обработать данные и сохранить их в нужном формате.

  • Индекс = это таблица с оптимизированным для выборки набором данных.
  • Индексатор = класс который оптимизирует данные и записывает их в таблицу индекса

Способы обновления данных

Предусмотрено 2а способа обновления данных индекса

  • Полный реиндекс - используется когда необходимо пересчитать все данные индекса.
  • Частичный реиндекс - используется при обновлении конкретного значения индекса.

Теперь поговорим про каждый из способов подробнее.

Полный реиндекс

Для запуска полного реиндекса используется крон задача indexer_reindex_all_invalid, описанная в файле

/vendor/magento/module-indexer/etc/crontab.xml

Работает это так. Некоторые запускаемые процессы могут пометить весь индекс как "invalid". Делается это установкой статуса invalid в таблице indexer_state. Например, некоторый процесс поставит статус invalid для индексера catalog_product_flat

Magento 2: Статус invalid для индексера

В админке это будет выглядеть так

Magento 2: индексер atalog_product_flat

В случае, если magento крон установлен, то такой индекс будет обнаружен задачей indexer_reindex_all_invalid и отправлен на полную переиндексацию.

Возможности выполнить задачу indexer_reindex_all_invalid вручную средствами magento нет. Однако на помощь придет утилита N98-Magerun: https://github.com/netz98/n98-magerun2 . С ее помощью можно запустить эту задачу вот так:

а вот так посмотреть список всех доступных задач

Другой способ запустить переиндексацию, это выполнить консольную команду указав конкретный индексатор

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

В результате вы получите сообщение, что все индексы были перестроены

Просмотреть статус индексов из консоли можно так

результат

Тут

  • Title = название индекса
  • Status = статус индекса указывающий на то, требуется обновление или нет
  • Update On = тип частичного реиндекса (см. следующий раздел)
  • Schedule Status = Статус индексатора, указывающий на то, был ли он запущен или нет.
  • Schedule Updated = Время обновления Schedule Status

Просмотреть id индексаторов из консоли можно так

выведет

Подведем итоги по командам: php bin/magento <команда>

  • indexer:status = покажет какие  индексы требуют обновления
  • indexer:info = покажет id всех индексаторов
  • indexer:reindex = переиндексировать все индексы (все индексаторы будут запущены по очереди)
  • indexer:reindex <id-индексатора> = запустить конкретный индексатор

Частичный реиндекс

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

В Magento 2 существует два типа частичного реиндекса:

  • Update on Save = данные обновляются в момент сохранения изменений
  • Update by Schedule = измененные данные обновляются по расписанию

Для каждого из индексов, можно задать свой тип обновления. Например, индекс цен обновлять "по расписанию", а индекc категорий обновлять "в момент сохранения". Изменить эти настройки, можно в админке: Magento Admin > System > Tools > Index Management, затем отметить чекбоксами нужные индексы и выбрать нужный пункт в выпадающем списке "Actions"

Magento 2. Тип индексации

Основное отличие двух способов, это производительность при перестроении индексов. Разберем каждый из способов подробнее.

Update on Save - используется в случае, если при сохранении не будет необходимости обновлять большое кол-во элементов. Например, у вас 3 категории и 50 товаров, тогда смело можно использовать этот способ. Кроме того, данный способ позволяет практически моментально синхронизировать изменения с витриной.

Update on Schedule -  используется в случае, когда у вас огромное кол-во товаров или требуется перестроить индекс для большого кол-ва записей. Работает это так: id измененных записей накапливаются в отдельной таблице с постфиксом _cl, по расписанию запускается процесс индексации, который поочередно обновляет эти записи.

Конфигурация расписания Update on Schedule

Как вы уже знаете, индексы для которых указан тип индексации "Update on Schedule" будут запущены по расписанию.

Индексаторы запускаются в отдельной крон-группе: index. Настройки для данной группы задаются в админке, в разделе:

Magento 2: Настройки крона для группы indexer

Magento 2: Настройки крона для группы indexer

Расписание для индексаторов задается в файлe /vendor/magento/module-indexer/etc/crontab.xml

Подробнее про настройки и то как работает планировщик можно прочитать тут: Magento 2: Планировщик задач

Тут мы видим 2 крона, связанных с частичным индексированием:

  • ndexer_update_all_views - индексирует записи из таблиц имя-таблицы_cl
  • indexer_clean_all_changelogs - очищает старые записи из таблиц имя-таблицы_cl

Прежде чем продолжать нам нужно разобраться с тем что такое MView и как это используется в Magento

MView

Mview это сокращение от Materialized Views или на русском Материализованное представление.

Материализо́ванное представле́ние — физический объект базы данных, содержащий результат выполнения запроса.
Материализованные представления позволяют многократно ускорить выполнение запросов, обращающихся к большому количеству (сотням тысяч или миллионам) записей, позволяя за секунды (и даже доли секунд) выполнять запросы к терабайтам данных. Это достигается за счет прозрачного использования заранее вычисленных итоговых данных и результатов соединений таблиц. Предварительно вычисленные итоговые данные обычно имеют очень небольшой объем по сравнению с исходными данными. Целостность данных в материализованных представлениях поддерживается за счёт периодических синхронизаций или с использованием триггеров.
Впервые появились в СУБД Oracle.

Если упростить, то суть в том, чтобы сделать выборку по сложному запросу и сохранить результаты этой выборки в виртуальной таблице. Далее, вы можете делать запросы по этой таблице, точно так же как по обычной. Данные в виртуальной таблице обновляются посредством полного обновления или триггеров.

Основное отличие View от Materialized View в том, что в первом случае виртуальная таблица всегда содержит актуальные данные, а в случае с Materialized View кешированные. Как результата Materialized View работает быстрее, хоть и требует дополнительных действий по обновлению данных.

MySQL не имеет поддержки Materialized View, поэтому вместо виртуальных таблиц используются реальные. В Magento это индексные и flat таблицы.

Как работает обновление данных

Прежде всего, нужно пойти в админки и выбрать для определенного индекса режим работы "Update by Schedule"

Magento 2 индекс Update by schedule

Magento 2 индекс Update by schedule

В момент изменения режима работы, в базе данных будет создана таблица с постфиксом _cl (у нас это catalog_product_flat_cl), а так же будут добавлены тригеры, отвечающие за то, чтобы сохранять change log-и, или проще говоря идентификатор того, что определенное значение в таблице было обновлено. Тригеры навешиваются на CRUD операции в базе, поэтому добавляется сразу несколько тригеров. В случае, если сущность является EAV, то тригеры будет сгенерированы для каждой из участвующих таблиц. Т.е. в нашем примере, будет добавлено 27 тригеров, для 3х операций (INSERT, UPDATE, DELETE) в 9 таблицах:

Просмотреть тригеры можно такой командой

пример вывода

Magento 2 тригеры

Если просмотреть запросы, мы заметим что все что делают эти тригеры, это добавление id сущности в таблицу с постфиксом _cl. Т.е. каждый раз при обновлении записи, в таблицу с постфиксом _cl будет добавлено новое значение. Если посмотреть структуру таких таблиц, то увидим что там сохраняется всего два значения version_id и entity_id. Поле version_id - является primary key с автоинкрементом, entity_id - id сущности которая поменялась.

Magento 2: Структура cl таблицы

Magento 2: Структура cl таблицы

Подведем промежуточный итог: при включении режима обновления индексов "Update by schedule" создается таблица с постфиксом _cl, а так же добавляются тригеры на изменения данных сущности, которые наполняют эту таблицу. Как только мы обновляем какой-то товар в админке, в таблицу catalog_product_flat_cl добавляется новое значение.

Далее, нам нужно обратить внимание на таблицу mview_state, которая содержит информацию о том какие индексы работают в режиме "Update by schedule", их статус и главное, последнее обработанное значение из _cl таблицы в поле version_id.

Magento 2: Таблица mview_state

Теперь мы знаем что у нас есть catalog_product_flat_cl таблица где собраны id товаров которые обновились, а так же что в mview_state сохраняется последняя версия из этой таблицы, по которой изменения были внесены в индексную таблицу. Звучит запутанно, поэтому, лучше приведу пример, как это обновление работает.

  • Каждую минуту запускается magento планировщик (про планировщик написано тут)
  • Планировщик запускает индексатор, который называется indexer_update_all_views
  • Этот индексатор, перебирает поочередно все записи имеющие mode=enabled из таблицы mview_state
  • Далее он берет из этой таблицы mview_state.version_id и ищет в таблице catalog_product_flat_cl все version_id которые больше этого значения.
  • Таким образом индексатор узнает какие записи обновились с момента последней обработки, а соответственно их нужно обновить в индексе
  • Производит обновление индексов для этих записей
  • Обновляет значение mview_state.version_id , на последнее обработанное
  • Завершает работу

Как видите, все построено достаточно просто, по сути у нас есть список записей который были обновлены, и маркер последней обработанной индексатором записи. Берем все записи которые больше этого маркера, обновлем их и перезаписываем маркер.

Если присмотреться к списку шагов, вы не найдете шага с удалением обработанных записей из cl таблицы. Действительно, индексатор indexer_update_all_views не удаляет записи, которые он обработал, только меняет version_id на последний в таблице mview_state. За очистку старых данных в cl таблице отвечает другая задача, а именно indexer_clean_all_changelogs.

Объявления обоих индексаторов и связанных с ними классов находятся в файле: /vendor/magento/module-indexer/etc/crontab.xml

Под капотом indexer_update_all_views

Рассмотрим стек вызова индексаторов при запуске задачи indexer_update_all_views.

Я пока не определился с нормальным переводом view, view processor, indexer в используемом контексте, поэтому все три сущности буду называть индексатор, чтобы не путать терминами типа "вид", "процессор вида" и т.д.

Сперва запускается стандартный cron:run который согласно расписания определенного тут

/vendor/magento/module-indexer/etc/crontab.xml

добавляет соответствующие задачи, в таблицу cron_schedule. Далее, запуск этого же крона процессит записи из этой таблицы и в случае если запланированная дата запуска настала, запускает их, таким образом происходит вызов метода

как видим тут ничего практически не происходит, поэтому обратимся к вызову находящемуся внутри метода execute

как видим и тут не очень много полезного, опускаемся еще ниже

тут все становится немного интереснее, а именно мы видим что выбираются индексаторы соответствующей группы (если она указана) и далее уже у индексаторов поочередно запускается метод update. Это означает, что если в одном из индексаторов произойдет ошибка которая прервет выполнение работы, то остальные индексаторы запущены не будут. Очередность можно получить с помощью вот такого простого скрипта, который надо положить в корневую диекторию проекта и запустить из консоли

Выполнятся следующие индексаторы сверху вниз

по-умолчанию, индексаторы имеют тип

если убрать разбиение на группы и переключение статусов, то мы увидим что тут из view берется action_class и у него вызывается метод execute с передачей внутрь id-шек которые нужно проиндексировать:

эти $action'ы - это классы определенные в файлах etc/indexer.xml , пример

вот полный список классов по-умолчанию для упомянутых индексаторов

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

 

Заключение

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

Статья получилась большая, и может содержать некоторые логические ошибки в названиях происходящих процессов, поэтому если заметите, дайте знать в комментариях.

Полезнае ссылки

Author: | Rating: 4/5 | Tags: , , , ,

Leave a Reply

Your email address will not be published. Required fields are marked *

Allowed HTML-tags: <a>, <code>, <i>, <em>, <strong>, <b>, <u>, <strike>