You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
-
需求背景
以1.0.0版本为参考,对分布式内存的缓存,基础框架采用了Caffeine,使用方式上集成spring-cache标准实现独立的CacheManager。通知机制支持ZK以及Nacos,通知级别支持到CacheName级,更新的方式主要是各节点通过对CacheName版本的监听,实现CacheName级别的Evict,在关联缓存被查询时进行重新生成。
关于数据的变更,此处除了版本机制外还有一个考量,是Jraft的集成,但考虑到jraft更适合独立功能集群的部署,如果普遍性的集成到业务中会造成不必要的开销,并增加了业务的复杂度,所以并未使用该方案。
优化点
解决方案
多实例的性能损耗
综上可以看出,我们最好的方案还是在Caffeine内的ConcurrentHashMap上层增加一层桶的逻辑,用来做Key的离散分布。
如果要实现我们的想法,还是需要从Caffeine Cache的具体实现入手。众所周知,Cache实例的创建是由Caffeine.newBuilder().build(),那么就需要从此处开始,看看Caffine在构造Cache实例的过程究竟做了什么,构造出来的实例长成什么样子?
Caffeine的构造
BoundedLocalCache
这个类的核心,在于final ConcurrentHashMap<Object, Node<K, V>> data。BoundedLocalCache在data的上层进行了包装,在put时首先使用Key,Value构造出Node对象
然后再将其put到data中
那这个思路其实已经很明朗,如果改造为桶的模式,那么唯一的方式就是往ConcurrentHashMap上进行封装,那么改造Caffeine源码涉及定制发布等问题,所以其实可以直接在我们的代码中将Key这一级做Caffeine外层的处理,这一层将Key进行桶的划分和记录,并通过Caffine的Key事件进行动态维护,当然会在存储上带来一定的开销,不过对于内存缓存的使用场景中,第一我们对Key的记录还是以引用的方式,第二Key相比于Value来说还是较小的,所以可以认为与内存级快速更新带来的好处相比,这些开销是可接受的,额外的存储开销我们会在后续性能测试中给出详细的数据。接下来,就说说以aradin-spring-caffine为基础如何进行该方案的实现。
#36
Beta Was this translation helpful? Give feedback.
All reactions