SpringBoot - 第二十五章 | 缓存支持與Redis集中式缓存(二)
上一篇介紹了在Spring Boot中如何引入緩存、緩存註解的使用、以及EhCache的整合。
雖然EhCache已經能夠適用很多應用場景,但是由於EhCache是進程內的緩存框架,在集群模式下時,各應用伺服器之間的緩存都是獨立的,因此在不同伺服器的進程間會存在緩存不一致的情況。即使EhCache提供了集群環境下的緩存同步策略,但是同步依然需要一定的時間,短暫的緩存不一致依然存在。
在一些要求高一致性(任何資料變化都能及時的被查詢到)的系統和應用中,就不能再使用EhCache來解決了,這個時候使用集中式緩存是個不錯的選擇,因此本文將介紹如何在Spring Boot的緩存支持中使用Redis進行資料緩存。
下面以上一篇的例子作為基礎進行改造,將緩存內容遷移到redis中。
準備工作
首先,下載範例 chapte14 。
先來回顧一下在此案例中,我們做了什麼內容:
- 引入了
spring-data-jpa
和EhCache
- 定義了
Customer
實體 - 使用
spring-data-jpa
實現了對Customer
對象的資料訪問接口CustomerRepository
- 使用
Cache
相關註解配置了緩存 - 單元測試,通過連續的查詢和更新資料後的查詢來驗證緩存是否生效
開始修改
刪除EhCache的配置文件
ehcache.xml
pom.xml中刪除EhCache的依賴,增加redis的依賴:
1
2
3
4<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
</dependency>application.properties
中增加redis配置1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19# REDIS (RedisProperties)
# Redis資料庫索引(默認為0)
spring.redis.database=0
# Redis伺服器地址
spring.redis.host=localhost
# Redis伺服器連接端口
spring.redis.port=6379
# Redis伺服器連接密碼(默認為空)
spring.redis.password=
# 連接池最大連接數(使用負值表示沒有限制)
spring.redis.pool.max-active=8
# 連接池最大阻塞等待時間(使用負值表示沒有限制)
spring.redis.pool.max-wait=-1
# 連接池中的最大空閒連接
spring.redis.pool.max-idle=8
# 連接池中的最小空閒連接
spring.redis.pool.min-idle=0
# 連接超時時間(毫秒)
spring.redis.timeout=1000修改 Customer 序列化(Serializable)
修改 測試範例
運行我們的單元測試,可以觀察到此時CacheManager的實例是org.springframework.data.redis.cache.RedisCacheManager,並獲得下面的執行結果。
1 | Hibernate: select customer0_.id as id1_0_, customer0_.age as age2_0_, customer0_.create_by as create_b3_0_, customer0_.create_dt as create_d4_0_, customer0_.modify_by as modify_b5_0_, customer0_.modify_dt as modify_d6_0_, customer0_.name as name7_0_ from customer customer0_ where customer0_.name=? |
可以發現在第一次查詢的時候,執行了Select語句,第二次則沒有,直接取得緩存內的值;在第三次查詢前,我們將age更新成20,但是在第三次查詢的時候,卻得到了10的結果。
問題分析
為什麼同樣的邏輯在EhCache中沒有問題,但是到Redis中會出現這個問題呢?
在EhCache緩存時沒有問題,主要是由於EhCache是進程內的緩存框架,第一次通過select查詢出的結果被加入到EhCache緩存中,第二次查詢從EhCache取出的對象與第一次查詢對象實際上是同一個對象(可以在使用Chapter4-4-1工程中,觀察u1==u2來看看是否是同一個對象),因此我們在更新age的時候,實際已經更新了EhCache中的緩存對象。
而Redis的緩存獨立存在於我們的Spring應用之外,我們對資料庫中資料做了更新操作之後,沒有通知Redis去更新相應的內容,因此我們取到了緩存中未修改的資料,導致了資料庫與緩存中資料的不一致。
因此我們在使用緩存的時候,要注意緩存的生命週期,利用好上一篇上提到的幾個註解來做好緩存的更新、刪除。
再次修改
針對上面的問題,我們只需要在更新age的時候,通過@CachePut
來讓資料更新操作同步到緩存中。
修改 CustomerRepository
清空Redis緩存內容
1
2$ redis-cli
127.0.0.1:6379> flushdb
- 再次運行我們的單元測試
1 | Hibernate: insert into customer (age, create_by, create_dt, modify_by, modify_dt, name) values (?, ?, ?, ?, ?, ?) |
以上範例建議用Debug的方式一步一步去看,可以看到第一次查詢是直接從緩存中取得,因為在初始化資料的時候,執行save的方法就已經將這個資料寫入到Redis裡面了,所以後面的第一次和第二次的查詢都直接取緩存資料。
然後在第三次查詢前,更新的資料也一併剛新Redis緩存,所以查詢出來的值就是正確的了。
這邊主要介紹了為什麼要使用Redis做緩存,以及如何在Spring Boot中使用Redis做緩存,並且通過一個小問題來幫助大家理解緩存機制,在使用過程中,一定要注意緩存生命週期的控制,防止資料不一致的情況出現。
註:以上參考了
SpringBoot2.0+整合redis,使用 RedisTemplate操作redis
oKong 的 SpringBoot | 第十一章:Redis的集成和简单使用文章。
程序猿DD-翟永超 的 Spring Boot中的缓存支持(二)使用Redis做集中式缓存 文章。