SpringCache+Redis+Canal+Redisson,分布式锁/分布式缓存,读写模式,缓存击穿/穿透/雪崩,SpringCloud-SpringCache技术解决方案讨论与总结。

  Redis缓存问题:穿透,击穿,雪崩

  读模式,lazy版,遇到缓存没有的数据,去数据库读,然后载入数据库写模式,positive版 双写,失效模式

  缓存雪崩

  同一时间大规模key失效,大量请求涌入数据库

  解决:失效时间添加随机值避免雪崩

  缓存击穿

  热点key突然失效,大并发涌入数据库。

  解决:针对业务情况,过于热点key不过期(但一定要注意key的生成量相关问题,避免爆内存)加锁,拿到锁才允许查数据库

  缓存穿透

  访问到了非法key值,该key值不存在,即为大量缓存不命中,全部打到数据库,而数据库也没有响应数据,导致缓存永远没法正常起效。

  解决:给数据库也没有的值也添加至缓存,例如null值进缓存。

  -(但是该方法也存在问题,大量非法请求的话,每次随机,则缓存每次也不命中,缓存无意义,)给查询做过滤,查询前判断是否存在该key,不存在直接返回。

  redis分布式锁

  使用redisson,redisson实现了juc里面的各种锁 看门狗机制

  自动续期机制,默认30秒一个锁,到时间业务没结束自动续期,结束了则自动删锁。但是在调用lock()方法时手动指定锁的时间的话,没有自动续期机制(可以足够长,确保业务完成) 常用锁介绍

  缓存一致性问题

  双写模式

  写数据库同时写缓存失效模式

  数据更新后写数据库,删除原缓存。

  -解决:读写锁,面向多读情况,少写,性能影响较小(并且分布式读写锁)缓存主动更新

  以上两种均存在问题,即缓存一致性问题

  对于即时性要求不高的业务,容忍度提高,缓存一致性可以忽略。 使用Canal,mysql的binlog机制mysql更新同时,Canal作为mysql子节点,订阅binlog同步至其他中间件,例如redis利:无需关心缓存同步,专心处理数据库即可。弊:增加中间件,添加各种复杂度等。Cannal在大数据方面应用,订阅到多张表数据,进行数据分析,然后数据异构出适合用户的推荐表等

  SpringCache 缓存抽象层

  通过注解解决分布式缓存的以上各种问题,使用简单注解解决技术痛点。CompositeCacheManager 缓存管理器管理多个CacheManagerCacheManager 管理缓存(类似单个容器)多种实现 ConcurrentMapCache,redis,等。例如ConcurrentMapCache内用ConcurrentMap来管理缓存,保存在store属性里。实际操作是Cache组件。Crud。 整合SpringCache 引入依赖

  配置

  自动配置:

  CacheConfigurations,里使用什么作为缓存中间件,则会放入到配置的MAPPINGS内,使用静态代码块初始化put到Mappings里,那我们使用Redis,实际上会再导入RedisCacheConfiguration类做自动配置,里面有个RedisCacheManager(会初始化所有缓存),实际配置这个提供这个Bean即可。RedisCacheManager会读取配置文件,默认配置基础上,自己配置例如名字之类的,初始化我们的缓存。

  自动配置内容:字符序列化器,key前缀,等

  我们需要做的配置

  使用缓存注解操作缓存@Cacheable 触发将数据保存到缓存的操作(将方法返回值放入缓存)@CacheEvict 出发缓存删除操作@CachePut 不影响方法执行更新缓存@Caching 复合多种以上操作@CacheConfig 类级别共享缓存配置 读模式缓存使用

  开启缓存功能

  启动类添加@EnableCaching注解即可

  给方法添加注解即可完成

  @Cacheable,加在有查数据库的方法上,表示自动缓存,以后缓存中有则当前方法不会调用,直接返回缓存内数据缓存中没有,则调用该方法,返回值放入缓存,以供以后使用

  每一个需要缓存的数据@Cacheable()注解需要指定参数,缓存名字,

  @Cacheable({“category”,“xxxx”}) 此注解标注的名字类似给缓存分区,指定相同name的在一个区,类似namespace名称空间的东西。

  @Cacheable注解常用参数:value=xxx 即等同于上述未指定参数起名分区key 缓存key名,可以为参数指定key=“#root.method.name”SpEL表达式语法,参考官方文档。举例:#root.method.name…

  -sync =true //查询加锁,解决击穿

  默认行为缓存中没有那就执行,缓存中有,方法不执行直接给缓存值。key默认自动生成,缓存名字::simplekey []自定义生成的值序列化使用java序列化器默认ttl -1 即为无过期时间。

  自定义指定json序列化器

  配置文件指定存活时间

  缓存null值,缓存穿透问题

  写模式缓存失效缓存使用

  方法替换为@CacheEvict即可

  多缓存操作,例如一个更新删多个缓存

  缓存修改

  总结讨论,与 SpringCache不足 读模式穿透击穿雪崩

  相应使用上述解决方案 写模式-数据一致性问题读写加锁引入Canal,感知Mysql变动更新数据库 读写都多,考虑不适用缓存,直接查数据库

  总结:常规数据,读多写少,一致性宽容度高,可以使用SpringCache。那特殊读写都多,且宽容度低之类的,特殊设计。