Если использовать кеш без разбора, кто сойдет с ума!

Java Redis

Оригинал: Miss Sister Taste (идентификатор публичной учетной записи WeChat: xjjdog), добро пожаловать, пожалуйста, сохраните источник для перепечатки.

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

«Откуда группа людей взяла аккаунт большого босса, чтобы сделать слепой тест, и оказалось, что проблема с данными», сказал директор Ню с воздухом на носу и подбоченившись. Поскольку учетная запись босса участвовала в тесте один раз, она стала окончательной тестовой учетной записью молчаливого понимания всех. Многие люди используют его, чтобы принудительно манипулировать балансом учетной записи для обеспечения высокого параллелизма.

"Разве учетная запись босса не является тестовой..."— прошептал кто-то внизу.

"На самом деле ни на одном аккаунте нет таких интенсивных операций..."Кто-то прошептал, так что его лицо стало тяжелым директором.

— Думаешь, я шучу?- сказал директор с красными глазами,«Однажды я столкнулся с ошибкой первого уровня из-за такой проблемы с несогласованностью данных. Именно так, сегодня я покажу вам, почему существует несогласованность данных»..

Я проковылял на сцену в очках и тайно засмеялся в душе, режиссеру хотелосьCache Aside PatternДайте Коупу снова.

Набор с открытым исходным кодом и ориентированной на обучение системой, добро пожаловать в STAR:GitHub.com/star hotel о, хорошо/неплохо…. Он включает в себя сложный бизнес ToB, бизнес с высокой степенью параллелизма в Интернете, приложение кэширования, DDD, руководство по микросервисам. Управляемый моделью, управляемый данными. Поймите путь эволюции крупномасштабных сервисов, навыки кодирования, изучите Linux и настройте производительность. Помощь Docker/k8s, мониторинг, сбор логов, изучение промежуточного ПО. Front-end технология, back-end практика и т.д. Основная техника:SpringBoot+JPA+Mybatis-plus+Antd+Vue3.

1. Почему данные противоречивы?

Узкое место базы данных очевидно для всех: в среде с высокой степенью параллелизма легкоI/OЗаблокировано. Это главный приоритет для общих данных, отдайте его скорости более быстрого хранения. Это более быстрое хранилище, вероятно, будет распределенным, например,Redis, это также может быть автономным, таким какCaffeine.

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

Проблема несоответствия данных стала более распространенной в мире.进修Учащиеся, имеющие опыт работы с многопоточностью в Java, обязательноJMMМодель еще свежа в памяти. Значение, если оно хранится в двух местах одновременно, вызовет проблемы.

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

2. Cache Aside Pattern

Как обеспечить согласованность данных в БД и кеше? Теперь лучшее передовое решение состоит в том, чтобыCache Aside Pattern.

Давайте сначала посмотрим на процесс чтения данных.Правила:Сначала прочитайте кеш, затем прочитайте БД. Подробные шаги следующие:

  1. Каждый раз, когда данные читаются, это читается из кэша
  2. Если он прочитан, он вернется напрямую, что называется попаданием в кеш.
  3. Если данные в кеше не могут быть прочитаны, получается копия из БД, что называется промахом кеша
  4. Вставьте прочитанные данные в кеш, и в следующий раз, когда вы их прочитаете, вы можете напрямую попасть в них.

Давайте снова посмотрим на запросы на запись. Правила таковы:Сначала обнови бд, потом кеш удаляй. Подробные шаги следующие:

  1. Записать изменения в базу
  2. Удалить соответствующие данные в кеше

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

Ребята, они собираются допросить директора.

3. Почему删除кэш вместо更新кеш?

Это легче понять. Когда несколько операций обновления происходят одновременно,删除Определены результаты работы результатов; и更新операция, может дать разные результаты.

image.png

Как показано. Два запроса A и B, запрос B после запроса A, данные самые последние. Из-за наличия кеша, если при сохранении есть небольшое отклонение, это приведет к тому, что кэшированное значение A перезапишет значение B, после чего значение записи в базе данных будет несовместимо с кэшированным значением до следующих данных. изменить .

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

image.png

4. Почему бы не удалить сначала кеш, а потом обновить базу?

Этот вопрос аналогичен. Нам даже не нужен параллельный сценарий записи, чтобы найти проблему.

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

image.png

Как показано выше, запрос на запись сначала удаляет кэш. В результате в это время есть другие запросы на чтение для чтения старого значения базы данных в базу данных, а данные в кеше в это время равны 0. Затем БД была обновлена, и запись базы данных была изменена на 100. После такой тряски данные в базе и кеше неконсистентны.

Все внезапно закивали, и многие зачарованно улыбнулись.

5. Кешируйте аннотации в Spring

С помощью SpringBoot можно легко управлять Redis. В Java есть три широко используемых клиента Redis: jedis, redisson и lettuce. Spring использует салат по умолчанию.

Многим нравится использовать абстрактный пакет кэширования Spring spring-cache.

Он использует аннотации и АОП для абстрагирования уровня кэша и может переключаться между различными платформами кэширования в куче и распределенными платформами. Вот его координаты maven:

<dependency> 
    <groupId>org.springframework.boot</groupId> 
    <artifactId>spring-boot-starter-cache</artifactId> 
</dependency>

Есть три шага с использованием Spring-Cache:

  1. добавить в класс запуска@EnableCachingаннотация;

  2. использоватьCacheManagerИнициализируйте используемую структуру кэша и используйте аннотацию @CacheConfig для внедрения ресурсов, которые будут использоваться;

  3. использовать@Cacheableи другие аннотации для кэширования ресурсов.

Есть три аннотации для операций с кэшем:

  1. @CacheableУказывает, что если в системе кэширования нет такого значения, возвращаемое значение метода будет кэшировано;

  2. @CachePutУказывает, что при каждом выполнении метода возвращаемое значение кэшируется;

  3. @CacheEvictУказывает, что некоторые кэшированные значения очищаются при выполнении метода.

Затем возникает проблема, в spring-cache@CacheEvictОбратите внимание, следует ли сначала удалить кеш или удалить кеш позже? Если вы этого не понимаете, вы не можете спать по ночам. Ключевая технология используется не только с удовольствием, но и с уверенностью.

Удаление кеша находится вCacheAspectSupportреализован в , мы отметим код ниже.

// Process any early evictions
processCacheEvicts(contexts.get(CacheEvictOperation.class), true,
		CacheOperationExpressionEvaluator.NO_RESULT);
...
// Process any late evictions
processCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue);

Он имеет действие предварительной очистки и действие после очистки через логическую переменную.boolean beforeInvocationнастраивать. Откуда берется это значение? еще надо посмотреть@CacheEvictаннотация.

/**
* Whether the eviction should occur before the method is invoked.
* <p>Setting this attribute to {@code true}, causes the eviction to
* occur irrespective of the method outcome (i.e., whether it threw an
* exception or not).
* <p>Defaults to {@code false}, meaning that the cache eviction operation
* will occur <em>after</em> the advised method is invoked successfully (i.e.,
* only if the invocation did not throw an exception).
*/
boolean beforeInvocation() default false;

Очень хорошо, его значение по умолчанию равно false, что доказывает, что действие удаления отстает, и практика такжеCache Aside Pattern.

6. Есть ли другие режимы?

Я слышал, что есть такжеRead Through Pattern,Write Through Pattern,Write Behind Caching PatternИ другие распространенные шаблоны синхронизации кэша, почему бы не использовать их?Ягодицы одноклассника двигались взад-вперед по стулу, желая попробовать, и он, наконец, воспользовался возможностью, чтобы заговорить.

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

Например,Read Through, на самом деле это заставляет вас не знать о существовании уровня кэша для операций чтения. Обычно вы реализуете загрузку кеша вручную, ноRead ThroughТам может быть слой агента, чтобы сделать это для вас.

Другой пример,Write Through, вам больше не нужно думать о том, синхронизированы ли база данных и кеш, слой прокси сделал это за вас, вам просто нужно запихнуть в него данные.

Read ThroughиWrite ThroughКонфликта нет, они могут существовать одновременно, поэтому в коде бизнес-уровня нет понятия синхронизации. Шуан Вай Вай.

Что касаетсяWrite Behind Caching, что означает, что он сначала попал в кеш, а затем асинхронный поток медленно доставил данные из кеша в БД. Чтобы использовать эту вещь, вы должны оценить, могут ли ваши данные быть потеряны, и сможет ли ваш объем кэш-памяти выдержать испытание бизнес-пиками. Текущая операционная система, БД и даже очереди сообщений, такие как Kafka и т. д., в определенной степени будут практиковать этот режим.

Но сейчас это не имеет ничего общего с потребностями нашего бизнеса.

7. У паттерна Cache Aside тоже есть проблемы

Режиссер попал на коня, одну из двух вершин, и после долгого дня популяризации убедил всех студентов. Когда все хотели поаплодировать режиссеру, раздался нестройный голос.

Я обнаружил огромную проблему.Одноклассник сказал,Если обновление базы данных прошло успешно, но удаление кеша не удалось, это также приведет к несогласованности кеша..

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

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

Способ 2. После сбоя операции удаления кеша повторите попытку определенное количество раз. Если это все еще не работает, есть большая вероятность, что служба кеша неисправна, в это время должен быть записан лог, и эти ключи будут удалены, когда служба кеша вернется в нормальное состояние.

Способ 3: еще один шаг, сначала удалите кеш, затем обновите данные, а затем удалите кеш. Это больше работы, но и безопаснее.

Нет проблем?Директор огляделся и увидел, что все кивают.Нет Нет Нет, есть еще несоответствия данных.

Все были в замешательстве.

Картинка выше, которая выглядит правильно, на самом деле неверна. Зачем? Потому что операция чтения данных из БД в кеш не атомарна.

image.png

Например, на рисунке выше при инвалидации (или удалении) кеша просто приходит запрос на чтение. Этот запрос на чтение получил старое значение базы данных, но по разным причинам (например, из-за истощения сети) он не был сразу записан в кеш, а был отложен. За время, пока собирался записать в кеш, много чего произошло, был еще один запрос, база обновилась до значения 200, а кеш удалился.

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

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

Итак, вы знаете, как это сделать правильно?Спросил у директора.

понял! В будущем мы будем использовать аннотации spring-cache для завершения работы и больше не будем вручную писать логику согласованности в коде.

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

Об авторе:Мисс сестра вкус(xjjdog), публичная учетная запись, которая не позволяет программистам идти в обход. Сосредоточьтесь на инфраструктуре и Linux. Десять лет архитектуры, десятки миллиардов ежедневного трафика, обсуждение с вами мира высокой параллелизма, дающие вам другой вкус. Мой личный WeChat xjjdog0, добро пожаловать в друзья для дальнейшего общения.