Наша высокопроизводительная многогранная поисковая система NoSQL с управляемой навигацией, 1search (более подробная информация здесь), обслуживает автомобильный трафик в среднем за 9 мсек. Это быстро. Однако средний может вводить в заблуждение. Например, если большинство ответов значительно быстрее, среднее значение 9 мс может указывать на проблему. Возможно, некоторым частям поисковой системы присуща некоторая медлительность? Фактически, эта медлительность выявила себя только при взгляде на различные графики процентилей.

Вполне естественно, что «средний» показатель вводит нас в заблуждение, но особенно после того, как мы получили 15-кратное «среднее» улучшение скорости (история для другого дня) для некоторых сайтов, на которые мы перешли со старой поисковой системы. Мы не поступили бы мудрее, если бы у нас не было множества доступных метрик AWS Cloudwatch, которые позволяли бы нам запускать нашу систему предупреждений о снижении производительности услуг через PagerDuty при нарушении порогового значения метрики Cloudwatch.

К нашему удивлению, среднего значения в 9 мс оказалось недостаточно, чтобы не дать устрашающему (но очень полезному) PagerDuty звонить на наши телефоны. Его частота значительно снизилась по сравнению со старой поисковой системой, но не настолько, чтобы полностью ее остановить. Именно это подтолкнуло нас к расследованию.

Как выяснилось, некоторые типы запросов сильно нагружали процессор и требовали много времени для обработки. Это были запросы управляемой навигации и уточняющего фильтра. Они показывают вам все варианты, позволяя фильтровать их, чтобы получить более узкий набор результатов. Это были тяжелые запросы ЦП, потому что они должны были предоставить клиентскому интерфейсу много информации.

Например, если вы откроете уточнение Марка на веб-сайте продаж автомобилей, вы получите список марок, который заполняется в режиме реального времени с указанием их количества, а также индикатор, показывающий вам, есть ли какие-либо модели для дальнейшей доработки на основе на вашем текущем фильтре. На других наших сайтах, таких как Chileautos, мы показываем их на самой панели уточнения без необходимости открывать отдельный диалог. Помимо уточняющих ссылок, мы также предоставляем оптимизированные для SEO URL-адреса, чтобы поисковые системы в Интернете, такие как Google, могли сканировать и индексировать наши сайты для привлечения органического трафика.

Сканирование Google глубоких ссылок было основной проблемой. С количеством URL-адресов, которые роботы-сканеры могут посещать с помощью навигации и уточнений, значительно превосходят по количеству обычные операции, которые также выполняли бы посетители-люди, такие как разбиение на страницы и сортировка, «среднее» время ответа значительно подскочило из-за тяжелых запросов ЦП. Фактически, каждый раз, когда появляется больше запросов на навигацию и уточнение даже от посетителей-людей, мы видим такое же увеличение «среднего» показателя, особенно если было много элементов с множественным выбором. Например, кто-то мог выбрать все 50 моделей разных производителей. Это также может быть скребок экрана, который мы не смогли заблокировать, особенно если в запросах было выбрано более 300 моделей, но это тема для другого дня. Тем не менее, именно эти факторы исказили нашу «среднюю» фигуру.

Больше всего проблем вызывал Google, точнее Googlebot. Менее года назад, в июле 2020 года, Google поручил компании Deloitte провести исследование (Миллисекунды составляют миллионы) стоимости задержки во время ответа сайта и сочли это достаточно затратным, чтобы впоследствии изменить свой алгоритм ранжирования, чтобы дать бонусные баллы для сайты с быстрым временем отклика. К сожалению для нас, хотя наша поисковая система достаточно быстра для большинства типов запросов, именно те, которые сканируют роботы Google, мы медленно выполняем. Для некоторых комбинаций навигации и уточнений мы можем работать с 800 мс и более. В сочетании с визуализацией HTML и т. Д., Мы оказались далеко за пределами зеленого региона Google в отношении производительности сайта для более глубоких ссылок.

В то же время роботы Google были чрезвычайно агрессивны в своем поведении сканирования, параллельно отправляя более 2000 уникальных IP-адресов для сканирования нашей навигации и уточнений, пропуская быстрые операции, такие как разбиение на страницы и сортировка. Вдобавок ко всему, они, казалось, проверяли нашу стратегию разрушения и масштабирования, оставаясь совершенно беззвучными в течение нескольких часов и возвращаясь, чтобы поразить нас без какого-либо увеличения. Поскольку эти дорогостоящие запросы стоят на два порядка больше, чем «средние» запросы, нам пришлось вручную избыточно выделять ресурсы и запускать огромное количество инстансов AWS.

Роботов Google легко остановить, заблокировав их, но это неизбежно повредит нашему SEO-рейтингу и, следовательно, нашему органическому трафику, так что это был не вариант.

Какое решение?

Оптимизация использования

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

Мы посмотрели, как используется функция навигации и уточнения. При продажах автомобилей интерфейс открывает модальное диалоговое окно для представления выбранных вариантов. Обычно, когда диалоговое окно не отображается, нам нужно только показать выбранные узлы, чтобы пользователи могли удалить их текущий выбор. Таким образом, мы можем отложить дополнительный выбор (например, показ всех «марок») до отдельного запроса и только для одного родительского узла за раз. Другими словами, нет необходимости выполнять ту работу, которая в любом случае не будет использоваться клиентской частью. Например, если мы показываем список «производителей», нет необходимости заполнять список «местоположений» в том же запросе. Это оказало наибольшее влияние на производительность, но потребовало небольших изменений во внешнем интерфейсе. Мы перешли с 800 мс на 400 мс.

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

Оптимизация кода первая

Максимально избегайте записи в память - создавайте память только для хранения наших временных результатов, когда они нам нужны. Большая часть функций навигации и уточнения включает обход дерева. Мы сохраняем промежуточные результаты по мере углубления, потому что они нужны дочерним узлам. В листовых узлах нам нужно только вычислить «счетчик», чего можно достичь без предварительного сохранения другого промежуточного результата. Если мы прекратим преждевременно сохранять временные результаты, проверив, нужно ли нам сначала пройти дальше, мы можем добиться большого выигрыша.

Как и во всех современных архитектурах ЦП, оперативная память (ОЗУ) на порядки медленнее, чем арифметические операции ЦП. Как оказалось, это сильно повлияло на производительность глубоких ссылок, для которых мы оптимизировали, потому что большинству узлов не нужно хранить промежуточные результаты. Мы перешли с 400 мс на 200 мс.

Оптимизация кода 2

Преобразование массивных деревьев выражений в строку при сериализации в JSON также оказалось дорогостоящим. Каждое дерево выражений лишь немного отличается от своего родителя. Например, если представлен список «Марок» и вы выбрали Audi, добавление узла означает, что вы также выберете BMW в дополнение к Audi. Удаление узла означает удаление BMW из вашего выбора Audi и BMW. Как видите, единственное, что меняется в дереве выражений, - это BMW. Однако вместо просто Audi представьте, что у вас есть множество различных вариантов. В этом случае вы будете повторять одну и ту же работу снова и снова для каждого узла, даже если знаете, что большинство из них не меняется.

e.g.

(And.State.Victoria ._. Year.range (2018 ..) ._. GenericGearType.Automatic ._. Odometer.range (.. 60000) ._. Condition.Used._. ‹More›. (Или. Make.Audi ._. Make. ‹только это изменения›.))

Метод оптимизации здесь заключался в том, чтобы сохранить базовое выражение string и манипулировать строкой для каждого узла вместо того, чтобы каждый раз преобразовывать их из деревьев выражений в строки. В сочетании с новой функцией .Net Core Span это позволило нам получить сверхэффективный способ манипулирования строками. Влияние на производительность было значительным. Мы перешли с 200 мс на 100 мс.

Оптимизация кода три

Нет SEO с множественным выбором и другими небольшими оптимизациями - мы не поддерживаем SEO-дружественные URL-адреса для операторов OR (например, выбраны как BMW, так и Audi), поэтому не пытайтесь сгенерировать его с самого начала (т. Е. Стратегия раннего выхода).

например Мы знаем, что следующее не имеет удобного для SEO представления URL.

Или.Make.Audi._.Make.BMW.

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

Полученные результаты

Навигация и уточнения 800 мс теперь 80 мс (10-кратное ускорение). Это также улучшило наше среднее время отклика с 9 мс до 3,5 мс. Это также означает, что загрузка ЦП значительно снижается, что сокращает среднее количество экземпляров, которые нам нужны для нормальной работы, более чем наполовину и намного больше, когда срабатывает робот Googlebot. Посетители-люди также могут почувствовать ускорение, особенно если вы тратите много времени на уточнение результатов поиска.

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