Скачать CV
Назад к бизнес-кейсам

Данные

Locale-slab вместо JSON на 2 МБ

Три локали в одной JSON-колонке раздували каждый листинг. Упакованный slab в Memcached срезал кучу воркера вдвое.

В каталоге было пятьдесят тысяч SKU и три локали. У каждой строки title и description лежали JSON-объектом с en, ru и de. Листинг из сорока позиций вытаскивал из MySQL 2 МБ JSON, потом json_decode() строил дерево в воркере, потом Vue SSR получал то же дерево ещё раз в payload страницы.

Запрос просил русский. Воркер всё равно выделял английский и немецкий. Это расход, который видно в RSS, и расход, который видно в TTFB.

Проблема была в том, что сетку держали как документное хранилище. Посетители ждали decode и HTML с двумя ненужными словарями. Нужна была одна локаль на проводе и упакованное значение, которое воркер режет без json_decode().

Заголовок со смещениями, затем три среза локалей. PHP читает один срез через substr().
Заголовок со смещениями, затем три среза локалей. PHP читает один срез через substr().

Slab

Slab это одно значение Memcached на страницу листинга. В начале заголовок: magic, число элементов и таблица смещений. Дальше три упакованных области, по одной на локаль. Элемент это id, slug, price и title с длиной в префиксе. Без JSON. Без Eloquent. Читатель, которому нужен ru, прыгает на смещение ru и проходит сорок записей.

Сборка slab не бесплатная, поэтому её нет на запросе. Запись в MySQL кладёт id SKU в Redis-множество catalog:dirty. Job Horizon снимает это множество, пересобирает страницы с этими id и делает SET новых slab. Читатели job не ждут. Они держат предыдущий slab, пока не придёт новый.

MySQL источник правды. Redis помечает грязные id. Horizon пишет в Memcached.
MySQL источник правды. Redis помечает грязные id. Horizon пишет в Memcached.

Что Vue SSR перестал возить

Раньше в SSR-payload попадал полный локализованный объект каждой плитки. После slab сервер рендерит одну локаль и сериализует только её. Клиент гидрирует те же строки. Смена языка это навигация, а не второй словарь в памяти.

  • Payload листинга упал примерно со 180 КБ HTML до примерно 40 КБ на те же сорок плиток.
  • json_decode() ушёл с горячего пути.
  • MySQL потерял JSON extract в запросе листинга. Покрывающего индекса по id, slug, price хватает.

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

Какой опыт из этого

Листинг это диапазон скаляров. JSON удобен для записи. Держать оба на публичном пути значит везти 2 МБ ненужных локалей в воркер и в документ.

Пересборку с запроса снимать. Читатели держат последний slab. Грязные id в Redis дешевле json_decode() на каждый хит.

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

Назад к бизнес-кейсам