Количество слов в статье: 6867 слов. Чтение занимает около 8 минут.
В обычном процессе разработки программного обеспечения важность кэширования становится все более заметной. Это не только сокращает время обратной связи с пользователями, но и в большинстве случаев эффективно снижает нагрузку на систему.
В этой статье будет подробно рассказано о местномкэшрамка:「Caffeine Cache」。
Caffeine Cacheс его высокой производительностьюи Масштабируемость побеждает「Король локального кэширования」заголовок,Это кэш-библиотека Java. Целью его разработки является оптимизация скорости вычислений, эффективности памяти и практичности.,для удовлетворения потребностей современных разработчиков программного обеспечения.
Локальным кешем по умолчанию в версии Spring Boot 1.x является Guava Cache. После Spring 5 (SpringBoot 2.x) Spring официально отказался от Guava Cache в качестве механизма кэширования и вместо этого использовал Caffeine, который имеет более высокую производительность, в качестве компонента кэширования по умолчанию. Это большое подтверждение для Caffeine.
Далее мы подробно представим функции и приложения Caffeine Cache и органично интегрируем этот эффективный инструмент кэширования в ваш проект.
Разбор кофеина Прежде чем использовать Cache, мы должны сначала понять кэшалгоритм. исключение. Отличный алгоритм Исключение может значительно повысить эффективность, поскольку процесс кэширования всегда сопровождается уничтожением данных.
В зависимости от времени данные, которые первыми попадают в кэш, будут удалены первыми. Когда кэш заполнен, самые ранние данные, помещенные в кэш, удаляются.
Преимущества: Простота реализации, лучше подходит для некоторых приложений с нечасто повторяющимися запросами.
Недостатки: Частота доступа и время доступа к элементам данных не учитываются, а самые последние и часто используемые данные могут быть удалены.
Этот алгоритм принимает решения на основе исторических записей доступа к данным, и данные, к которым не было доступа в течение длительного времени, будут удалены. LRU поддерживает связанный список всех элементов кэша. Новые данные вставляются в заголовок связанного списка. Если кэш заполнен, данные будут удалены из конца связанного списка.
Преимущества: LRU учитывает последние шаблоны доступа к данным, имеет отличную производительность по принципу локальности, прост и практичен.
Недостатки: он не может отражать частоту доступа к данным. Если к данным обращались недавно, они не будут удалены, даже если частота доступа низкая. Например, если к фрагменту данных часто обращаются в первые 59 секунд минуты и нет доступа в последнюю секунду, но пакет непопулярных данных попадает в кэш в последнюю секунду, то «горячие» данные могут быть удалены.
Основной принцип заключается в подсчете каждого объекта в кэше и записи количества обращений к нему. Когда кэш заполнен и некоторые объекты необходимо удалить, алгоритм LFU отдает приоритет объектам, к которым обращались реже всего.
Преимущества: LFU может лучше справляться с ситуациями, когда долгосрочный доступ является стабильным и частым, поскольку это гарантирует, что часто используемые объекты не будут легко удалены.
Недостатки: LFU не может эффективно обрабатывать некоторые объекты, к которым часто временно обращаются, но которые впоследствии больше не используются. Поскольку время доступа к этим объектам уже очень велико, даже если в дальнейшем к ним больше не будет доступа, их будет нелегко устранить, что может привести к пустой трате места в кэше. А LFU необходимо поддерживать счетчики доступа ко всем объектам, которые могут занимать много места для хранения и вычислительных ресурсов.
Caffeine использует стратегию устранения Window TinyLfu, которая обеспечивает почти оптимальную результативность.
Вы можете примерно догадаться об этом, глядя на название. LFU Варианты,Когда возникает проблема разрыва страницы кэша (т.е. нехватка места в кэше и необходимость замены старого элемента кэша),Используйте информацию о частоте статистики, чтобы выбрать лучшего кандидата на замену.
Как это работает:
По сравнению с традиционными стратегиями LRU и LFU, W-TinyLFU имеет следующие преимущества:
недостаток:
В общем, W-TinyLFU — это очень сложный и гибкий алгоритм кэширования, который хорошо справляется с идентификацией и обработкой долговременных и пакетных «горячих» данных, но по сравнению с более простыми алгоритмами, такими как LRU, он требует больше ресурсов и точной настройки.
Caffeine предоставляет в общей сложности четыре типа кэша, соответствующие четырем стратегиям загрузки.
Самый распространенный вид кэша,Нет необходимости указывать метод загрузки,Приходится вызывать вручнуюput()
загрузить。Нужно обратить вниманиеда,Метод put() перезапишет существующий ключ.
При получении значения кэша,Если вы хотите использовать кэш, когда значение не существует,Атомарно записывает значение в кэш,тогда ты можешь позвонитьget(key, k -> value)
метод,Этот метод позволит избежать конфликтов при записи.
Во многих случаях,при использованииget(key, k -> value)
час,Если есть еще один вызов этого метода в то же время, чтобы конкурировать,Тогда последний будет заблокирован,до предыдущегонитьвозобновлятькэш Заканчивать;И если другойнитьвызовgetIfPresent()
метод,немедленно вернет ноль,Не будет заблокирован.
Cache<String, String> cache = Caffeine.newBuilder().build();
cache.getIfPresent("1"); // null
cache.get("1", k -> 1); // 1
cache.getIfPresent("1"); //1
cache.set("1", "2");
cache.getIfPresent("1"); //2
LoadingCacheда Своеобразная автоматическая загрузка кэша. Отличие его от обычного кэша в том, что,Если кэш не существует или срок его действия истек, при вызове метода get() будет автоматически вызван метод CacheLoader.load() для загрузки последнего значения. Вызов метода getAll() будет проходить по всем ключам и вызывать get(. ), если не реализован метод CacheLoader().
При использовании LoadingCache вам необходимо указать CacheLoader и реализовать его метод load() для автоматической загрузки при отсутствии кеша.
Во многих случаях,когда дванить同часвызовget()
,Тогда последний будет заблокирован,Пока не завершится предыдущее обновление кэша.
LoadingCache<String, Object> cache = Caffeine.newBuilder().build(new CacheLoader<String, Object>() {
@Override
public @Nullable Object load(@NonNull String s) {
return «Читать из библиотеки данных»;
}
@Override
public @NonNull Map<@NonNull String, @NonNull Object> loadAll(@NonNull Iterable<? extends @NonNull String> keys) {
return null;
}
});
cache.getIfPresent("1"); // null
cache.get("1"); // Чтение из библиотеки данных
cache.getAll(keys); // null
LoadingCache особенно практичен. Мы можем настроить логику в методе загрузки и загружать ее из базы данных, когда кэш не существует. Это может обеспечить многоуровневое кэширование.
Вариант AsyncCacheCache.,Все результаты ответовCompletableFuture
。
таким образом,AsyncCache адаптирует асинхронное программирование. По умолчанию,кэш Рассчитайте, используяForkJoinPool.commonPool()
какнитьбассейн,Если вы хотите указать нить пула,то вы можете переопределить и реализоватьCaffeine.executor(Executor)
метод。
В многопоточной ситуации, когда два потока вызывают get(key, k -> value),вернется「Тот же объект CompletableFuture」。Поскольку сам возвращаемый результат не блокирует,Вы можете выбрать ожидание блокировки или неблокировку в соответствии с дизайном вашего бизнеса.
import com.github.benmanes.caffeine.cache.AsyncCache;
import com.github.benmanes.caffeine.cache.CacheLoader;
import com.github.benmanes.caffeine.cache.Caffeine;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
public class CaffeineExample {
private final Executor executor = Executors.newSingleThreadExecutor();
CacheLoader<String, String> loader = new CacheLoader<>() {
@Override
public String load(String key) throws Exception {
// Имитировать процесс загрузки данных
Thread.sleep(1000);
return key.toUpperCase();
}
@Override
public CompletableFuture<String> asyncLoad(String key, Executor executor) {
return CompletableFuture.supplyAsync(() -> {
try {
return load(key);
} catch (Exception e) {
throw new RuntimeException(e);
}
}, executor);
}
};
AsyncCache<String, String> cache = Caffeine.newBuilder()
.maximumSize(100)
.executor(executor)
.buildAsync(loader);
public void test() throws Exception {
// Асинхронный сбор
CompletableFuture<String> future = cache.get("hello");
System.out.println(future.get()); // выход HELLO
}
public static void main(String[] args) throws Exception {
new CaffeineExample().test();
}
}
Вы можете сказать это по названию: очевидно, это Загрузка. Кэш и асинхронность Cacheкомбинация функций。Async Loading Cache поддерживает автоматическую загрузку кешей асинхронно.。
Вот пример того, как создать кэш асинхронной загрузки:
import com.github.benmanes.caffeine.cache.AsyncLoadingCache;
import com.github.benmanes.caffeine.cache.Caffeine;
// создавать AsyncLoadingCache
AsyncLoadingCache<String, Data> cache = Caffeine.newBuilder()
.maximumSize(10000)
.expireAfterWrite(5, TimeUnit.MINUTES)
.buildAsync(this::loadData);
// Определить метод загрузки
private CompletableFuture<Data> loadData(String key) {
// Это всего лишь пример, вы можете выполнить операцию загрузки в соответствии с реальной ситуацией.
return CompletableFuture.supplyAsync(() -> {
// Загрузите данные из библиотеки данных или из другого места.
Data data = ... ;
return data;
});
}
// Используйте кэш
public void useCache(String key) {
cache.get(key).thenAccept(data -> {
// Здесь данные да loadData метод возвращает объект
// ХОРОШО data Процесс
...
});
}
Кэш асинхронной загрузки также очень практичен. В некоторых бизнес-сценариях загрузка данных занимает много времени. В этом случае кэш асинхронной загрузки можно использовать, чтобы избежать блокировки загрузки данных.
Кофеин предлагает три стратегии переработки: переработка по размеру, переработка по времени и переработка по эталонам.
Существует два способа стратегии переработки на основе размера: один основан на размере кэша, а другой — на весе.
// Выселение на основании подсчёта кэша
LoadingCache<String, Object> cache = Caffeine.newBuilder()
.maximumSize(10000)
.build(new CacheLoader<String, Object>() {
@Override
public @Nullable Object load(@NonNull String s) {
return «Читать из библиотеки данных»;
}
@Override
public @NonNull Map<@NonNull String, @NonNull Object> loadAll(@NonNull Iterable<? extends @NonNull String> keys) {
return null;
}
});
// Выселения производятся в зависимости от веса кэша. Те, у кого вес меньше, будут выселены в первую очередь.
LoadingCache<String, Object> cache1 = Caffeine.newBuilder()
.maximumWeight(10000)
.weigher(new Weigher<String, Object>() {
@Override
public @NonNegative int weigh(@NonNull String s, @NonNull Object o) {
return 0;
}
})
.build(new CacheLoader<String, Object>() {
@Override
public @Nullable Object load(@NonNull String s) {
return «Читать из библиотеки данных»;
}
@Override
public @NonNull Map<@NonNull String, @NonNull Object> loadAll(@NonNull Iterable<? extends @NonNull String> keys) {
return null;
}
});
Уведомление:MaximumWeight и MaximumSize не могут использоваться одновременно.,Это потому, что оба механизма используются для ограничения размера кэша. Необходимо сделать выбор между этими двумя.
// Выход на основе различных стратегий истечения срока действия
LoadingCache<String, Object> cache = Caffeine.newBuilder()
.expireAfter(new Expiry<String, Object>() {
@Override
public long expireAfterCreate(String key, Object value, long currentTime) {
return TimeUnit.SECONDS.toNanos(seconds);
}
@Override
public long expireAfterUpdate(@Nonnull String s, @Nonnull Object o, long l, long l1) {
return 0;
}
@Override
public long expireAfterRead(@Nonnull String s, @Nonnull Object o, long l, long l1) {
return 0;
}
}).build(key -> function(key));
Кофеин предлагает три типа стратегии выбора времени. выселения:
Четыре ссылочных типа в Java
ссылочный тип | Время сбора мусора | использовать | время выживания |
---|---|---|---|
Сильная ссылка Сильная ссылка | никогда | общее состояние объекта | Завершить работу, когда JVM перестанет работать |
Мягкая ссылка Мягкая ссылка | Когда недостаточно памяти | кэш объектов | Завершить из-за недостаточности памяти |
Слабая ссылка | во время сбора мусора | кэш объектов | gc завершает работу после запуска |
Фантомная ссылка Фантомная ссылка | никогда | Виртуальные ссылки можно использовать для отслеживания активности объектов, перерабатываемых сборщиком мусора. Когда объект, связанный с виртуальной ссылкой, перерабатывается сборщиком мусора, будет получено системное уведомление. | Завершить работу, когда JVM перестанет работать |
// Вытеснить кэш, если не указан ни ключ, ни значение.
LoadingCache<String, Object> cache = Caffeine.newBuilder()
.weakKeys()
.weakValues()
.build(key -> function(key));
// Вытеснение, когда сборщику мусора необходимо освободить память
LoadingCache<String, Object> cache1 = Caffeine.newBuilder()
.softValues()
.build(key -> function(key));
Уведомление:AsyncLoadingCache не поддерживает слабые и мягкие ссылки, а Caffeine.weakValues() и Caffeine.softValues() нельзя использовать вместе.
Метод CacheWriter может записывать все данные в кэше третьей стороне, чтобы избежать потери данных при перезапуске службы.
LoadingCache<String, Object> cache = Caffeine.newBuilder()
.writer(new CacheWriter<String, Object>() {
@Override public void write(String key, Object value) {
// Запись на внешнее хранилище
}
@Override public void delete(String key, Object value, RemovalCause cause) {
// Удалить внешнее хранилище
}
})
.build(key -> function(key));
Кофеин имеет встроенную функцию сбора данных,проходитьCaffeine.recordStats()
метод,можно открытьданныесобирать。такCache.stats()
метод将会返回когда前кэшнекоторые изстатистикаиндекс,Например:
Cache<String, String> cache = Caffeine.newBuilder().recordStats().build();
cache.stats(); // Получить индикатор статистики
После знакомства с Caffeine Cache давайте познакомимся с тем, как плавно интегрировать Caffeine Cache в проект.
Без лишних слов, приступим к практике.
Сначала добавьте зависимости Maven для Spring Boot Starter Cache и Caffeine в файл pom.xml:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>3.1.1</version>
</dependency>
</dependencies>
Во-вторых, создайте класс конфигурации и создайте компонент CacheManager:
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.TimeUnit;
@Configuration
@EnableCaching
public class CachingConfig {
@Bean
public CacheManager cacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCaffeine(caffeineCacheBuilder());
return cacheManager;
}
Caffeine<Object, Object> caffeineCacheBuilder() {
return Caffeine.newBuilder()
.initialCapacity(100)
.maximumSize(500)
.expireAfterAccess(10, TimeUnit.MINUTES)
.weakKeys()
.recordStats();
}
}
Класс Caffeine использует шаблон построителя и имеет следующие параметры конфигурации:
Weigher<K,V>
Как рассчитатькэш Вес записи。Помимо настройки bean-компонентов, вы также можете использовать файлы конфигурации:
spring:
cache:
type: caffeine
cache-names:
- userCache
caffeine:
spec: maximumSize=1024,refreshAfterWrite=60s
После завершения настройки вы можете использовать его напрямую с аннотациями:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
@Service
public class SomeService {
@Autowired
private SomeRepository someRepository;
@Cacheable("items")
public Item getItem(int id) {
return someRepository.findById(id);
}
}
В этом примере,Мы добавили @Cacheableаннотацию в метод getItem.,Каждый раз, когда этот метод вызывается,SpringСначала найди имяitem
изcacheсерединаданет Есть перепискаidиз条目。если есть,Просто верните значение кэша,нет вызывает метод и сохраняет результат в кеше.
Как использовать аннотации,相关из常用аннотациявключать:
@Caching(cacheable = @Cacheable("users"),
evict = {@CacheEvict("cache2"), @CacheEvict(value = "cache3", allEntries = true)})
public User find(Integer id) {
return null;
}
Этот тип аннотации также можно использовать в классе, указывая, что все методы класса поддерживают соответствующую аннотацию кэша.
Среди них наиболее часто используемым является @Cacheable. Часто используемые атрибуты аннотации @Cacheable следующие:
Spring Cache также поддерживает выражения Spring Expression Language (SpEL). Вставлять динамические значения в имена или ключи кэша можно через SpEL.
Приведу несколько примеров:
@Cacheable(value = "books", key = "#isbn")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
Он будет использовать переданный findBook
методический isbn
Значение параметра.
Вы также можете использовать более сложные выражения SpEL, например:
@Cacheable(value = "books", key = "#root.args[0]")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
#root.args[0]
Относится к первому параметру вызова метода, т.е. isbn
。
Также есть примеры, содержащие условные выражения:
@Cacheable(value = "books", key = "#isbn", condition = "#checkWarehouse == true")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
только если checkWarehouse
Параметры true
кеш будет применен.
@Cacheable
默认из行для模式да Нет同步из。这意味着如果你существовать多нить环境середина使用它,и есть два или более нит, запрашивающих одни и те же данные одновременно,Тогда может произойти поломка кэша. То есть, да,Все запросы попадают в библиотеку данных,Потому что до того, как первый запрос заполнит кэш,Все остальные запросы не найдут элемент кэша.
Spring 4.1Появился новый объект недвижимостиsync
решить эту проблему。如果настраивать@Cacheable(sync=true)
,тогда только один нить выполнит метод и добавит результат в кэш,Другая нить подождет.
Вот пример кода:
@Service
public class BookService {
@Cacheable(cacheNames="books", sync=true)
public Book findBook(ISBN isbn) {
// Вот несколько медленных способов поиска книг, таких как запросы к библиотеке данных, вызовы API и т. д.
}
}
В этом примере,Независимо от того, сколько людей пытаются найти одну и ту же книгу, используя один и тот же ISBN.,только одиннитьдействительно будет реализованоfindBook
метод并将结果存储существовать名для"books"изкэшсередина。другойнитьбуду ждать,Затем получите результат из кэша,而Нет需要执行findBook
метод。
В разных конфигурациях Caffeine режим синхронизации ведет себя по-разному:
Тип кэша кофеина | Включить ли синхронизацию | Многопоточное чтение несуществующих/вытесненных ключей | Многопоточное чтение ключей, подлежащих обновлению |
---|---|---|---|
Cache | нет | Каждый выполняет аннотированный метод независимо. | - |
Cache | да | Поток 1 выполняет аннотированный метод, а поток 2 блокируется до завершения обновления кэша. | - |
LoadingCache | нет | Поток 1 выполняет функцию load(), а поток 2 блокируется до завершения обновления кэша. | Поток 1 немедленно возвращает значение, используя старое значение, и асинхронно обновляет кэшированное значение. Поток 2 возвращает значение немедленно, не обновляя его. |
LoadingCache | да | Поток 1 выполняет аннотированный метод, а поток 2 блокируется до завершения обновления кэша. | Поток 1 немедленно возвращает значение, используя старое значение, и асинхронно обновляет кэшированное значение. Поток 2 возвращает значение немедленно, не обновляя его. |
Содержание этой статьи подошло к концу. Наконец, я подведу краткое резюме. Надеюсь, эта статья принесет вам пользу и мысли.
В этой статье мы углубимся в кофеин. Кэш и его алгоритм Внутренняя работа исключения. Мы также подробно рассказываем, как интегрировать Caffeine в приложение SpringBoot. Кэш. Я надеюсь, что благодаря этой статье читатели смогут получить более глубокое представление о кофеине. Преимущества Cache и эффективное применение на практике.
В целом, Caffeine Cache не только предоставляет мощные функции кэширования, но и эффективную стратегию устранения. Это делает его очень хорошим выбором при работе с большими объемами данных или большим количеством одновременных запросов. Более того, благодаря хорошей совместимости со SpringBoot, вы можете быстро и легко использовать его в проектах SpringBoot.
Наконец, нам нужно помнить, что хотя кофеин Cache — мощный инструмент, но для правильного и эффективного использования требуется его алгоритм. имеет глубокое понимание. Поэтому рекомендуется продолжать уделять внимание и изучите последние достижения в этой области, чтобы лучше использовать кофеин. Кэш повышает производительность вашего приложения.