使用Spring Cache和Redis實(shí)現(xiàn)查詢數(shù)據(jù)緩存
1. 前言
在現(xiàn)代應(yīng)用程序中,查詢緩存的使用已經(jīng)變得越來(lái)越普遍。它不僅能夠顯著提高系統(tǒng)的性能,還能提升用戶體驗(yàn)。緩存通過(guò)在內(nèi)存中存儲(chǔ)頻繁訪問(wèn)的數(shù)據(jù),減少對(duì)數(shù)據(jù)庫(kù)或其他存儲(chǔ)系統(tǒng)的訪問(wèn),從而加快數(shù)據(jù)讀取速度。在這篇文章中,我們將探討緩存的基本概念、重要性以及如何使用Spring Cache和Redis實(shí)現(xiàn)查詢數(shù)據(jù)緩存 。
2. 緩存
2.1 什么是緩存
緩存是一種臨時(shí)存儲(chǔ)機(jī)制,用于在內(nèi)存中保存頻繁訪問(wèn)的數(shù)據(jù)。它可以是硬件(如CPU緩存)或軟件(如應(yīng)用程序緩存)。緩存的主要目的是通過(guò)減少數(shù)據(jù)訪問(wèn)的延遲,提高系統(tǒng)的響應(yīng)速度。以下是緩存的一些關(guān)鍵特性:
- 臨時(shí)性:緩存中的數(shù)據(jù)通常是臨時(shí)的,會(huì)在一段時(shí)間后失效或被替換。
- 快速訪問(wèn):由于緩存數(shù)據(jù)存儲(chǔ)在內(nèi)存中,訪問(wèn)速度非??臁?/li>
- 空間有限:緩存的存儲(chǔ)空間通常有限,因此需要有效的管理策略,如LRU(最近最少使用)策略。
2.2 使用緩存的好處
- 提高性能:緩存可以顯著減少數(shù)據(jù)讀取的時(shí)間,因?yàn)閮?nèi)存訪問(wèn)速度比硬盤或網(wǎng)絡(luò)存儲(chǔ)快很多。
- 減輕數(shù)據(jù)庫(kù)負(fù)載:緩存可以減少數(shù)據(jù)庫(kù)的查詢次數(shù),從而減輕數(shù)據(jù)庫(kù)的負(fù)載,提升整體系統(tǒng)的穩(wěn)定性和可擴(kuò)展性。
- 節(jié)省資源:通過(guò)減少對(duì)后端系統(tǒng)的訪問(wèn),緩存可以幫助節(jié)省帶寬和計(jì)算資源。
- 提高用戶體驗(yàn):快速的數(shù)據(jù)訪問(wèn)可以顯著提升用戶體驗(yàn),特別是在需要頻繁讀取數(shù)據(jù)的應(yīng)用場(chǎng)景中。
2.3 緩存的成本
- 內(nèi)存消耗:緩存需要占用系統(tǒng)的內(nèi)存資源,過(guò)多的緩存可能會(huì)影響其他應(yīng)用程序的性能。
- 數(shù)據(jù)一致性:緩存中的數(shù)據(jù)可能會(huì)與數(shù)據(jù)庫(kù)中的數(shù)據(jù)不一致,尤其是在數(shù)據(jù)頻繁更新的場(chǎng)景中。需要設(shè)計(jì)有效的緩存失效策略來(lái)保證數(shù)據(jù)的一致性。
- 復(fù)雜性增加:引入緩存機(jī)制會(huì)增加系統(tǒng)的復(fù)雜性,需要處理緩存的管理、更新和失效等問(wèn)題。
- 維護(hù)成本:緩存系統(tǒng)需要定期監(jiān)控和維護(hù),以確保其高效運(yùn)行。
2.4 Spring Cache和Redis的優(yōu)點(diǎn)
為了實(shí)現(xiàn)高效的數(shù)據(jù)緩存,Spring Boot提供了Spring Cache模塊,而Redis則是一個(gè)強(qiáng)大的緩存數(shù)據(jù)庫(kù)。結(jié)合使用Spring Cache和Redis,能夠充分發(fā)揮二者的優(yōu)點(diǎn),實(shí)現(xiàn)高效的數(shù)據(jù)緩存。
- Spring Cache的優(yōu)點(diǎn):
- 簡(jiǎn)化緩存操作:Spring Cache提供了一系列注解(如
@Cacheable
、@CachePut
、@CacheEvict
),簡(jiǎn)化了緩存的使用,使開發(fā)者能夠?qū)W⒂跇I(yè)務(wù)邏輯。 - 靈活的緩存管理:Spring Cache支持多種緩存提供者(如EhCache、Hazelcast、Redis等),可以根據(jù)具體需求選擇合適的緩存實(shí)現(xiàn)。
- 透明的緩存機(jī)制:Spring Cache使得緩存操作對(duì)業(yè)務(wù)代碼透明,開發(fā)者無(wú)需關(guān)心緩存的具體實(shí)現(xiàn)細(xì)節(jié)。
- 簡(jiǎn)化緩存操作:Spring Cache提供了一系列注解(如
- Redis的優(yōu)點(diǎn):
- 高性能:由于數(shù)據(jù)存儲(chǔ)在內(nèi)存中,Redis的讀寫速度非常快,能夠處理每秒數(shù)百萬(wàn)級(jí)別的請(qǐng)求。
- 豐富的數(shù)據(jù)結(jié)構(gòu):Redis支持多種數(shù)據(jù)結(jié)構(gòu),如字符串、哈希、列表、集合、有序集合等,能夠滿足不同場(chǎng)景下的數(shù)據(jù)存儲(chǔ)需求。
- 持久化支持:雖然Redis主要用于內(nèi)存存儲(chǔ),但它也提供了數(shù)據(jù)持久化的功能,可以將數(shù)據(jù)定期保存到磁盤,防止數(shù)據(jù)丟失。
- 分布式支持:Redis支持主從復(fù)制、哨兵模式和集群模式,能夠?qū)崿F(xiàn)高可用性和數(shù)據(jù)的水平擴(kuò)展。
- 靈活的過(guò)期策略:Redis支持為每個(gè)鍵設(shè)置過(guò)期時(shí)間,自動(dòng)刪除過(guò)期數(shù)據(jù),方便實(shí)現(xiàn)緩存失效策略。
3. Spring Cache基礎(chǔ)知識(shí)
在Spring Boot中,Spring Cache提供了一套簡(jiǎn)潔且強(qiáng)大的緩存抽象機(jī)制,幫助開發(fā)者輕松地將緩存集成到應(yīng)用程序中。以下是Spring Cache的一些核心概念和常用注解。
3.1 Spring Cache的核心概念
CacheManager
- 定義:
CacheManager
是Spring Cache的核心接口,負(fù)責(zé)管理多個(gè)緩存實(shí)例。它是緩存操作的入口點(diǎn),提供了獲取和操作緩存實(shí)例的方法。 - 實(shí)現(xiàn):Spring提供了多種
CacheManager
實(shí)現(xiàn),如ConcurrentMapCacheManager
、EhCacheCacheManager
、RedisCacheManager
等。不同的實(shí)現(xiàn)適用于不同的緩存存儲(chǔ)機(jī)制。
Cache
- 定義:
Cache
是緩存的具體實(shí)現(xiàn),負(fù)責(zé)存儲(chǔ)和檢索緩存數(shù)據(jù)。它提供了基本的緩存操作,如put
、get
、evict
等。 - 實(shí)現(xiàn):具體的
Cache
實(shí)現(xiàn)依賴于底層的緩存存儲(chǔ)機(jī)制,如內(nèi)存緩存、Redis緩存等。
3.2 Spring Cache的注解
3.2.1 SpEL表達(dá)式
因?yàn)镾pring Cache使用SpEL表達(dá)式來(lái)動(dòng)態(tài)生成緩存鍵,所以在學(xué)習(xí)Spring Cache的注解之前我們還要先簡(jiǎn)單了解一下SpEL表達(dá)式的語(yǔ)法,這部分可以先不看懂,在后面看注解的時(shí)候回來(lái)看即可。
SpEL表達(dá)式的語(yǔ)法類似于Java的表達(dá)式語(yǔ)法,支持以下幾種操作:
- 字面量:
- 數(shù)字:
1
,2.5
- 字符串:
'hello'
,"world"
- 布爾值:
true
,false
- 空值:
null
- 數(shù)字:
- 屬性和方法:
- 訪問(wèn)對(duì)象的屬性:
#user.name
- 調(diào)用對(duì)象的方法:
#user.getName()
- 訪問(wèn)對(duì)象的屬性:
- 運(yùn)算符:
- 算術(shù)運(yùn)算:
+
,-
,*
,/
,%
- 比較運(yùn)算:
==
,!=
,<
,>
,<=
,>=
- 邏輯運(yùn)算:
&&
,||
,!
- 算術(shù)運(yùn)算:
- 集合和數(shù)組:
- 訪問(wèn)集合元素:
#users[0]
- 集合操作:
#users.size()
,#users.isEmpty()
- 訪問(wèn)集合元素:
- 條件運(yùn)算符:
- 三元運(yùn)算符:
condition ? trueValue : falseValue
- Elvis運(yùn)算符:
expression ?: defaultValue
- 三元運(yùn)算符:
- 變量:
- 定義和使用變量:
#variableName
- 定義和使用變量:
接下來(lái)進(jìn)入Spring Cache注解的學(xué)習(xí):
3.2.2 @Cacheable
作用:@Cacheable
注解用于標(biāo)注需要緩存的方法。當(dāng)該方法被調(diào)用時(shí),Spring Cache會(huì)先檢查緩存中是否存在對(duì)應(yīng)的數(shù)據(jù)。如果存在,則直接返回緩存數(shù)據(jù);如果不存在,則執(zhí)行方法并將結(jié)果存入緩存。
示例:
@RestController("/users") @RequiredArgsConstructor public class UserController { private final UserService userService; @Cacheable(value = "user", key = "#id") public User getUser(Long id) { // 獲取用戶的邏輯 return userService.findById(id); } }
- 參數(shù):
value
:指定緩存的名稱。key
:指定緩存的鍵,可以使用SpEL表達(dá)式。
3.2.3 @CachePut
- 作用:
@CachePut
注解用于標(biāo)注需要更新緩存的方法。即使緩存中已經(jīng)存在數(shù)據(jù),該方法仍然會(huì)執(zhí)行,并將結(jié)果更新到緩存中。 - 示例:
@RestController("/users") @RequiredArgsConstructor public class UserController { private final UserService userService; @CachePut(value = "user", key = "#user.id") public User updateUser(User user) { // 更新用戶的邏輯 return userService.save(user); } }
- 參數(shù):
value
:指定緩存的名稱。key
:指定緩存的鍵,可以使用SpEL表達(dá)式。
3.2.4 @CacheEvict
作用:@CacheEvict
注解用于標(biāo)注需要清除緩存的方法。當(dāng)該方法被調(diào)用時(shí),Spring Cache會(huì)清除對(duì)應(yīng)的緩存數(shù)據(jù)。
示例:
@RestController("/users") @RequiredArgsConstructor public class UserController { private final UserService userService; @CacheEvict(value = "user", key = "#id") public void deleteUser(Long id) { // 刪除用戶的邏輯 userService.deleteById(id); } }
- 參數(shù):
value
:指定緩存的名稱。key
:指定緩存的鍵,可以使用SpEL表達(dá)式。allEntries
:如果設(shè)置為true
,則清除緩存中的所有數(shù)據(jù)。
4. 實(shí)現(xiàn)查詢數(shù)據(jù)緩存
4.1 準(zhǔn)備工作 Redis安裝與配置:
- Redis安裝與配置:
這里可以自行查找文章進(jìn)行安裝和配置,網(wǎng)上優(yōu)質(zhì)文章很多。
創(chuàng)建Product
實(shí)體類:
@Data @AllArgsConstructor public class Product implements Serializable { private Long id; private String name; private Integer category; private String description; private Integer stock; }
創(chuàng)建枚舉類ResultEnum
:
@Getter public enum ResultEnum { /* 成功狀態(tài)碼 */ SUCCESS(1, "操作成功!"), /* 錯(cuò)誤狀態(tài)碼 */ FAIL(0, "操作失敗!"), /* 參數(shù)錯(cuò)誤:10001-19999 */ PARAM_IS_INVALID(10001, "參數(shù)無(wú)效"), PARAM_IS_BLANK(10002, "參數(shù)為空"), PARAM_TYPE_BIND_ERROR(10003, "參數(shù)格式錯(cuò)誤"), PARAM_NOT_COMPLETE(10004, "參數(shù)缺失"), /* 用戶錯(cuò)誤:20001-29999*/ USER_NOT_LOGGED_IN(20001, "用戶未登錄,請(qǐng)先登錄"), USER_LOGIN_ERROR(20002, "賬號(hào)不存在或密碼錯(cuò)誤"), USER_ACCOUNT_FORBIDDEN(20003, "賬號(hào)已被禁用"), USER_NOT_EXIST(20004, "用戶不存在"), USER_HAS_EXISTED(20005, "用戶已存在"), /* 系統(tǒng)錯(cuò)誤:40001-49999 */ FILE_MAX_SIZE_OVERFLOW(40003, "上傳尺寸過(guò)大"), FILE_ACCEPT_NOT_SUPPORT(40004, "上傳文件格式不支持"), /* 數(shù)據(jù)錯(cuò)誤:50001-599999 */ RESULT_DATA_NONE(50001, "數(shù)據(jù)未找到"), DATA_IS_WRONG(50002, "數(shù)據(jù)有誤"), DATA_ALREADY_EXISTED(50003, "數(shù)據(jù)已存在"), AUTH_CODE_ERROR(50004, "驗(yàn)證碼錯(cuò)誤"), /* 權(quán)限錯(cuò)誤:70001-79999 */ PERMISSION_UNAUTHENTICATED(70001, "此操作需要登陸系統(tǒng)!"), PERMISSION_UNAUTHORIZED(70002, "權(quán)限不足,無(wú)權(quán)操作!"), PERMISSION_EXPIRE(70003, "登錄狀態(tài)過(guò)期!"), PERMISSION_TOKEN_EXPIRED(70004, "token已過(guò)期"), PERMISSION_LIMIT(70005, "訪問(wèn)次數(shù)受限制"), PERMISSION_TOKEN_INVALID(70006, "無(wú)效token"), PERMISSION_SIGNATURE_ERROR(70007, "簽名失敗"); // 狀態(tài)碼 int code; // 提示信息 String message; ResultEnum(int code, String message) { this.code = code; this.message = message; } public int code() { return code; } public String message() { return message; } public void setCode(int code) { this.code = code; } public void setMessage(String message) { this.message = message; } }
創(chuàng)建統(tǒng)一返回結(jié)果封裝類Result
:
@Data @NoArgsConstructor public class Result<T> implements Serializable { // 操作代碼 Integer code; // 提示信息 String message; // 結(jié)果數(shù)據(jù) T data; public Result(ResultEnum resultCode) { this.code = resultCode.code(); this.message = resultCode.message(); } public Result(ResultEnum resultCode, T data) { this.code = resultCode.code(); this.message = resultCode.message(); this.data = data; } public Result(String message) { this.message = message; } //成功返回封裝-無(wú)數(shù)據(jù) public static Result<String> success() { return new Result<String>(ResultEnum.SUCCESS); } //成功返回封裝-帶數(shù)據(jù) public static <T> Result<T> success(T data) { return new Result<T>(ResultEnum.SUCCESS, data); } //失敗返回封裝-使用默認(rèn)提示信息 public static Result<String> error() { return new Result<String>(ResultEnum.FAIL); } //失敗返回封裝-使用返回結(jié)果枚舉提示信息 public static Result<String> error(ResultEnum resultCode) { return new Result<String>(resultCode); } //失敗返回封裝-使用自定義提示信息 public static Result<String> error(String message) { return new Result<String>(message); } }
4.2 添加依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
4.3 修改配置文件
spring: data: redis: # Redis服務(wù)器地址 host: ${shijun.redis.host} # Redis服務(wù)器端口 port: ${shijun.redis.port} # Redis服務(wù)器認(rèn)證密碼 password: ${shijun.redis.password} # Redis數(shù)據(jù)庫(kù)索引 database: ${shijun.redis.database}
4.4 配置緩存管理器
/** * 配置類,用于設(shè)置緩存管理器及相關(guān)配置,以啟用緩存功能 * * @author shijun * @date 2024/06/13 */ @EnableCaching @Configuration public class CacheConfig extends CachingConfigurerSupport { /** * 配置Redis鍵的序列化方式 * * @return StringRedisSerializer,用于序列化和反序列化Redis中的鍵 */ private RedisSerializer<String> keySerializer() { return new StringRedisSerializer(); } /** * 配置Redis值的序列化方式 * * @return GenericJackson2JsonRedisSerializer,使用Jackson庫(kù)以JSON格式序列化和反序列化Redis中的值 */ private RedisSerializer<Object> valueSerializer() { return new GenericJackson2JsonRedisSerializer(); } /** * 緩存前綴,用于區(qū)分不同的緩存命名空間,一般以模塊名或者服務(wù)名命名,這里暫時(shí)寫cache */ public static final String CACHE_PREFIX = "cache:"; /** * 配置緩存管理器,使用Redis作為緩存后端 * * @param redisConnectionFactory Redis連接工廠,用于創(chuàng)建Redis連接 * @return RedisCacheManager,Redis緩存管理器實(shí)例 */ @Bean public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) { // 配置序列化,解決亂碼的問(wèn)題,設(shè)置緩存名稱的前綴和緩存條目的默認(rèn)過(guò)期時(shí)間 RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() // 設(shè)置鍵的序列化器 .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer())) // 設(shè)置值的序列化器 .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer())) // 設(shè)置緩存名稱的前綴 .computePrefixWith(name -> CACHE_PREFIX + name + ":") // 設(shè)置緩存條目的默認(rèn)過(guò)期時(shí)間為300秒 .entryTtl(Duration.ofSeconds(300)); // 創(chuàng)建非鎖定的Redis緩存寫入器 RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(Objects.requireNonNull(redisConnectionFactory)); // 返回Redis緩存管理器實(shí)例,使用上述配置 return new RedisCacheManager(redisCacheWriter, config); } }
分析:
StringRedisSerializer :使用StringRedisSerializer
將緩存的鍵序列化為字符串。因?yàn)镽edis中的鍵通常是字符串類型,使用字符串序列化器可以確保鍵在Redis中以可讀的形式存儲(chǔ),便于調(diào)試和管理。GenericJackson2JsonRedisSerializer :使用
GenericJackson2JsonRedisSerializer
將緩存的值序列化為JSON格式,可讀性高并且便于人工排查數(shù)據(jù)。
4.5 使用Spring Cache注解
由于我們的緩存的數(shù)據(jù)源來(lái)自于數(shù)據(jù)庫(kù),而數(shù)據(jù)庫(kù)的數(shù)據(jù)是會(huì)發(fā)生變化的,因此,如果當(dāng)數(shù)據(jù)庫(kù)中數(shù)據(jù)發(fā)生變化,而緩存卻沒(méi)有同步,此時(shí)就會(huì)有數(shù)據(jù)一致性問(wèn)題存在,在一些并發(fā)場(chǎng)景會(huì)出現(xiàn)問(wèn)題。
這里采用
Cache Aside Pattern
即旁路緩存模式:緩存調(diào)用者在更新完數(shù)據(jù)庫(kù)后再去更新緩存,也稱之為雙寫方案。
分析:
- 應(yīng)用程序首先從緩存中查找數(shù)據(jù)。
- 如果緩存命中,則直接返回緩存中的數(shù)據(jù)。
- 如果緩存未命中,則從數(shù)據(jù)庫(kù)中讀取數(shù)據(jù),并將讀取到的數(shù)據(jù)寫入緩存,以便
- 后續(xù)請(qǐng)求可以直接從緩存中獲取。
- 寫流程
分析:
- 應(yīng)用程序首先更新數(shù)據(jù)庫(kù)中的數(shù)據(jù)。
- 然后使緩存中的對(duì)應(yīng)數(shù)據(jù)失效
@Slf4j @RestController("/products") public class ProductController { /** * 根據(jù)ID獲取產(chǎn)品信息 * 通過(guò)@Cacheable注解,當(dāng)請(qǐng)求的產(chǎn)品ID在緩存中存在時(shí),直接從緩存中獲取產(chǎn)品信息,減少數(shù)據(jù)庫(kù)查詢 * * @param id 產(chǎn)品ID * @return 返回查詢結(jié)果,包含指定ID的產(chǎn)品信息 */ @GetMapping("/getProductById") @Cacheable(value = "productsCache", key = "#id") public Result<Product> getProductById(Long id) { // 當(dāng)從數(shù)據(jù)庫(kù)獲取數(shù)據(jù)時(shí)會(huì)打印,如果是從緩存中查詢并不會(huì)執(zhí)行到這里。 log.info("從數(shù)據(jù)庫(kù)獲取產(chǎn)品: id = {}", id); Product product = new Product(id, "product", 100, "課本", 10); return Result.success(product); } /** * 更新產(chǎn)品信息 * 通過(guò)@CacheEvict注解,當(dāng)更新產(chǎn)品時(shí),清除緩存中對(duì)應(yīng)產(chǎn)品的數(shù)據(jù),確保獲取到最新的數(shù)據(jù) * 設(shè)置allEntries為true,表示清除整個(gè)緩存中的所有產(chǎn)品數(shù)據(jù) * * @param product 產(chǎn)品對(duì)象,包含更新后的詳細(xì)信息 * @return 返回更新結(jié)果,成功更新時(shí)返回成功標(biāo)志 */ @PutMapping("/updateProduct") @CacheEvict(value = "productsCache", key = "#product.id") public Result updateProduct(@RequestBody Product product) { // 更新操作 return Result.success(); } /** * 刪除指定ID的產(chǎn)品 * 通過(guò)@CacheEvict注解,當(dāng)刪除產(chǎn)品時(shí),清除緩存中對(duì)應(yīng)產(chǎn)品的數(shù)據(jù) * * @param id 待刪除產(chǎn)品的ID * @return 返回刪除結(jié)果,成功刪除時(shí)返回成功標(biāo)志 */ @DeleteMapping("/deleteProductById") @CacheEvict(value = "productsCache", key = "#id") public Result deleteProductById(Long id) { // 刪除操作 return Result.success(); } }
4.6 測(cè)試
4.6.1 查詢測(cè)試
因?yàn)槲覀冊(cè)诓樵兘涌谏鲜褂玫?code>@Cacheable接口,所以當(dāng)執(zhí)行查詢操作時(shí),第一次查詢會(huì)從數(shù)據(jù)庫(kù)中獲取,因此會(huì)輸出「從數(shù)據(jù)庫(kù)獲取產(chǎn)品: id = x」,此時(shí)查看Redis控制臺(tái)會(huì)發(fā)現(xiàn)出現(xiàn)一個(gè)對(duì)應(yīng)的緩存,之后的每次查詢都會(huì)從Redis中查詢(控制臺(tái)不會(huì)輸出「從數(shù)據(jù)庫(kù)獲取產(chǎn)品: id = x」),直到對(duì)應(yīng)的緩存數(shù)據(jù)時(shí)間結(jié)束。
發(fā)送查詢請(qǐng)求:
查看Redis中的緩存數(shù)據(jù):
通過(guò)觀察可以發(fā)現(xiàn)
CacheConfig
類中的序列化配置起作用了,Redis中的數(shù)據(jù)不再是一堆亂碼,并且在右上角還有我們之前配置的緩存的過(guò)期時(shí)間(我們之前配置的300s)。
查看控制臺(tái)發(fā)現(xiàn)本次查詢?yōu)閺臄?shù)據(jù)庫(kù)查詢:
再次發(fā)送會(huì)發(fā)現(xiàn)數(shù)據(jù)成功的查詢了:
再次查詢控制臺(tái)發(fā)現(xiàn)并沒(méi)有輸出從數(shù)據(jù)庫(kù)獲取產(chǎn)品: id = 1
,說(shuō)明本次查詢?yōu)閺腞edis緩存中獲取數(shù)據(jù)。
4.6.2 更新、刪除測(cè)試
因?yàn)橹拔覀冊(cè)诟潞蛣h除接口上使用的@CacheEvict注解,所以當(dāng)執(zhí)行更新或者刪除操作時(shí),會(huì)將Redis中對(duì)應(yīng)的產(chǎn)品緩存數(shù)據(jù)刪除。
分別發(fā)送更新請(qǐng)求和刪除請(qǐng)求,然后再次查看Redis中的緩存數(shù)據(jù):
可以發(fā)現(xiàn)Redis當(dāng)中對(duì)應(yīng)的緩存數(shù)據(jù)被刪除了,符合我們的設(shè)計(jì):
5. 總結(jié)
在本文中,我們?cè)敿?xì)介紹了如何在Spring Boot項(xiàng)目中使用Spring Cache和Redis實(shí)現(xiàn)數(shù)據(jù)緩存,并簡(jiǎn)單講解了使用Cache Aside Pattern
來(lái)解決數(shù)據(jù)一致性問(wèn)題,希望對(duì)大家學(xué)習(xí)有所幫助。如有問(wèn)題,大家可以私信或者在評(píng)論區(qū)詢問(wèn)。
以上就是使用Spring Cache和Redis實(shí)現(xiàn)查詢數(shù)據(jù)緩存的詳細(xì)內(nèi)容,更多關(guān)于Spring Cache Redis查詢數(shù)據(jù)緩存的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java使用正則表達(dá)式截取重復(fù)出現(xiàn)的XML字符串功能示例
這篇文章主要介紹了Java使用正則表達(dá)式截取重復(fù)出現(xiàn)的XML字符串功能,涉及java針對(duì)xml字符串及指定格式字符串的正則匹配相關(guān)操作技巧,需要的朋友可以參考下2017-08-08解決java 查看JDK中底層源碼的實(shí)現(xiàn)方法
本篇文章是對(duì)在java中查看JDK中底層源碼的解決方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05SpringBoot整合MyCat實(shí)現(xiàn)讀寫分離的方法
這篇文章主要介紹了SpringBoot整合MyCat實(shí)現(xiàn)讀寫分離的方法,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-04-04Redis使用RedisTemplate模板類的常用操作方式
這篇文章主要介紹了Redis使用RedisTemplate模板類的常用操作方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09SpringBoot項(xiàng)目調(diào)優(yōu)及垃圾回收器的比較詳解
這篇文章主要介紹了SpringBoot項(xiàng)目調(diào)優(yōu)及垃圾回收器的比較詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04編寫Spring MVC控制器的14個(gè)技巧(小結(jié))
這篇文章主要介紹了編寫Spring MVC控制器的14個(gè)技巧,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11解決mapstruct在eclipse生成不了mapper的實(shí)現(xiàn)類問(wèn)題
這篇文章主要介紹了解決mapstruct在eclipse生成不了mapper的實(shí)現(xiàn)類問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11IDEA生成標(biāo)準(zhǔn)JavaBean的幾種方法總結(jié)
標(biāo)準(zhǔn)javaBean是定義一個(gè)類的標(biāo)準(zhǔn)結(jié)構(gòu),下面這篇文章主要給大家總結(jié)介紹了關(guān)于IDEA生成標(biāo)準(zhǔn)JavaBean的幾種方法,文中通過(guò)圖文以及代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-03-03