javascript
Spring Boot中的缓存支持(二)使用Redis做集中式缓存
上一篇介紹了在Spring Boot中如何引入緩存、緩存注解的使用、以及EhCache的整合。
雖然EhCache已經(jīng)能夠適用很多應(yīng)用場景,但是由于EhCache是進(jìn)程內(nèi)的緩存框架,在集群模式下時,各應(yīng)用服務(wù)器之間的緩存都是獨(dú)立的,因此在不同服務(wù)器的進(jìn)程間會存在緩存不一致的情況。即使EhCache提供了集群環(huán)境下的緩存同步策略,但是同步依然需要一定的時間,短暫的緩存不一致依然存在。
在一些要求高一致性(任何數(shù)據(jù)變化都能及時的被查詢到)的系統(tǒng)和應(yīng)用中,就不能再使用EhCache來解決了,這個時候使用集中式緩存是個不錯的選擇,因此本文將介紹如何在Spring Boot的緩存支持中使用Redis進(jìn)行數(shù)據(jù)緩存。
下面以上一篇的例子作為基礎(chǔ)進(jìn)行改造,將緩存內(nèi)容遷移到redis中。
準(zhǔn)備工作
可以下載案例Chapter4-4-1,進(jìn)行下面改造步驟。
先來回顧一下在此案例中,我們做了什么內(nèi)容:
- 引入了spring-data-jpa和EhCache
- 定義了User實體,包含id、name、age字段
- 使用spring-data-jpa實現(xiàn)了對User對象的數(shù)據(jù)訪問接口UserRepository
- 使用Cache相關(guān)注解配置了緩存
- 單元測試,通過連續(xù)的查詢和更新數(shù)據(jù)后的查詢來驗證緩存是否生效
開始改造
刪除EhCache的配置文件src/main/resources/ehcache.xml
pom.xml中刪除EhCache的依賴,增加redis的依賴:
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-redis</artifactId> </dependency> |
- application.properties中增加redis配置,以本地運(yùn)行為例,比如:
| spring.redis.host=localhost spring.redis.port=6379 spring.redis.pool.max-idle=8 spring.redis.pool.min-idle=0 spring.redis.pool.max-active=8 spring.redis.pool.max-wait=-1 |
我們需要做的配置到這里就已經(jīng)完成了,Spring Boot會在偵測到存在Redis的依賴并且Redis的配置是可用的情況下,使用RedisCacheManager初始化CacheManager。
為此,我們可以單步運(yùn)行我們的單元測試,可以觀察到此時CacheManager的實例是org.springframework.data.redis.cache.RedisCacheManager,并獲得下面的執(zhí)行結(jié)果:
| Hibernate: insert into user (age, name) values (?, ?) Hibernate: select user0_.id as id1_0_, user0_.age as age2_0_, user0_.name as name3_0_ from user user0_ where user0_.name=? 第一次查詢:10 第二次查詢:10 Hibernate: select user0_.id as id1_0_0_, user0_.age as age2_0_0_, user0_.name as name3_0_0_ from user user0_ where user0_.id=? Hibernate: update user set age=?, name=? where id=? 第三次查詢:10 |
可以觀察到,在第一次查詢的時候,執(zhí)行了select語句;第二次查詢沒有執(zhí)行select語句,說明是從緩存中獲得了結(jié)果;而第三次查詢,我們獲得了一個錯誤的結(jié)果,根據(jù)我們的測試邏輯,在查詢之前我們已經(jīng)將age更新為20,但是我們從緩存中獲取到的age還是為10。
問題思考
為什么同樣的邏輯在EhCache中沒有問題,但是到Redis中會出現(xiàn)這個問題呢?
在EhCache緩存時沒有問題,主要是由于EhCache是進(jìn)程內(nèi)的緩存框架,第一次通過select查詢出的結(jié)果被加入到EhCache緩存中,第二次查詢從EhCache取出的對象與第一次查詢對象實際上是同一個對象(可以在使用Chapter4-4-1工程中,觀察u1==u2來看看是否是同一個對象),因此我們在更新age的時候,實際已經(jīng)更新了EhCache中的緩存對象。
而Redis的緩存獨(dú)立存在于我們的Spring應(yīng)用之外,我們對數(shù)據(jù)庫中數(shù)據(jù)做了更新操作之后,沒有通知Redis去更新相應(yīng)的內(nèi)容,因此我們?nèi)〉搅司彺嬷形葱薷牡臄?shù)據(jù),導(dǎo)致了數(shù)據(jù)庫與緩存中數(shù)據(jù)的不一致。
因此我們在使用緩存的時候,要注意緩存的生命周期,利用好上一篇上提到的幾個注解來做好緩存的更新、刪除
進(jìn)一步修改
針對上面的問題,我們只需要在更新age的時候,通過@CachePut來讓數(shù)據(jù)更新操作同步到緩存中,就像下面這樣:
| (cacheNames = "users") public interface UserRepository extends JpaRepository<User, Long> { (key = "#p0") User findByName(String name); (key = "#p0.name") User save(User user); } |
在redis-cli中flushdb,清空一下之前的緩存內(nèi)容,再執(zhí)行單元測試,可以獲得下面的結(jié)果:
| Hibernate: insert into user (age, name) values (?, ?) 第一次查詢:10 第二次查詢:10 Hibernate: select user0_.id as id1_0_0_, user0_.age as age2_0_0_, user0_.name as name3_0_0_ from user user0_ where user0_.id=? Hibernate: update user set age=?, name=? where id=? 第三次查詢:20 |
可以看到,我們的第三次查詢獲得了正確的結(jié)果!同時,我們的第一次查詢也不是通過select查詢獲得的,因為在初始化數(shù)據(jù)的時候,調(diào)用save方法時,就已經(jīng)將這條數(shù)據(jù)加入了redis緩存中,因此后續(xù)的查詢就直接從redis中獲取了。
本文內(nèi)容到此為止,主要介紹了為什么要使用Redis做緩存,以及如何在Spring Boot中使用Redis做緩存,并且通過一個小問題來幫助大家理解緩存機(jī)制,在使用過程中,一定要注意緩存生命周期的控制,防止數(shù)據(jù)不一致的情況出現(xiàn)。
代碼示例
本文的相關(guān)例子可以查看下面?zhèn)}庫中的chapter4-4-2目錄:
- Github:https://github.com/dyc87112/SpringBoot-Learning
- Gitee:https://gitee.com/didispace/SpringBoot-Learning
【轉(zhuǎn)載請注明出處】:http://blog.didispace.com/springbootcache2/
總結(jié)
以上是生活随笔為你收集整理的Spring Boot中的缓存支持(二)使用Redis做集中式缓存的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 前端组件化开发实践
- 下一篇: 阿里P8架构师谈:从单体架构、到SOA、