Redis集成Spring筆記

1. 依賴(lài)包安裝

<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-redis</artifactId>
    <version>1.7.5.RELEASE</version>
</dependency>
<dependency>
    <groupId>redis.clientsgroupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
</dependency>

2. Spring 項(xiàng)目集成進(jìn)緩存支持
要啟用緩存支持,我們需要?jiǎng)?chuàng)建一個(gè)新的 CacheManager bean。CacheManager 接口有很多實(shí)現(xiàn),本文演示的是和 Redis 的集成,自然就是用 RedisCacheManager 了。Redis 不是應(yīng)用的共享內(nèi)存,它只是一個(gè)內(nèi)存服務(wù)器,就像 MySql 似的,我們需要將應(yīng)用連接到它并使用某種“語(yǔ)言”進(jìn)行交互,因此我們還需要一個(gè)連接工廠以及一個(gè) Spring 和 Redis 對(duì)話要用的 RedisTemplate,這些都是 Redis 緩存所必需的配置,把它們都放在自定義的 CachingConfigurerSupport 中:

package com.defonds.bdp.cache.redis;

import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;

@Configuration
@EnableCaching
public class RedisCacheConfig extends CachingConfigurerSupport {

    @Bean
    public JedisConnectionFactory redisConnectionFactory() {
        JedisConnectionFactory redisConnectionFactory = new JedisConnectionFactory();

        // Defaults
        redisConnectionFactory.setHostName("192.168.1.166");
        redisConnectionFactory.setPort(6379);
        return redisConnectionFactory;
    }

    @Bean
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory cf) {
        RedisTemplate<String, String> redisTemplate = new RedisTemplate<String, String>();
        redisTemplate.setConnectionFactory(cf);
        return redisTemplate;
    }

    @Bean
    public CacheManager cacheManager(RedisTemplate redisTemplate) {
        RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);

        // Number of seconds before expiration. Defaults to unlimited (0)
        cacheManager.setDefaultExpiration(3000); // Sets the default expire time (in seconds)
        return cacheManager;
    }
    
}

當(dāng)然也別忘了把這些 bean 注入 Spring,不然配置無(wú)效。在 applicationContext.xml 中加入以下:

<context:component-scan base-package="com.defonds.bdp.cache.redis" />  

3. 緩存某些方法的執(zhí)行結(jié)果
設(shè)置好緩存配置之后我們就可以使用 @Cacheable 注解來(lái)緩存方法執(zhí)行的結(jié)果了,比如根據(jù)省份名檢索城市的 provinceCities 方法和根據(jù) city_code 檢索城市的 searchCity 方法:

    // R
    @Cacheable("provinceCities")
    public List<City> provinceCities(String province) {
        logger.debug("province=" + province);
        return this.cityMapper.provinceCities(province);
    }
    
    // R
    @Cacheable("searchCity")
    public City searchCity(String city_code){
        logger.debug("city_code=" + city_code);
        return this.cityMapper.searchCity(city_code);   
    }

4. 緩存數(shù)據(jù)一致性保證
CRUD (Create 創(chuàng)建,Retrieve 讀取,Update 更新,Delete 刪除) 操作中,除了 R 具備冪等性,其他三個(gè)發(fā)生的時(shí)候都可能會(huì)造成緩存結(jié)果和數(shù)據(jù)庫(kù)不一致。為了保證緩存數(shù)據(jù)的一致性,在進(jìn)行 CUD 操作的時(shí)候我們需要對(duì)可能影響到的緩存進(jìn)行更新或者清除。

 // C
 @CacheEvict(value = { "provinceCities"}, allEntries = true)
 public void insertCity(String city_code, String city_jb, 
   String province_code, String city_name,
   String city, String province) {
  City cityBean = new City();
  cityBean.setCityCode(city_code);
  cityBean.setCityJb(city_jb);
  cityBean.setProvinceCode(province_code);
  cityBean.setCityName(city_name);
  cityBean.setCity(city);
  cityBean.setProvince(province);
  this.cityMapper.insertCity(cityBean);
 }

 // U
 @CacheEvict(value = { "provinceCities", "searchCity" }, allEntries = true)
 public int renameCity(String city_code, String city_name) {
  City city = new City();
  city.setCityCode(city_code);
  city.setCityName(city_name);
  this.cityMapper.renameCity(city);
  return 1;
 }
 
 // D
 @CacheEvict(value = { "provinceCities", "searchCity" }, allEntries = true)
 public int deleteCity(String city_code) {
  this.cityMapper.deleteCity(city_code);
  return 1;
 }

業(yè)務(wù)考慮,本示例用的都是 @CacheEvict 清除緩存。如果你的 CUD 能夠返回 City 實(shí)例,也可以使用 @CachePut 更新緩存策略。筆者推薦能用 @CachePut 的地方就不要用 @CacheEvict,因?yàn)楹笳邔⑺邢嚓P(guān)方法的緩存都清理掉,比如上面三個(gè)方法中的任意一個(gè)被調(diào)用了的話,provinceCities 方法的所有緩存將被清除。

5. 自定義緩存數(shù)據(jù) key 生成策略

對(duì)于使用 @Cacheable 注解的方法,每個(gè)緩存的 key 生成策略默認(rèn)使用的是參數(shù)名+參數(shù)值,比如以下方法:

@Cacheable("users")
public User findByUsername(String username)

這個(gè)方法的緩存將保存于 key 為 users~keys 的緩存下,對(duì)于 username 取值為 "趙德芳" 的緩存,key 為 "username-趙德芳"。一般情況下沒(méi)啥問(wèn)題,二般情況如方法 key 取值相等然后參數(shù)名也一樣的時(shí)候就出問(wèn)題了,如:

@Cacheable("users")
public Integer getLoginCountByUsername(String username)

這個(gè)方法的緩存也將保存于 key 為 users~keys 的緩存下。對(duì)于 username 取值為 "趙德芳" 的緩存,key 也為 "username-趙德芳",將另外一個(gè)方法的緩存覆蓋掉。
解決辦法是使用自定義緩存策略,對(duì)于同一業(yè)務(wù)(同一業(yè)務(wù)邏輯處理的方法,哪怕是集群/分布式系統(tǒng)),生成的 key 始終一致,對(duì)于不同業(yè)務(wù)則不一致:

 @Bean
 public KeyGenerator customKeyGenerator() {
  return new KeyGenerator() {
   @Override
   public Object generate(Object o, Method method, Object... objects) {
    StringBuilder sb = new StringBuilder();
    sb.append(o.getClass().getName());
    sb.append(method.getName());
    for (Object obj : objects) {
     sb.append(obj.toString());
    }
    return sb.toString();
   }
  };
 }

于是上述兩個(gè)方法,對(duì)于 username 取值為 "趙德芳" 的緩存,雖然都還是存放在 key 為 users~keys 的緩存下,但由于 key 分別為 "類(lèi)名-findByUsername-username-趙德芳" 和 "類(lèi)名-getLoginCountByUsername-username-趙德芳",所以也不會(huì)有問(wèn)題。
這對(duì)于集群系統(tǒng)、分布式系統(tǒng)之間共享緩存很重要,真正實(shí)現(xiàn)了分布式緩存。
筆者建議:緩存方法的 @Cacheable 最好使用方法名,避免不同的方法的 @Cacheable 值一致,然后再配以以上緩存策略。

6. 緩存的驗(yàn)證

7. 注意事項(xiàng)

  • 要緩存的 Java 對(duì)象必須實(shí)現(xiàn) Serializable 接口
  • 緩存的生命周期我們可以配置,然后托管 Spring CacheManager,不要試圖通過(guò) redis-cli 命令行去管理緩存。
  • CacheManager 必須設(shè)置緩存過(guò)期時(shí)間,否則緩存對(duì)象將永不過(guò)期,這樣做的原因如上,避免一些野數(shù)據(jù)“永久保存”。此外,設(shè)置緩存過(guò)期時(shí)間也有助于資源利用最大化,因?yàn)榫彺胬锉A舻挠肋h(yuǎn)是熱點(diǎn)數(shù)據(jù)。
  • 緩存適用于讀多寫(xiě)少的場(chǎng)合,查詢(xún)時(shí)緩存命中率很低、寫(xiě)操作很頻繁等場(chǎng)景不適宜用緩存。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,653評(píng)論 19 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 47,281評(píng)論 6 342
  • 之前用SpringBoot+MyBatisPlus+SpringMVC整合搭建了一個(gè)基礎(chǔ)web開(kāi)發(fā)框架,使用這三個(gè)...
    z77z閱讀 4,629評(píng)論 2 25
  • 翻看手機(jī)相冊(cè),不知不覺(jué)自己又給小陽(yáng)臺(tái)的簕杜鵑拍了好多照片呢。 每次在陽(yáng)臺(tái)放松看到它,都會(huì)忍不住拍下幾張。陰天,雨天...
    吾塔塔閱讀 289評(píng)論 0 2
  • 在護(hù)理精細(xì)化管理下,我們科室進(jìn)行了整頓、清掃,優(yōu)化科室環(huán)境,創(chuàng)造了一個(gè)舒適整潔的工作場(chǎng)所。 在督導(dǎo)訓(xùn)練師指導(dǎo)下,...
    林麗麗閱讀 871評(píng)論 0 0

友情鏈接更多精彩內(nèi)容