缓存应用案例分析

/ Java / 没有评论 / 1476浏览

为什么需要缓存

  1. 降低数据库查询压力
  2. 提前准备好结果数据,从而提升服务器性能
  3. 幂等性判断

什么数据需要缓存

  1. 列表信息
  1. 单个信息通过主键缓存
  1. 多服务器部署中,幂等性任务处理

环境准备工作

  1. 配置JetCache运行环境配置文件"tbd-redis.yml":
# 端口
#spring:
#  redis:
#    # redis服务器地址(默认为loaclhost)
#    host: 192.168.0.206
#    # redis端口(默认为6379)
#    port: 6379
#    # redis数据库索引(默认为0),避免和其他应用冲突,途比达序号为8,胖丁序号为6,数据中心序号为7
#    database: 8
#    # redis访问密码(默认为空)
#    password: root,.123
#    #超时连接
#    timeout: 1000ms
#    jedis:
#      pool:
#        #最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制。
#        max-wait: 1000ms
#        #最大连接数据库连接数,设 0 为没有限制
#        max-active: 100
#        #最大等待连接中的数量,设 0 为没有限制
#        max-idle: 8
#        #最小等待连接中的数量,设 0 为没有限制
#        min-idle: 0

remoteCache: &remoteCache
  #  type: redis
  #  keyConvertor: fastjson
  #  valueEncoder: kryo
  #  valueDecoder: kryo
  #  host: ${spring.redis.host:localhost}
  #  port: ${spring.redis.port:6379}
  #  password: ${spring.redis.password}
  #  database: ${spring.redis.database}
  #  poolConfig:
  #    minIdle: 5
  #    maxIdle: 20
  #    maxTotal: 50
  type: redis.lettuce
  keyConvertor: fastjson
  valueEncoder: kryo
  valueDecoder: kryo
  # uri格式:redis://密码@ip:端口/redis库名?timeout=5s  url[0]
  # uri: redis://${spring.redis.password}@${spring.redis.password}:${spring.redis.port:6379}/7?timeout=5s
  # redis数据库索引(默认为0),避免和其他应用冲突,途比达序号为8,胖丁序号为6,数据中心序号为7
  # uri格式:redis://密码@ip:端口/redis库名?timeout=5s
  uri: redis://root,.123@192.168.100.4:6379/8?timeout=1s
  poolConfig:
    minIdle: 0
    maxIdle: 8
    maxTotal: 100

localCache: &localCache
  type: caffeine
  keyConvertor: fastjson


jetcache:
  statIntervalMinutes: 15
  areaInCacheName: false
  local:
    # 默认1小时本地缓存
    default:
      <<: *localCache
      expireAfterWriteInMillis: 60000
      expireAfterAccessInMillis: 60000
    # 長時本地緩存,主要用于要求时效一般
    longTime:
      <<: *localCache
      expireAfterWriteInMillis: 300000
      expireAfterAccessInMillis: 180000
    # 短時本地緩存,主要用于要求时效较高的配置
    shortTime:
      <<: *localCache
      expireAfterWriteInMillis: 3000
      expireAfterAccessInMillis: 3000
  remote:
    # 默认1小时的远程缓存
    default:
      expireAfterWriteInMillis: 3600000
      <<: *remoteCache
    # 长时远程緩存,主要用于要求时效要求一般的集中式缓存
    longTime:
      expireAfterWriteInMillis: 7200000
      <<: *remoteCache
    # 短時远程緩存,主要用于要求时效较高的集中式缓存
    shortTime:
      expireAfterWriteInMillis: 300000
      <<: *remoteCache
  1. pom.xml中引入jar包
<dependency>
  <groupId>com.yanzuoguang</groupId>
  <artifactId>yzg-util-redis</artifactId>
</dependency>
或者
<dependency>
  <groupId>com.alicp.jetcache</groupId>
  <artifactId>jetcache-starter-redis-lettuce</artifactId>
</dependency>
  1. 项目中 "bootstrap.yml" 增加 引入配置文件"tbd-redis.yml "
spring:
  cloud:
    config:
      name: tbd-redis

列表信息缓存

一般来说列表信息缓存信息都需要时效处理,这种缓存不需要删除,但是缓存时间不能太长(建议最长1-5分钟),但是需要定时失效.并通过整个对象转换为Json字符串后MD5,来确定是否自动重新刷新.

实现方式:

/**
* 查询分销渠道
*
* @param req
* @return
*/
@Cached(
  // 默认规则名称:缓存时效规则,以及存储方式
  area="shortTime",
  // 缓存名称,通常定义到缓存常量中,防止缓存名称冲突
  name = CacheName.SALE_SALE_QUERY, 
  // 缓存关键字,用于做唯一性判断,可以不配置,则由框架自动进行key值生成
  key = "#req.toJsonMd5()", 
  // 结果为空时是否缓存
  cacheNullValue = true, 
  // 同时缓存到redis,内存中(降低redis压力),可以分别配置redis缓存时间和
  cacheType = CacheType.BOTH ,
  // 本次配置时效单位.注意:配置文件中的值和这个单位无关.
  timeUnit=TimeUnit.SECONDS,
  // 本地内存缓存失效时间,可以单独配置,一般通过area属性来控制
  localExpire = 3, 
  // Redis缓存失效时间,可以单独配置,一般通过area属性来控制
  expire = 3
)
// 开启自动刷新
@CacheRefresh(
  // 自动刷新时间
  refresh = CacheName.REFRESH_TIME, 
  // 不访问后多长时间停止刷新
  stopRefreshAfterLastAccess = CacheName.REFRESH_STOP
)
// 开启多线程缓存保护,即 @Cached.key 相等时则不缓存
@CachePenetrationProtect
PageSizeData<SaleSaleLoadResVo> query(SaleSaleQueryReqVo req);

单个信息缓存

一般来说单个信息缓存信息都可以保留较长时效,这种缓存需要在增加,修改时自动删除,缓存可以设置较长时间(建议最长30-60分钟),可以支持定时刷新,从而提升加载速度.并开启多线程保护.多个请求时,只一个人去加载.这个通过主键加载时不能通过扩展参数返回2种以上的不同结果.

实现方式:

/**
* 查询分销渠道
*
* @param req
* @return
*/
@Cached(
  // 默认规则名称:缓存时效规则,以及存储方式,在配置文件中定义,默认为: default
  area="default",
  // 缓存名称,通常定义到缓存常量中,防止缓存名称冲突
  name = CacheName.SALE_SALE, 
  // 缓存关键字,用于做唯一性判断,单个缓存中必须配置.
  key = "#req.saleId", 
  // 结果为空时是否缓存
  cacheNullValue = true, 
  // 同时缓存到redis,内存中(降低redis压力),可以分别配置redis缓存时间和
  cacheType = CacheType.BOTH 
)
// 开启自动刷新
@CacheRefresh(
  // 自动刷新时间
  refresh = CacheName.REFRESH_TIME, 
  // 不访问后多长时间停止刷新
  stopRefreshAfterLastAccess = CacheName.REFRESH_STOP
)
// 开启多线程缓存保护,即 @Cached.key 相等时则不缓存
@CachePenetrationProtect
SaleSaleLoadResVo load(SaleSaleLoadReqVo req);

/**
* 分销渠道规则保存/修改
*
* @param req
* @return
*/
// 强制现有缓存失效
@CacheInvalidate(
  // 默认规则名称:缓存时效规则,以及存储方式,在配置文件中定义,默认为: default
  area="default",
  // 缓存名称
  name = CacheName.SALE_SALE, 
  // 缓存主键,得和 @Cached.key 相等
  key = "#req.saleId"
)
String save(SaleSaleSaveReqVo req);

/**
 * 删除分销信息
 *
 * @param req
 * @return
 */
// 强制现有缓存失效
@CacheInvalidate(
  // 默认规则名称:缓存时效规则,以及存储方式,在配置文件中定义,默认为: default
  area="default",
  // 缓存名称
  name = CacheName.SALE_SALE, 
  // 缓存主键,得和 @Cached.key 相等
  key = "#req.saleId"
)
int remove(SaleSaleRemoveReqVo req);

/**
* 渠道上下架
*
* @param req
* @return
*/
// 强制现有缓存失效
@CacheInvalidate(
  // 默认规则名称:缓存时效规则,以及存储方式,在配置文件中定义,默认为: default
  area="default",
  // 缓存名称
  name = CacheName.SALE_SALE, 
  // 缓存主键,得和 @Cached.key 相等
  key = "#req.saleId"
)
String inline(SaleSaleInlineReqVo req);

多服务器部署中,幂等性任务处理

这种一般用于判断请求是否重复,并把一些重复的资源放入缓存中.从而提升最近一段时间该订单的加载速度.

@Compent
class Classs{

  @CreateCache(name = "order:save:", expire = 3600, cacheType = CacheType.BOTH)
  private Cache<String, OrderLoadResVo> cacheOrderSave;

  // 生成分布式锁执行函数,同一个key同一时间只会有一个函数执行,100秒内每1秒钟检测一次
  CacheLock.run(
    // 缓存对象
    cacheOrderSave,
    // 等待最长时长
    waitTime, 
    // 等待时间间隔
    waitUnit, 
    // 等待关键字
    key + "_RUN", 
    // 当key对应函数,没有其他人执行时,开始执行本函数.奇谈人执行则等待
    new Runnable() {
      @Override
      public void run() {
        // 读取缓存
        OrderLoadResVo loadCache = cacheOrderSave.get(key);
        // .... 
        // 写入缓存
        cacheOrderSave.put(order.getOrderId(), load);
        cacheOrderSave.put(channelKey, load);
      }
    });
}