Это вторая часть цикла «Масштабирование Wix до 100 миллионов пользователей». Вступление читайте тут.

Когда мы только запускали Wix, был использован стек Tomcat, Hibernate и Ehcache c базой данных MySQL и фронтендом на Flash. Почему мы выбрали этот стек? Да просто потому, что у нашего первого бэкенд-разработчика уже был опыт работы с ним. Частью этой архитектуры был Ehcache – отличная кэш-библиотека для Hibernate и JVM, которая создавала абстракцию в виде карты для кэша памяти и которая могла также быть сконфигурирована как распределенный кэш. Ehcache, в отличие от Memcached, запускается как процесс в JVM и в точности реплицирует состояние кэша для всех узлов кластера. Обратим внимание, что в то время (около 2006–2008 гг.) Encache все еще был независимым open source проектом и не был частью Terracotta (в рамках Terracotta модель репликации и дистрибуции может быть иной, но для данной статьи это не столь важно).

Аспекты использования кэша




Поскольку у нас уже были реальные клиенты, мы установили два сервера Tomcat для обеспечения дополнительной надежности. Следуя правилам выстраивания архитектуры, мы установили распределенный Ehcache-кластер между серверами. Мы исходили из того, что MySQL работает медленно (как и любая другая SQL-система), а значит кэш оперативной памяти обеспечит гораздо более высокую скорость чтения и снизит нагрузку на базу данных.

Но что случится, когда мы столкнемся с проблемой данных, например, повреждением или замусориванием данных? Назовем такую проблему «некорректное состояние». Один подобный случай произошел, когда мы выкатили версию редактора Wix Editor, которая создавала неверное определение сайта. Симптомом этой проблемы стали поврежденные пользовательские сайты, т.е. пользователи не могли просматривать и редактировать свои сайты. К счастью, благодаря тому, что у нас была (и есть) очень большая пользовательская база, пользователи немедленно обнаружили эту проблему и сообщили о ней. Мы откатили проблемную версию и исправили поврежденные файлы определений в базе данных. К сожалению, даже после того, как мы внесли исправления во все места, где хранились эти данные, пользователи продолжали жаловаться на поврежденные сайты. Причина была в том, что мы просто исправили некорректное состояние, хранящееся в базе данных, забыв о том, что кэш тоже хранит копии наших данных, включая поврежденные документы.

Ehcache – это своего рода «черный ящик»: Java-библиотека без интерфейса SQL-запросов и без управляющего приложения для просмотра содержимого кэша. Поскольку у нас не было легкого способа «заглянуть» внутрь кэша, мы не могли его диагностировать и проанализировать, столкнувшись с повреждением данных (заметим, что у некоторых других решений для кэша есть управляющие приложения, которые превращают их в «белый ящик», но мы не работали ни с одним из них). Когда мы осознали, что некорректное состояние, по-видимому, сохранилось еще и в кэше, для решения проблемы нам понадобилось сперва исправить некорректное состояние в базе данных. Оба сервера приложений хранили некорректное состояние у себя в кэше, поэтому мы сначала остановили один из серверов, чтобы очистить кэш его памяти и снова запустить. Но так как кэш был распределенным, то даже после перезапуска сервера представление кэша его памяти было реплицировано со второго сервера приложений. В результате мы снова вернулись к некорректному состоянию. Перезапуск второго сервера на этом этапе ничем бы не помог, второй сервер получил бы репликацию некорректного состояния от первого. Единственным способом избавиться от этого некорректного состояния было остановить и перезапустить оба сервера, что привело к краткосрочному простою наших сервисов.

Как насчет инвалидации кэша?


Поскольку мы использовали Ehcache, у которого есть управляющий API с поддержкой инвалидации, мы могли бы написать специальный код, предписывающий обоим серверам считать кэш недействительным (инвалидационный переключатель). Но если бы мы не подготовили инвалидационный переключатель для работы с определенным типом данных, нам бы снова пришлось перезапускать оба сервера одновременно, чтобы избавиться от некорректного состояния. Конечно, мы могли бы сделать управляющее приложение для Ehcache, добавив возможность просмотра и инвалидации данных. Но в тот момент, когда нужно было принять это решение, мы задумались: «А нужен ли нам кэш на самом деле?».

Первым делом мы проверили статистику MySQL. Оказалось, что при правильном использовании MySQL операции чтения занимают доли миллисекунд даже для больших таблиц. Сегодня у нас есть таблицы, в которых свыше 100 миллионов строк, и мы читаем из них со скоростью в доли миллисекунд. Мы добились этого, обеспечив процесс MySQL достаточной памятью для работы с дисковым кэшем и читая отдельные строки по первичному ключу или индексу без соединения таблиц (JOIN). В конечном итоге мы поняли, что нам не нужен кэш. Фактически, в большинстве случаев, когда люди используют кэш, в нем на самом деле нет особой необходимости. Мы считаем, что кэш не является частью архитектуры. Это, скорее, одно из возможных решений для проблемы производительности, причем не самое лучшее.

Наши рекомендации по использованию кэша таковы:
1. Вам не нужен кэш.
2. Нет, правда, не нужен.
3. Если у вас сохраняются проблемы с производительностью, попробуйте разобраться с их источником. Что работает медленно? Почему оно работает медленно? Можно ли изменить архитектуру, чтобы работало не так медленно? Можно ли оптимизировать данные для чтения?

Если вам необходимо использовать кэш, обдумайте следующее:
• Как вы обеспечите инвалидацию кэша?
• Как вы будете просматривать данные в кэше («черный ящик» или «белый ящик»)?
• Как будет происходить холодный старт системы? Сможет ли система справиться с трафиком при пустом кэше?
• Какое ухудшение производительности влечет использование кэша?

Главный архитектор программного обеспечения конструктора для создания сайтов Wix,
Йоав Абрахами
Оригинал статьи: блог инженеров компании Wix

Комментарии (24)


  1. harlov91
    31.03.2016 05:16
    +12

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


  1. insensible
    31.03.2016 05:45
    +15

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


  1. ShadowsMind
    31.03.2016 09:12
    +11

    Ожидал от статей от Wix большего… 3 статьи и все вода.


  1. necromant2005
    31.03.2016 09:17

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


    1. gricom
      31.03.2016 17:31
      +1

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


      1. necromant2005
        31.03.2016 17:40

        Это не совсем так голова отличный кеш-но он слишком умный. реальный кеш это лист бумаги на который ты записываешь имя человека и сбоку рисуешь его портрет для распознавания. И дальше когда добавляется новый коллега ты его добавляешь туда, а если удаляется пытается соотнести изображение с тем что у тебя нарисовано и вычеркнуть. А еще веселее если 2 брата близнеца работают в разных отделах. И кеш у тебя не просил листик а еще отображает иерархию отделов (вед охота знать что вот это Админ идет). И тогда ситуация перевода человека из одного отдела в другой это вообще ужас-ужас.


        1. gricom
          31.03.2016 17:44

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


  1. Sioln
    31.03.2016 10:00

    Как-то огульно весь кеш обозвали бякой. А при этом почти в каждой системе есть какие-нибудь мелкие, как правило, статичные справочники, которые влекут за собой лишние round-trip'ы к БД. Их кеширование в банальные словари или списки позволяют увеличить производительность системы малой кровью.


  1. gricom
    31.03.2016 10:39
    +4

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

    Частью этой архитектуры был Ehcache

    Раз уж вы использовали мощные кластерные кэши на Java, то могли бы использовать read-through — механизм, который позволяет кэшу самому сходить в базу, если при запросе оказалось, что в кэше нет нужных данных. Не знаю насчет EhCache, но в Infinispan и в Apache Ignite, которые бесплатные и open source, такая функциональность есть, не говоря уже о коммерческих решениях, таких как Oracle Coherence и т.д. А если еще использовать и write-through/write-behind, которые позволяют пробрасывать в базу всё, что пишется в кэш (write-through — синхронная запись, write-behind — асинхронная), то вы можете вообще забыть о такой проблеме, как инвалидация кэша, потому что если всю работу с данными проводить через кэш, то это будет и быстро, и консистентно, и масштабируемо, а сама БД будет выполнять только роль persistent storage, который нужен чтобы прогреть кэш при холодном старте.


  1. rotor
    31.03.2016 10:44
    +1

    С кэшем связана одна существенная проблема — усложнение архитектуры. Появляется дополнительный сильно связанный слой.
    В конечном итоге оптимальным выбором является действительно использовать кэш минимально. Но в этом случае, встает задача эффективного выбора основного хранилища. В заметке, например, сказано что MySQL был выбран исключительно по субъективным причинам. В тоже время, возможно, если бы к выбору хранилища данных подошли бы более ответственно, то необходимость в кэшировании могла бы вообще не возникнуть.
    Мы в последнее время вообще отказались и от MySQL и от кэша. В итоге производительность только улучшилась отпало множество проблем связанных с обслуживанием кэша, архитектура стала куда прозрачнее.


    1. Wix_engineering
      31.03.2016 10:47

      Об этом как раз следующий пост.


    1. VolCh
      31.03.2016 11:03
      +1

      Архитектура усложняется если кэширование производить на стороне приложения, а не хранилища, прозрачно для приложения.


    1. akalend
      31.03.2016 12:55

      зачастую бывает, что выбор БД — это лишь историческое наследие прошлых лет, однако как гласит основное правило: используются те инструменты, которое лучше знает архитектор системы.


  1. yuriy12
    31.03.2016 10:46

    Мне кажется вы обобщаете кэширование БД и кэширование в принципе. Вот вам один из случаев: страница со списком территориальных регионов и SVG отображением границ этих регионов. Регионы и их границы изменяются… да в принципе рактически не изменяются. Вы предлагаете при каждом запрсе этой страницы делать выборку в БД, потом по геокоординатам строить SVG и DOM и отдавать результат пользователю? Лично я закэширую страницу целиком с периодом как минимум в сутки.


  1. VolCh
    31.03.2016 11:10
    +2

    Что совсем без кэша? Без трёх уровней кэша в процессоре, без кэша в диске, без дискового (как минимум) кэша в ОС, без кэша в том же MySQL?
    Ваш случай — хороший пример безответственного подхода к кэшированию: "сначала кэш сделаем, а думать как его инвалидировать и греть будем потом". А ведь думать над этим надо начинать при первой же мысли "что-то тормозит, может кэш намутить".
    И, да, если вы для чтения только по ключам без джойнов денормализовали свою базу, то вы просто сделали кэш в самой базе.


    1. sapl
      31.03.2016 22:00

      А что плохого в использовании MySQL как key-value хранилища (вообще без Join-ов)?
      (без относительно случая автора статьи)
      У нас на похожем стеке (Java/jdbc + tomcat) кэш в первую очередь
      помогал от переполнения пула соединений к базе (как бы быстро не выполнялись запросы — пул был узким местом почему-то).


      1. VolCh
        31.03.2016 22:39

        Да ничего плохо, если value нормализованы. Но на практике эти value часто являются кэшем для джойнов и(или) группировок, и для джойнов вовсе не 1:1 или 1:0, и для группировк не по первичному ключу. То есть джойны и группировки просто делаются на стороне приложения при записи (вариант — материализованные вью на стороне БД), чтобы чтение было максимально быстрым. Такой подход имеет право на жизнь, но он является разновидностью кэширования.


  1. achekalin
    01.04.2016 11:34
    +1

    Краткое содержание: «мы слепили сайт из чего-то, что нашли, еще чего-то, и еще из Явы и Флеша, потом набрали юзеров, потом выяснили, что наш кеш не умеет инвалидироваться, потом его отключили — а сайт стал работать быстро. Вывод: кеши в мире никому не нужны.»

    Перевод: вместо того, что построить по-настоящему эффективную архитектуру, люди взяли что-то, чем сами не умели управлять, и из собственного незнания сделали вывод, что никому в мире кеш не нужен.

    Я бы сказал, что делать сайт на чистом флеше, расползающийся в почти любом браузере, сайт, для индексирования которого массе людей пришлось предпринимать героические усилия — это само по себе плевать против ветра. Если же взять для кеша систему, инвалидировать записи в которой сама же система управления сайтом не умеет… Скажите, вам за такое героическое преодоление самими же вами созданных граблей хоть премию выписали? :)

    P.S. Честно говоря, по опыту поддержки сайтов с миллионами посетителей могу сказать ровно обратное. Только сайты эти а) используют более эффективный стек, б) крутятся на в сотни раз менее мощных серверах, и в) не упоминаются в героических рассказах о том, как решили грабли, откровенно созданные своими руками.

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


  1. IvanPanfilov
    01.04.2016 17:18

    Все правильно, кеш не нужен.

    И это хороший повод переписать все на Tarantool.


    1. VolCh
      01.04.2016 18:11

      Тарантул же по сути и есть кеш с транзакционной персистентностью. Нет?


      1. IvanPanfilov
        02.04.2016 07:16

        или база данных с лучшими качествами кеша и без его минусов, из коробки — как посмотреть.


    1. gricom
      01.04.2016 18:20

      Ну это до тех пор, пока Wix не напишет статью о том, как они отказались от тарантула


  1. tmk826
    03.04.2016 23:40
    +1

    Решение правильное, а вывод — нет. Правильно настроить кеш не тривиальная задача и во многих случаях правильнее отдать память операционной системе — пусть она управляет кеш-ом.


    1. VolCh
      04.04.2016 13:28

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