Spring cache

Spring Cache本身是一个缓存体系的抽象实现,并没有具体的缓存能力,要使用Spring Cache还需要具体的缓存实现来完成;也就是我们说的cache storage。Spring Boot 集成了多种cache的实现,如果你没有在配置类中声明CacheManager或者CacheResolvoer,那么SpringBoot会按顺序在下面的实现类中寻找:Generic、JCache (JSR-107) (EhCache 3, Hazelcast, Infinispan, and others)、EhCache 2.x、Hazelcast、Infinispan、Couchbase、Redis、Caffeine、Simple。

一、如何在代码中配置缓存

1)首先需要引入依赖spring-boot-starter-cache,其中包含CacheAutoConfiguration类,提供基于convention的配置,并允许customization。

2)其次,在启动类中用@EnableCaching开启缓存,启动类指的是main函数所在的类,也是我们通常所说的配置类(@Configuration);@EnableCaching会触发一个post processor,扫描每一个spring bean,查看是否已经存在缓存;如果找到了,就会自动创建一个代理拦截方法调用。

3)最后,配置cache manager用于管理缓存;cache manager有多种实现,最近项目中使用的是SimpleCacheManager,缓存的具体实现也有很多种,最近项目使用的是caffein。

4)缓存配置属性:cache.specs.<cacheName>.expire-after-write表示缓存过期时间,可以指定时间单位,通常是秒;cache.specs.<cacheName>.maximum-size表示可以存储多少缓存项,单位是个。

二、如何在代码中使用缓存

2.1 @Cacheable("cache name"、key、condition)

被@Cacheable标注的方法首先检查缓存中是否存在要查询的数据,如果有则直接返回,没有则调用方法查询,并把请求参数和返回值作为键值对存储在缓存中。

key可以在注解中用spEL表达式指定,如当参数值为整形ID,则可以指定为#id或#p0,如参数是对象类型,可指定为#obj.id;没有指定则默认使用被标注方法的参数作为key,单个参数则该参数值为key,多个参数则哈希值为key,没有参数则默认0作为key。

缓存条件分为基于输入或基于输出的条件;基于输入的条件用condition限定,使用spEl表达式编写,值为true则缓存,否则不缓存;基于输出的条件可以用unless限定。

2.2 清空缓存

缓存空间有限,清空缓存可以清除不再需要的缓存项;或用于配合更新缓存。

1)@CacheEvict(name="cache name", key=xx, condition=xx):cachename至少指定一个;key用spEL表达式编写,如果为空则按照所有参数进行组合定义;condition用spEl表达式编写,结果为“true”清空缓存,“false”则不清空;beforeInvocation:是否在程序执行前清空缓存,为true时则在程序执行前清空缓存,默认为false;allEntries:是否清空所有缓存,用spEl表达式编写,当程序执行时,为true则全部清空缓存,false则不全部清空,默认为false。

2)调用cache manager提供的evict方法,传入key;可以结合spring task scheduler,定时清理缓存;

3)调用cache manager提供的clear方法,清空全部缓存项。

2.3 更新缓存

@CachePut:@Cacheable和@CachePut的区别在于,@Cacheable只在结果未被缓存时执行方法并把结果放入缓存,当结果已经被缓存则不再调用方法直接返回缓存中的结果。 @CachePut必然会执行方法并把结果放入缓存,用于不是每次必然执行,一旦执行就需要更新缓存的方法。

考虑到并发下的竞争,通常更推荐清空缓存而不是更新缓存的方式。

2.4 其他工具类注解

@Caching:group multiple caching annotations,java编译器不允许一个方法有多个同类型注解。例如,我想着执行方法时填充到缓存1,并清除缓存2,并更新缓存3。

@CacheConfig:在类级别设置缓存通用信息,如cache name,这样在方法级别就不需要重复设置。

三、缓存的底层实现与常见问题

3.1 ConcurrentHashMap

请看另一篇文章

3.2 动态代理

Spring cache是基于Spring Aop的动态代理机制来对方法的调用进行切面,这里关键点是对象的引用问题,如果对象的方法是内部调用(即this引用)而不是外部引用,则会导致 proxy 失效,那么我们的切面就失效,也就是说上面定义的各种注释包括 @Cacheable、@CachePut 和 @CacheEvict 都会失效。解决方案要么两个方法放在不同的类,要么通过applicationContext.getBean(ClassName)的方式获取到实例再调用方法。

最近项目上的遇到了一个问题就是因为有同事犯了上述的错误,让方法A和方法B处于一个类中,当方法A调用方法B时,方法B声明的缓存不生效,导致页面访问速度明显变慢;更严重的是,这个问题直到上了生产环境才暴露出来。其影响不仅仅是让我们的应用响应速度变慢,也会发送更多请求到第三方系统,导致其压力增大。这个问题如何解决并不复杂,但是我们花费了很多时间来retro问题发生的原因,以及我们可以怎样优化流程并避免此类问题发生。

验尸报告如下:

1)开发对底层知识需要加强掌握,不仅限于业务代码和框架使用,框架使用只能告诉我们正确的用法,人不是机器,不可能背过一切,框架使用规则的背后,是java/jvm等知识,理解了为什么才能理解的更透彻,把知识真正变成自己的。这也是很大大公司着重考察数据结构等的原因。改进措施是继续搞读书分享会,提高团队的学习技术氛围。

2)我们基本上每天都会做code review,为什么代码评审没有发现问题?首先,我们相信大家都认真在看,因此代码评审需要更认真,这个没什么价值;但是我们可以吸取的经验教训是:小步提交。包含bug的代码提交里包含46个文件的修改。小步提交使得review代码的时候我们的注意力更集中的探讨一个修改。

3)为什么测试没有发现问题?就像测试金字塔一样,我们有不同层级的测试;从层级角度来说,首先,没有自动化针对缓存的测试,这个是必须要加上的;其次,手动测试没有对缓存的验证,我的想法是如果某个用户故事涉及了缓存的修改,那么开发需要自己检查日志,并截图缓存有效作为附近放在用户故事里面;第三,我们有没有可能引入性能测试,在哪个环境引入,在哪个层级引入。

3.3 缓存数据不一致

spirng cache是内存型缓存,当有多台服务器时,可能存在数据不一致;比如获取商店信息有缓存,缓存了商店状态A;此时更新商店状态到B,请求发送到了Server A,则server A的缓存被清空;但Server B的缓存为状态A;此时用户请求查看商户,如果请求发送到Server A,则返回最新状态B,如果请求发送到Server B,则返回状态A。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,240评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,328评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,182评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,121评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,135评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,093评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,013评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,854评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,295评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,513评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,398评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,989评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,636评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,657评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容