👉 应用开发中,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开源缓存框架