👉 应用开发中,Cache毫无疑问是很重要的一块:提升应用性能的关键,降低像DB这样关键资源的负荷;但Cache的使用有很多要注意的问题与陷阱。
在应用开发通过Cache来性能优化时,要注意或了解下面的问题/陷阱:
- 多级
Cache支持- 应用开发时往往会考虑加上内存
Cache,因为内存Cache性能高很多于Cache中间件(如Memcached)- 当然要特别注意的是 内存
Cache数据条目上限 的设置,避免内存消耗过多导致应用瘫痪。 - 由于内存中
Cache数据条目,如何决定哪些数据要Cache,移出策略的设置变得要好好思考。 - 如果
Cache涉及数据条目很多、很分散没有时间局部性,那么内存Cache可能是浪费内存且增加了操作性能更低。
- 当然要特别注意的是 内存
- 这样数据访问路径就成了:内存
Cache-> 远程Cache-> 实际数据(如DB)
- 应用开发时往往会考虑加上内存
- 无实际数据时的 防击穿
导致没有数据时,一直会访问DB。
(自己实现时,容易忽略这点) Cache中没有数据时,即首次加载时,并发的Cache请求的 防击穿
期望在这种情况下,并发请求只会触发一次实际数据请求,数据请求完成后,所有并发Cache请求都返回数据。
(自己实现时,容易忽略这点)- 使用
Cache请求时,透明 数据的加载,即- 业务实现不需要自己写这样的逻辑:
- 如果
Cache中没有数据读实际数据,设置好Cache; - 如果
Cache中有数据返回Cache数据。 - 这样逻辑繁琐易漏易错。
- 如果
Cache会持有加载实际数据的Loader,参见Guava的CacheLoader模式。
- 业务实现不需要自己写这样的逻辑:
如果有『击穿』的情况,系统性能压力上来时会雪崩,而你想依赖的之前性能压测的性能数据已经没有意义了。
总得来说,应该使用Cache框架而不应该自己去实现:
Cache要解决好的问题与业务无关很稳定,所以一定有好的Cache框架,并解决好上面这些问题。Cache的并发控制/线程安全的问题,对于一般应用开发者来说,可能没有概念,即使是面向自己场景的简单实现也可能漏洞百出。- 完整的
Cache实现以应对不同场景的需求,要考虑很多方面:- 条目的逐出(
Eviction)/过期策略,如- 最简单常用的基于固定数目上限的
LRU - 基于时间的逐出
- 基于引用,以加速内存
GC
- 最简单常用的基于固定数目上限的
- 逐出条目的回调,方便应用可以集成条目的生命周期,如添加监控
Cache的命中率,方便了解系统的情况- ……
- 条目的逐出(
- Java Caching System - Apache Commons
- Guava Cache
- Caffeine is a high performance, near optimal caching library based on Java 8.
- Ehcache
- J2Cache
- 更多参见 Java开源缓存框架
