День 2598. #ЗаметкиНаПолях
Мы Создали Слой Кэша. PostgreSQL Показал, что Зря.
Пользователь открывает страницу свего тарифного плана, а видит старый. Он обновляет страницу, и всё исправляется. Наш показатель попадания в кэш 98%, а мы всё равно выглядели некомпетентными. В тот момент мы перестали рассматривать кэширование как функцию повышения производительности и стали рассматривать его как проблему истинности. Потому что система перестала быть медленной, зато она стала ненадёжной.
Ошибка, которая не воспроизводится
Ничего не ломалось постоянно. Приходила жалоба в службу поддержки: цифры менялись после обновления. Коллега воспроизводил это один раз, а потом больше никогда. Отдел QC спрашивал, как это протестировать, и лучшим ответом было – хз, зависит от кэша.
Слой кэширования, которым мы гордились
Мы не просто вставили Redis и стали надеяться на лучшее. Мы регистрировали промахи, измеряли частоту попаданий, использовали TTL, создали правило, что каждая запись должна удалять связанные ключи. А потом реальность посмеялась над нами.
Одной конечной точке требовались данные пользователя, разрешения, метка сегмента и флаги функций. Каждая часть имела свой ключ и
свой TTL, и все части обновлялись с разной частотой. Мы сшили их вместе во время чтения и заметили, что это работает достаточно быстро - первая ложь. Вторая ложь: высокая частота попаданий в кэш создавала у нас чувство безопасности. Штука в том, что это может скрывать проблемы в БД.
Часть, о которой никто не говорит
Кэширование не просто увеличивает скорость. Оно добавляет новое поле для ошибок. Сложность не в хранении байтов. Сложность в определении, что означают эти байты, когда мир меняется. Обновление плана, изменение роли, переключение флага… В БД это отражается как факты, а в кэше… не понятно. Мы не снижали нагрузку, мы увеличивали неопределённость.
Наш ключ кэша был огромным, потому что комбинации фильтров были бесконечны. Поэтому кэш никогда не мог быть полным. При промахе кэша БД получала удар. При попадании всё выглядело хорошо, пока не начинало рушиться.
Разгадка была довольно простой:
– мы фильтровали по столбцам, которые не были проиндексированы вместе;
- мы сортировали так, что Postgres не мог сделать это дёшево;
- мы возвращали больше данных, чем требовалось конечной точке.
Маленькое изменение (добавление правильного индекса) привело к тому, что кэш стал не нужен. Не потому, что Redis плох. Потому что Postgres прекрасно справлялся.
Это решение позволило сократить нагрузку на каждый запрос, а не только на неудачные. До изменений p95 в пиковые периоды трафика находился в диапазоне 420–600 мс. Промахи кэша приводили к ответам >900 мс, и графики становились всё более «зубчатыми». После корректировки индекса и запросов p95 стабилизировался в районе 140–190 мс при той же пиковой нагрузке.
Никаких ритуалов прогрева. Никаких задач «подготовки» кэша. Никаких загадочных жалоб о том, что обновление страницы «изменяет реальность».
Самым большим достижением стала не скорость. Дело в том, что ответ перестал меняться, когда пользователь моргнул. Наша неудача не в добавлении кэширования. Она в том, что мы сделали кэш путём истины по умолчанию. Мы приучили себя больше доверять частоте попаданий, чем корректности.
Правило кэширования
Если вы не можете объяснить правило инвалидации одним предложением, вы не кэшируете, вы гадаете. Зачастую узким местом является не БД, а форма запросов и объём запрашиваемых данных. Кэш может какое-то время скрывать это. А потом это приводит к ухудшению качества работы, не за счёт задержки, а за счёт снижения уверенности.
Источник: https://medium.com/@maahisoft20/we-built-a-cache-layer-postgresql-made-it-embarrassing-9762f055f21f
Post #3120
2.05K
- 👍 13