Заказ с Kwork. Магазин запчастей для ноутбуков mekonopsis.ru: 791 834 карточки товара, база больше семи гигабайт, в одной категории 145 612 позиций. Владелец жаловался на медленные страницы и на то, что со временем сайт тормозит всё сильнее.
Замеры сняты с самого сервера, медиана из трёх заходов подряд: так в цифру не попадает канал до посетителя. Шкала общая, 100% = 1132 мс.
Сначала я думал на базу: в категории 145 тысяч товаров, сортировка идёт по вычисляемым выражениям, и такой запрос в логе медленных однажды выполнялся 45 секунд. Но настоящая статья расхода нашлась в другом месте.
Имя файла кеша содержало срок годности: cache.ключ.когда_протухнет.
Чтобы прочитать одну запись, движок обходил весь каталог функцией glob.
При 3 566 файлах одно чтение стоило 3,54 мс против 0,007 мс у прямого обращения,
а страница каталога делает таких чтений около 144. Полсекунды на ровном месте — и чем
больше кеша накапливалось, тем медленнее работал сайт. Отсюда же жалоба «со временем
тормозит». Перенёс срок годности внутрь файла, имя стало предсказуемым.
Тот же кеш пытался удалять файлы, которых уже нет. Журнал на 12 МБ состоял из этого почти целиком и прятал остальные ошибки. После правки новых записей нет.
Синхронизация начиналась со строки UPDATE oc_product SET quantity = 0
без условия — 791 834 строки при каждом запуске, и всё это время магазин показывал
нули. Заменил одним запросом, который трогает только изменившиеся строки: на живых
данных это 5 строк вместо 800 371.
Дубликаты начала первичного ключа, точные копии соседей и индексы с одним значением на всю таблицу. В таблице адресов каждая запись обновляла шесть индексов вместо двух — отсюда и «туча долгих запросов» при синхронизации тысяч товаров.
По 12 товаров на страницу давали 12 135 страниц в одной категории, у страниц со второй и дальше не было canonical, а номер за пределом диапазона отдавал 200 вместо 404. Робот обходил всё это и получал по семь секунд на глубоких страницах. Поправил robots.txt и карту сайта, поднял число товаров на странице.
В корне лежали файлы, которые мог скачать любой, а папка импорта позволяла запустить тяжёлую пересинхронизацию каталога простым переходом по адресу. Закрыл, ничего не удаляя: только переименование и правила доступа, любой шаг откатывается одним движением.
Через два часа после правок на сайт пришёл ИИ-краулер и начал качать каталог: 70 тысяч запросов в час вместо обычных трёх-пяти тысяч, около двадцати в секунду, и так несколько часов подряд. Магазин это выдержал: медианы времени сборки остались теми же, что в таблице выше. До правок такой поток сложил бы сайт. Краулеру я выставил задержку в robots.txt, поисковые системы не трогал — они приводят покупателей.
Синхронизация с МойСкладом по API стоит на кроне: 18 636 позиций, 431 425 карточек находят свой артикул, полный проход 78 секунд. Осталось включить оттуда же цены — это ждёт, пока владелец заведёт отдельный тип цены под сайт.