Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Кеширование

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

Подключение

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

Кеш включает @EnableCaching — мы уже поставили её в AppConfig в прошлом уроке. Без дополнительных библиотек Spring Boot хранит кеш в обычной ConcurrentHashMap в памяти приложения.

Запомнить результат

// src/main/java/com/example/blog/PostService.java
@Cacheable("posts")
public PostResponse findById(Long id) {
    return PostResponse.from(getPost(id));
}

@Cacheable("posts") — кеш с именем posts. Ключом служит аргумент id, значением — результат. Удобно, что кешируется запись PostResponse, а не сущность: она неизменяемая и не зависит от соединения с базой.

Сбросить устаревшее

Если статью изменили или удалили, старый ответ из кеша надо выбросить:

// src/main/java/com/example/blog/PostService.java
@Transactional
@CacheEvict(value = "posts", key = "#id")
public PostResponse update(Long id, NewPostRequest request) {
    // ...
}
 
@Transactional
@CacheEvict(value = "posts", key = "#id")
public void delete(Long id) {
    posts.delete(getPost(id));
}

key = "#id" — выражение: взять значение параметра id. После вызова метода запись с этим ключом пропадёт из кеша, и следующий findById снова сходит в базу.

Проверка

Запросим одну статью трижды и посмотрим журнал с show-sql: true:

curl http://localhost:8080/api/posts/65
curl http://localhost:8080/api/posts/65
curl http://localhost:8080/api/posts/65
Hibernate: select p1_0.id,p1_0.author_id,p1_0.body,p1_0.created_at,p1_0.title from posts p1_0 where p1_0.id=?
Hibernate: select u1_0.id,u1_0.password,u1_0.role,u1_0.username from users u1_0 where u1_0.id=?

Три запроса — и только одна пара SQL-запросов: статья и её автор. Второй и третий ответы пришли из кеша.

Аннотации кеша

АннотацияЧто делает
@CacheableВозвращает значение из кеша или вызывает метод и запоминает результат
@CacheEvictУдаляет значение; allEntries = true очищает весь кеш
@CachePutВсегда вызывает метод и обновляет значение в кеше

Кеш в памяти пропадает при перезапуске и у каждого экземпляра приложения свой. Если серверов несколько, подключают общий кеш — например, Redis через стартер spring-boot-starter-data-redis; аннотации при этом не меняются.

Частая ошибка

Кеш, как и транзакции, работает через посредника Spring. Вызов findById из другого метода того же PostService пойдёт мимо кеша — каждый раз в базу. Кеш срабатывает, только когда метод вызывают снаружи, из другого бина, например из контроллера. И не забывайте @CacheEvict: без него после изменения статьи читатели будут видеть старый заголовок до перезапуска.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.