Как стать автором
Обновить

Кэширование в Google App Engine

Время на прочтение4 мин
Количество просмотров1.6K
Google App EngineКак вы уже знаете из предыдущей статьи, в App Engine есть множество способов хранения информации. Но многие из них весьма специфичны, и для повсеместного пользования подходят всего три: память инстанса, memcache и datastore.

Под катом вас ждёт изложение в цифрах и картинках, краткие рекомендации по кэшированию и исходные коды простого cacher'a и приложения для тестов.

Методика тестирования.


Для тестов было заведено отдельное приложение, умеющее забивать базу различныму случайными данными и считывать их различными способами (в исходниках вы так же найдёте тесты для картинок, но их я решил оставить за рамками данной статьи). Запросы воспроизводились примерно в одно время с интервалом в несколько секунд, результаты записывались с помощью AppStats, после этого из статистики выбирались и скриншотились средние результаты.

Данные.


Используются результаты рандомного генератора — в каждом подразделе сперва идут результаты для одной записи с текстом примерно в 60Кб, после этого для списка в 1000 небольших записей (примерно соответствующих среднему комментарию).

Datastore.


Начнём, конечно же, с самого надёжного и самого медленного хранилища. Получаем из datastore одну запись в 60Кб методом get_by_key_name():
app engine, text from db

Выглядит неплохо — и по времени выполнения, и по потреблённым ресурсам, теперь попробуем получить выборку тысячи «комментариев»:
app engine, list from db

Вот и основная причина немасштабируемых приложений на масштабируемом App Engine — на получение данных уходит 440мс времени, а вычислительных ресурсов потребляется просто неприличное количество. Такими темпами приложение может потребить бесплатную квоту всего за пару тысяч запросов — необходимо кэшировать данные.

Примечание: 10 секунд api_cpu получены методом all().fetch(1000) на группе в 1000 записей, при выполнении GqlQuery для выборки из большего количества записей будет потреблено ещё больше ресурсов. Самое большое из виденного мной — 561 000 api_cpu_ms при добавлении в HRDS (high replication datastore) 1000 записей. Так что с большими выборками в App Engine надо работать предельно осторожно.

Memcache.


Самое универсально средство — это memcache, первым делом именно в нём рекомендуется хранить результаты выполнения запросов к datastore.

app engine, text from memcache

Получение записи происходит почти в 2 раза быстрее и требует в 2 раза меньше времени процессора. Правда, в абсолютных значениях экономия в 10мс не представляется серьёзной. Теперь посмотрим, как memcache переваривает список:
app engine, list from memcache

Здесь экономия гораздо более существенная: в 1,5 раза быстрее и в 15 раз дешевле.

Memcache + protobuf.


Memcache бесплатный и почти без ограничений, но всё же не резиновый — попробуем уменьшить размер данных в нём с помощью сериализации объектов класса db.Model в protocol buffer (как рекомендуется в этой статье). Это позволит данным дольше храниться в memcache, но станет ли лучше работать приложение?..

app engine, text from memcache with protocol buffer
app engine, list from memcache with protocol buffer
Если в первом случае разницы почти не видно, то во втором отчётливо видно повышение времени выполнения и потребляемых ресурсов на 20-25%.

Memcache + protobuf + zlib.


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

app engine, text from instanse memory with protocol buffer and zlib

Время выполнения memcache.Get уменьшилось на 1мс за счёт того, что сжатый текст передаётся гораздо быстрее. Впрочем, всё накопленное преимущество теряется при распаковке данных.

app engine, list from instanse memory with protocol buffer and zlib
На большом количестве маленьких записей нет и такого преимущества, лишь прирост времени и ресурсов примерно на 5%.

Local memory.


Вот мы и добрались до крайнего средства повышения производительности — памяти инстанса. Кроме множества недостатков (у каждого инстанса своя память, памяти этой всего 50Мб, до предельного возраста в 9000 запросов инстансы доживают крайне редко) у него есть главное достоинство — скорость.
app engine, text from instanse memory
Как обычно, для одной записи экономия несущественна, со списком же совсем другое дело:
app engine, list from instanse memory
Время выполнения уменьшилось в 100 раз, а вычислительных ресурсов теперь не требуется вообще — хороший аргумент для использования памяти инстанса для кэширования всего подряд.

Но не будем забывать про недостатки:
  1. У каждого инстанса своя память и, соответственно, свой кэш.
  2. Памяти всего 50Мб.


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

Local memory + protobuf.


app engine, text from instanse memory with protocol buffer
app engine, list from instanse memory with protocol buffer
Для одной записи колебания времени практически в пределах погрешности, однако на списке заметен серьёзный симптом: время и ресурсы для памяти инстанса и memcache практически совпадают. Вот таким нехитрым образом сжатие способно уничтожить прирост производительности.

Local memory + protobuf + zlib.


И для порядка, получение данных из памяти инстанса с применением zlib:

app engine, text from instanse memory with protocol buffer and zlib
app engine, list from instanse memory with protocol buffer and zlib
Чуда не произошло — получение списка по-прежнему занимает много времени и потребляет много ресурсов.

Выводы.

  • Небольшие одиночные объекты зачастую нет смысла кэшировать — лучше брать объект по ключу или имени из базы, чем усложнять код ради 10мс. Фреймворк или неоптимальный алгоритм могут потреблять больше.
  • Сериализацию и сжатие данных следует применять только для memcache, данные в памяти инстанса лучше хранить в готовом к употреблению виде.
  • Память инстанса — отличное место для хранения часто запрашиваемых и редко изменяемых данных. Если Ваше приложение — небольшой сайт с количеством данных в пределах 50Мб, то можно смело использовать память инстанса для всех «тяжёлых» запросов.

Код приложения.


Скачать код тестового приложения вы можете здесь. Кэшер из этого архива в своих проектах лучше не использовать, вместо него лучше взять отсюда версию, оптимизированную с учётом полученных данных.
Теги:
Хабы:
Всего голосов 42: ↑37 и ↓5+32
Комментарии21

Публикации

Истории

Ближайшие события

15 – 16 ноября
IT-конференция Merge Skolkovo
Москва
22 – 24 ноября
Хакатон «AgroCode Hack Genetics'24»
Онлайн
28 ноября
Конференция «TechRec: ITHR CAMPUS»
МоскваОнлайн
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань