Preface
Caching can effectively improve the performance and stability of the system by storing frequently accessed data in memory, reducing the pressure on underlying data sources such as databases. I think everyone has used it more or less in their projects, and our project is no exception. However, when I was reviewing the company's code recently, the writing was very stupid and low. The rough writing is as follows:
public User getById(String id) { User user = cache.getUser(); if(user != null) { return user; } // 從數(shù)據(jù)庫獲取 user = loadFromDB(id); cahce.put(id, user); return user; }
In fact, Spring Boot provides a powerful caching abstraction that makes it easy to add caching to your application. This article will talk about how to use the different cache annotations provided by Spring to implement the best practices for caching.
Enable caching @EnableCaching
Now most projects are SpringBoot projects, we can add the annotation @EnableCaching
to the startup class to enable the caching function.
@SpringBootApplication @EnableCaching public class SpringCacheApp { public static void main(String[] args) { SpringApplication.run(Cache.class, args); } }
Since you want to be able to use cache, you need to have a cache manager Bean. By default, @EnableCaching
will register a ConcurrentMapCacheManager
Bean, no separate bean declaration. ConcurrentMapCacheManage
r stores the value in an instance of ConcurrentHashMap
, which is the simplest thread-safe implementation of the caching mechanism.
Custom cache manager
The default cache manager cannot meet the needs because it is stored in the jvm memory, so how to store it in redis? At this time you need to add a custom cache manager.
1. Add dependencies
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
2. Configure the Redis cache manager
@Configuration @EnableCaching public class CacheConfig { @Bean public RedisConnectionFactory redisConnectionFactory() { return new LettuceConnectionFactory(); } @Bean public CacheManager cacheManager() { RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig() .disableCachingNullValues() .serializeValuesWith(SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())); RedisCacheManager redisCacheManager = RedisCacheManager.builder(redisConnectionFactory()) .cacheDefaults(redisCacheConfiguration) .build(); return redisCacheManager; } }
Now that we have the cache manager, how do we operate the cache at the business level?
We can use the @Cacheable
, @CachePut
or @CacheEvict
annotation to operate the cache.
@Cacheable
This annotation can cache the results of method execution. When the method is called again within the cache time limit, the method itself will not be called, but the results will be obtained directly from the cache and returned to caller.
Example 1: Caching the results of database queries.
@Service public class MyService { @Autowired private MyRepository repository; @Cacheable(value = "myCache", key = "#id") public MyEntity getEntityById(Long id) { return repository.findById(id).orElse(null); } }
In this example, the @Cacheable
annotation is used to cache the results of the getEntityById()
method based on its ID
from the database Retrieve the MyEntity object from .
But what if we update the data? Old data still in cache?
@CachePut
Then @CachePut
came out. The difference from the @Cacheable
annotation is to use the @CachePut
annotation annotation. The method will not check whether there are previously executed results in the cache before execution. Instead, the method will be executed every time and the execution results will be written to the specified cache in the form of key-value pairs. @CachePut
Annotations are generally used to update cache data, which is equivalent to the cache using the double-write mode in the write mode.
@Service public class MyService { @Autowired private MyRepository repository; @CachePut(value = "myCache", key = "#entity.id") public void saveEntity(MyEntity entity) { repository.save(entity); } }
@CacheEvict
The method marked @CacheEvict
will remove the stored data from the cache when it is called. @CacheEvict
Annotations are generally used to delete cached data, which is equivalent to the cache using the failure mode in write mode.
@Service public class MyService { @Autowired private MyRepository repository; @CacheEvict(value = "myCache", key = "#id") public void deleteEntityById(Long id) { repository.deleteById(id); } }
@Caching
@Caching
Annotations are used to specify multiple Spring Cache related things on a method or class at the same time annotation.
Example 1: The evict
attribute specified in the @Caching
annotation becomes invalid when the method saveEntity
is called Two caches.
@Service public class MyService { @Autowired private MyRepository repository; @Cacheable(value = "myCache", key = "#id") public MyEntity getEntityById(Long id) { return repository.findById(id).orElse(null); } @Caching(evict = { @CacheEvict(value = "myCache", key = "#entity.id"), @CacheEvict(value = "otherCache", key = "#entity.id") }) public void saveEntity(MyEntity entity) { repository.save(entity); } }
Example 2: When calling the getEntityById
method, Spring will first check whether the result has been cached in the myCache
cache. If so, Spring
will return the cached result instead of executing the method. If the result is not already cached, Spring will execute the method and cache the result in the myCache
cache. After the method is executed, Spring will remove the cached result from the otherCache
cache based on the @CacheEvict
annotation.
@Service public class MyService { @Caching( cacheable = { @Cacheable(value = "myCache", key = "#id") }, evict = { @CacheEvict(value = "otherCache", key = "#id") } ) public MyEntity getEntityById(Long id) { return repository.findById(id).orElse(null); } }
Example 3: When calling the saveData
method, Spring will first remove data from the otherCache
cache based on the @CacheEvict
annotation. Spring will then execute the method and save the results to a database or external API. After the
method is executed, Spring will add the results to the myCache
, myOtherCache
and myThirdCache
caches based on the @CachePut
annotation. middle. Spring will also check whether the result has been cached in the myFourthCache
and myFifthCache
caches based on the @Cacheable
annotation. If the result is not already cached, Spring will cache the result in the appropriate cache. If the result is already cached, Spring will return the cached result instead of executing the method again.
@Service public class MyService { @Caching( put = { @CachePut(value = "myCache", key = "#result.id"), @CachePut(value = "myOtherCache", key = "#result.id"), @CachePut(value = "myThirdCache", key = "#result.name") }, evict = { @CacheEvict(value = "otherCache", key = "#id") }, cacheable = { @Cacheable(value = "myFourthCache", key = "#id"), @Cacheable(value = "myFifthCache", key = "#result.id") } ) public MyEntity saveData(Long id, String name) { // Code to save data to a database or external API MyEntity entity = new MyEntity(id, name); return entity; } }
@CacheConfig
通過@CacheConfig
注解,我們可以將一些緩存配置簡化到類級別的一個地方,這樣我們就不必多次聲明相關(guān)值:
@CacheConfig(cacheNames={"myCache"}) @Service public class MyService { @Autowired private MyRepository repository; @Cacheable(key = "#id") public MyEntity getEntityById(Long id) { return repository.findById(id).orElse(null); } @CachePut(key = "#entity.id") public void saveEntity(MyEntity entity) { repository.save(entity); } @CacheEvict(key = "#id") public void deleteEntityById(Long id) { repository.deleteById(id); } }
Condition & Unless
condition
作用:指定緩存的條件(滿足什么條件才緩存),可用SpEL
表達(dá)式(如#id>0
,表示當(dāng)入?yún)?id 大于 0 時才緩存)unless
作用 : 否定緩存,即滿足unless
指定的條件時,方法的結(jié)果不進(jìn)行緩存,使用unless
時可以在調(diào)用的方法獲取到結(jié)果之后再進(jìn)行判斷(如 #result == null,表示如果結(jié)果為 null 時不緩存)
//when id >10, the @CachePut works. @CachePut(key = "#entity.id", condition="#entity.id > 10") public void saveEntity(MyEntity entity) { repository.save(entity); } //when result != null, the @CachePut works. @CachePut(key = "#id", condition="#result == null") public void saveEntity1(MyEntity entity) { repository.save(entity); }
清理全部緩存
通過allEntries
、beforeInvocation
屬性可以來清除全部緩存數(shù)據(jù),不過allEntries
是方法調(diào)用后清理,beforeInvocation
是方法調(diào)用前清理。
//方法調(diào)用完成之后,清理所有緩存 @CacheEvict(value="myCache",allEntries=true) public void delectAll() { repository.deleteAll(); } //方法調(diào)用之前,清除所有緩存 @CacheEvict(value="myCache",beforeInvocation=true) public void delectAll() { repository.deleteAll(); }
SpEL表達(dá)式
Spring Cache注解中頻繁用到SpEL表達(dá)式,那么具體如何使用呢?
SpEL 表達(dá)式的語法
Spring Cache可用的變量
最佳實踐
通過Spring
緩存注解可以快速優(yōu)雅地在我們項目中實現(xiàn)緩存的操作,但是在雙寫模式或者失效模式下,可能會出現(xiàn)緩存數(shù)據(jù)一致性問題(讀取到臟數(shù)據(jù)),Spring Cache
?暫時沒辦法解決。最后我們再總結(jié)下Spring Cache使用的一些最佳實踐。
只緩存經(jīng)常讀取的數(shù)據(jù):緩存可以顯著提高性能,但只緩存經(jīng)常訪問的數(shù)據(jù)很重要。很少或從不訪問的緩存數(shù)據(jù)會占用寶貴的內(nèi)存資源,從而導(dǎo)致性能問題。
根據(jù)應(yīng)用程序的特定需求選擇合適的緩存提供程序和策略。
SpringBoot
?支持多種緩存提供程序,包括?Ehcache
、Hazelcast
?和?Redis
。使用緩存時請注意潛在的線程安全問題。對緩存的并發(fā)訪問可能會導(dǎo)致數(shù)據(jù)不一致或不正確,因此選擇線程安全的緩存提供程序并在必要時使用適當(dāng)?shù)耐綑C(jī)制非常重要。
避免過度緩存。緩存對于提高性能很有用,但過多的緩存實際上會消耗寶貴的內(nèi)存資源,從而損害性能。在緩存頻繁使用的數(shù)據(jù)和允許垃圾收集不常用的數(shù)據(jù)之間取得平衡很重要。
使用適當(dāng)?shù)木彺嬷鸪霾呗?。使用緩存時,重要的是定義適當(dāng)?shù)木彺嬷鸪霾呗砸源_保在必要時從緩存中刪除舊的或陳舊的數(shù)據(jù)。
使用適當(dāng)?shù)木彺骀I設(shè)計。緩存鍵對于每個數(shù)據(jù)項都應(yīng)該是唯一的,并且應(yīng)該考慮可能影響緩存數(shù)據(jù)的任何相關(guān)參數(shù),例如用戶 ID、時間或位置。
常規(guī)數(shù)據(jù)(讀多寫少、即時性與一致性要求不高的數(shù)據(jù))完全可以使用 Spring Cache,至于寫模式下緩存數(shù)據(jù)一致性問題的解決,只要緩存數(shù)據(jù)有設(shè)置過期時間就足夠了。
特殊數(shù)據(jù)(讀多寫多、即時性與一致性要求非常高的數(shù)據(jù)),不能使用 Spring Cache,建議考慮特殊的設(shè)計(例如使用 Cancal 中間件等)。
The above is the detailed content of How to use cache in SpringBoot project. For more information, please follow other related articles on the PHP Chinese website!

Hot AI Tools

Undress AI Tool
Undress images for free

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Introduction to Jasypt Jasypt is a java library that allows a developer to add basic encryption functionality to his/her project with minimal effort and does not require a deep understanding of how encryption works. High security for one-way and two-way encryption. , standards-based encryption technology. Encrypt passwords, text, numbers, binaries... Suitable for integration into Spring-based applications, open API, for use with any JCE provider... Add the following dependency: com.github.ulisesbocchiojasypt-spring-boot-starter2. 1.1Jasypt benefits protect our system security. Even if the code is leaked, the data source can be guaranteed.

1. Redis implements distributed lock principle and why distributed locks are needed. Before talking about distributed locks, it is necessary to explain why distributed locks are needed. The opposite of distributed locks is stand-alone locks. When we write multi-threaded programs, we avoid data problems caused by operating a shared variable at the same time. We usually use a lock to mutually exclude the shared variables to ensure the correctness of the shared variables. Its scope of use is in the same process. If there are multiple processes that need to operate a shared resource at the same time, how can they be mutually exclusive? Today's business applications are usually microservice architecture, which also means that one application will deploy multiple processes. If multiple processes need to modify the same row of records in MySQL, in order to avoid dirty data caused by out-of-order operations, distribution needs to be introduced at this time. The style is locked. Want to achieve points

1. Customize RedisTemplate1.1, RedisAPI default serialization mechanism. The API-based Redis cache implementation uses the RedisTemplate template for data caching operations. Here, open the RedisTemplate class and view the source code information of the class. publicclassRedisTemplateextendsRedisAccessorimplementsRedisOperations, BeanClassLoaderAware{//Declare key, Various serialization methods of value, the initial value is empty @NullableprivateRedisSe

Springboot reads the file, but cannot access the latest development after packaging it into a jar package. There is a situation where springboot cannot read the file after packaging it into a jar package. The reason is that after packaging, the virtual path of the file is invalid and can only be accessed through the stream. Read. The file is under resources publicvoidtest(){Listnames=newArrayList();InputStreamReaderread=null;try{ClassPathResourceresource=newClassPathResource("name.txt");Input

Usage scenario 1. The order was placed successfully but the payment was not made within 30 minutes. The payment timed out and the order was automatically canceled. 2. The order was signed and no evaluation was conducted for 7 days after signing. If the order times out and is not evaluated, the system defaults to a positive rating. 3. The order is placed successfully. If the merchant does not receive the order for 5 minutes, the order is cancelled. 4. The delivery times out, and push SMS reminder... For scenarios with long delays and low real-time performance, we can Use task scheduling to perform regular polling processing. For example: xxl-job Today we will pick

When Springboot+Mybatis-plus does not use SQL statements to perform multi-table adding operations, the problems I encountered are decomposed by simulating thinking in the test environment: Create a BrandDTO object with parameters to simulate passing parameters to the background. We all know that it is extremely difficult to perform multi-table operations in Mybatis-plus. If you do not use tools such as Mybatis-plus-join, you can only configure the corresponding Mapper.xml file and configure The smelly and long ResultMap, and then write the corresponding sql statement. Although this method seems cumbersome, it is highly flexible and allows us to

SpringBoot and SpringMVC are both commonly used frameworks in Java development, but there are some obvious differences between them. This article will explore the features and uses of these two frameworks and compare their differences. First, let's learn about SpringBoot. SpringBoot was developed by the Pivotal team to simplify the creation and deployment of applications based on the Spring framework. It provides a fast, lightweight way to build stand-alone, executable

1. @Import introduces ordinary classes @Import introduces ordinary classes can help us define ordinary classes as Beans. @Import can be added to the classes corresponding to @SpringBootApplication (startup class), @Configuration (configuration class), and @Component (component class). Note: @RestController, @Service, and @Repository all belong to @Component@SpringBootApplication@Import(ImportBean.class)//ImportBean through the @Import annotation
