# multi-layer-cache-example
**Repository Path**: jonathanzyf/multi-layer-cache-example
## Basic Information
- **Project Name**: multi-layer-cache-example
- **Description**: 多级缓存方案示例代码 :Caffeine(RAM) - Redis(KV) - MySql(DB)
- **Primary Language**: Java
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 2
- **Forks**: 1
- **Created**: 2021-01-18
- **Last Updated**: 2024-09-28
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# 多级缓存框架及其应用
本工程由两部分内容构成,一部分是多级缓存框架实现,另一部分是基于DDD的示例应用程序,演示如何使用多级缓存框架。
## 工程结构
### 缓存框架
| Module | 核心职责 | 核心对象 |
|-----------------------|-------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| multi-layer-cache-api | 多级缓存框架接口定义 | Cache, CachedObjectKey, CacheScope, GlobalCache, GlobalCacheAdmin,
RAMCache, RAMCacheAdmin, RequestCache, RequestCacheAdmin, MultiLayerCaching,
CachingMethod, CacheEvent, CacheEventListener, MultiLayerCacheChain |
| multi-layer-cache | 多级缓存框架实现与配置 | EnableMultiLayerCaching, CachingAutoConfiguration, ThreadLocalRequestCache,
RequestCacheManager, CaffeineRAMCache, RAMCacheManager, RedisGlobalCache,
RedisCacheManager, CachedObjectChangedEvent, CachedObjectChangedMessageListener |
### DDD示例应用程序
| Module | 核心职责 | 核心对象 |
|-----------------------|--------------------------|----------------------------------------------------------------------------------------------------------------------------------------|
| order-bootstrap | 引导程序,装配、配置、启动 | OrderApplication, ApplicationConfiguration |
| application-context | 请求上下文, 传递上下文信息 | RequestContext |
| order-domain | 领域模型层,系统内核 | Order,OrderDetail,Org,Product,Customer,
OrderRepository,OrgRepository,ProductRepository,CustomerRepository,
OrderDomainService |
| order-service | 应用服务层,提供粗粒度服务能力,Facade模式 | OrderService, OrderDTO, OrderDetailDTO, OrderConverter | |
| order-controller | 对外接口,提供RestAPI | OrderController,RequestContextAspect |
| order-infrastructure | 基础设施层,数据存储 | OrderRepositoryImpl,OrderPO, OrderDetailPO, OrderConverter,
OrderDao, OrderDetailDao | |
| order-integration | ACL防腐层,集成外部服务 | OrgAdapter, ProductAdapter, CustomerAdapter,
OrgConverter, ProductConverter, CustomerConverter |
| org-sdk | 组织SDK,由外部提供 | OrgService, SdkOrgDTO |
| product-sdk | 商品SDK,由外部提供 | ProductService, SdkProductDTO |
| customer-sdk | 客户SDK,由外部提供 | CustomerService, SdkCustomerDTO |
| external-service-mock | 外部服务Mock实现,实际由RPC统一机制实现 | OrgServiceImpl, ProductServiceImpl, CustomerServiceImpl |
## 逻辑架构

## 部署架构

## 六边形架构

## 多级缓存框架详细设计
对于性能需求极高、某类对象使用很频繁、对象反序列化和重新装配成本较高的场景,可以通过多级缓存来解决问题。
1. 使用很频繁的对象,尤其是读多写很少的情况,适合使用堆内内存来缓存对象。由于堆内内存资源宝贵,缓存一般采用软引用方式,还要限制容量、设定过期时间。
2. 快速变更的简单值,锁,读多写少的对象,可以使用K-V换存;通过多个字段全文检索对象,可以通过搜索引擎存储索引;查找到的主键后再到数据库中查找全量数据。
3. 通过SQL语法灵活的更新、查找、分析统计数据,OLTP的使用关系型数据库,OLAP的使用数据仓库,海量数据存储采用No-SQL。
多级缓存使用时,根据不同数据的特性,根据不同存储介质成本,进行区别对待,取长补短。
### 多级缓存框架接口设计

### 多级缓存框架实现设计

Caffeine采用Window-TinyLFU缓存淘汰算法,提供了近乎最佳的命中率。

每种存储中间件都有适用范围,根据使用场景确定如何搭配。
| 存储 | 特征 | 适用场景 |
|---------------|------------------------|------------------------------------------------------------|
| Caffeine | JVM KV | 高速读,热词 |
| Redis | RPC KV | 高速读、写,热词 |
| ElasticSearch | FS JSON Inverted-Index | 根据关键词搜索主键 |
| MySql | DB ROW | OLTP,根据索引查找,索引未命中时大表查询有严重效率、稳定性问题 |
| HBase | BigTable COLUMN | 海量数据、对象存储、稀疏矩阵、动态列、实时报表、部分字段进行读取和聚合 |
| Mongodb | DOC JSON | 海量数据、动态列 |
| Hive | HDFS MR WH | OLAP、海量数据、统计分析 |
| GreenPlum | WH ROW COLUMN | OLAP、报表、AOCO-事实表、HEAP-维度表 |
| Ignite | DataGrid KV | @see `space-based pattern` or `cloud architecture pattern` |
热度:一段时间范围内,某些键被频繁访问,这些键对应的数据热度高;如果热度高的键数量少,则热词,反之,热度分散。
多级缓存方案搭配示例
1. 值较小、热词、更新不频繁:Caffeine(JVM KV) - Redis(KV) - MySql(DB ROW)
2. 值较大、热词、更新不频繁:Caffeine(JVM KV) - MySql(DB ROW)
3. 值较小、热度分散、更新频繁:Redis(KV) - MySql(DB ROW)
4. 值较大、热度分散:MySql(DB ROW) Cluster
5. 基于模糊匹配:ElasticSearch(FS InvertedIndex) - MySql(DB ROW)
如果不考虑事务强一致性,关系型数据库可以被No-SQL替代。
数据同步策略
1. 定期淘汰expire
2. 定期更新refresh
3. 领域事件通知缓存更新refresh或驱逐evict
### 多级缓存框架配置设计

1. 框架采用Micro Kernel架构,分为API和实现两个组件,核心接口在API组件中声明,多级缓存的存储与协调由在实现组件中提供默认实现,开箱即用;同时对外暴露接口,保留了开放扩展能力。
2. 框架使用者只需要使用`@EnableMultiLayerCaching`、`@MultiLayerCaching`、`@CachingMethod`三个注解即可,通过注解声明式编程,对框架使用方侵入性小,易于理解,易于维护。
3. 缓存键值命名、阈值设置等遵守统一规范,定义了RAM和Global缓存的配置类。
4. 后续增加对资源使用授权控制,对资源的申请建立完善的审批流程,经过专业人员分析认可后才能通过审批。
### 多级缓存示例应用程序设计

多级缓存应用示例代码与配置:
1. 开启多级缓存:使用`@EnableMultiLayerCaching`注解指定缓存切面和API切面。
```java
@EnableMultiLayerCaching(
cachingPointcutBasePackages = {"org.example.integration.adapter"},
apiPointcutBasePackages = {"org.example.order.controller"}
)
public class OrderApplication {
// ...
}
```
2. 缓存拦截方法配置:使用`@MultiLayerCaching`注解声明缓存名称和缓存作用范围,有`REQUEST`、`RAM`、`GLOBAL`三种类型缓存,可以组合使用,例如:`REQUEST_GLOBAL`表示同时开启请求级缓存和全局缓存;
使用`@CachingMethod`注解声明哪些方法需要拦截处理缓存,并指定缓存键值参数索引。
```java
@MultiLayerCaching(cacheName = "CUSTOMER", scope = CacheScope.REQUEST_RAM_GLOBAL)
public class CustomerAdapter implements CustomerRepository {
private CustomerService customerService;
public CustomerAdapter(@Autowired CustomerService customerService) {
this.customerService = customerService;
}
@CachingMethod()
public Customer getCustomer(String customerId) {
SdkCustomerDTO sdkCustomerDTO = customerService.getCustomer(customerId);
return CustomerConverter.INSTANCE.convert(sdkCustomerDTO);
}
@CachingMethod()
public List getCustomers(List customerIds) {
List orgDTOList = customerService.getCustomers(customerIds);
return CustomerConverter.INSTANCE.convert(orgDTOList);
}
}
```
3. 配置文件:指定缓存的初始容量、最大容量、过期时间、刷新时间等,并指定是否开启事件通知,以及监听哪些队列的消息。
```yaml
cache:
ram:
initial-capacity: 20
maximum-size: 50
expire-after-access-seconds: 3600
refresh-after-write-seconds: 3600
global:
expire-after-access-seconds: 3600
expire-after-write-seconds: 3600
event:
enabled: false
cache-notification-queues: product-changed-notify,org-changed-notify,customer-changed-notify
```
# Redis大Key问题排查与解决方案
## 大键值问题
大键值(BigKeys)两种主要使用形式:
- 字符串类型:它的big体现在单个value值很大,一般认为超过10KB就是bigkey。
- 非字符串类型:哈希、列表、集合、有序集合,它们的big体现在元素个数太多。
大键值危害:
1. 内存空间不均匀
2. 超时阻塞:Redis单线程的特性,操作bigkey的通常比较耗时,意味着阻塞Redis可能性越大。
3. 网络拥塞
4. 过期删除
5. 迁移困难
[Redis大Key问题排查与解决方案详情](https://gitee.com/jonathanzyf/redis-example/blob/master/README.md)
> Redis不同键值大小时压测效率差异,分别在键值大小为128字节、1KB、10KB、100KB、1MB、5MB下进行读(get)
> 测试,其中128字节、1KB、10KB、100KB为500并发,1MB、5MB为100并发,测试环境为个人笔记本(Intel i5 2.7G, 8G DDR3, SSD)。测试结果如下:
>
> 
>
> 测试统计
>
> |键值大小|平均每次耗时(ms)|并发数|样本数|总耗时(ms)|吞吐量(次/s)|
> |---|---|---|---|---|---|
> |128B|23.9|500|200000|10225|19560|
> |1KB|27.9|500|100000|7762|12883|
> |**10KB**|37.5|500|100000|8763|11412|
> |20KB|55.3|500|100000|14102|7091|
> |30KB|65.1|500|100000|16224|6164|
> |50KB|86|500|100000|17827|5609|
> |100KB|145.5|500|100000|31657|3159|
> |1MB|262.6|**100**|50000|140888|355|
> |5MB|1453.8|**100**|20000|304725|66
# 参考资料
以下内容由[通义灵码TONGYI Lingma](https://tongyi.aliyun.com/lingma)自动生成,如有侵权请联系我删除。
## 缓存的特点
1. High hit rate: A cache with a high hit rate means that most of the time, when data is requested, it can be found in
the cache. This is because the cache stores frequently accessed data, and accessing the data from cache is faster
than accessing it from the original storage location, such as a disk or a database.
2. Hot words: In a cache, hot words are data items that are frequently accessed, and are therefore stored in the cache.
By identifying hot words, a cache can optimize its storage and retrieval mechanisms to improve the hit rate and
reduce the cache miss rate.
3. Least recently used (LRU) or least frequently used (LFU) replacement policy: A cache with a replacement policy based
on LRU or LFU means that when the cache is full, the least recently or least frequently accessed data items will be
evicted to make room for new data. This ensures that the cache always stores the most frequently accessed data items,
which maximizes the hit rate.
4. Cache coherence: When multiple copies of the same data are stored in different caches, cache coherence ensures that
all the copies are consistent and up-to-date. Cache coherence is typically implemented through a protocol that
maintains the consistency of the data across all the caches.
5. Cache invalidation: When data is modified or deleted, the cache needs to be notified to invalidate its copies of the
data to ensure that the next time the data is requested, it is fetched from the original storage location, which is
the most up-to-date version of the data.
## 缓存的分类
1. 本地缓存:本地缓存是指在本地内存中存储的缓存,它可以加速程序的运行速度,但它也会占用更多的内存资源。
2. 远程缓存:远程缓存是指将缓存数据存储在远程服务器上,它可以减少本地存储的压力,但它需要更多的网络开销。
3. 缓存服务器:缓存服务器是指将缓存数据存储在远程服务器上,并且提供缓存服务,它可以减少
generated by TONGYI Lingma
## 缓存的使用场景
1. 减少数据库或远程服务访问次数:缓存可以减少数据库或远程服务的访问次数,减少IO操作,从而提高系统的性能。
2. 加快数据读取速度:缓存可以加快数据读取速度,从而提高系统的性能。
## 缓存的实现
1. 基于内存的缓存:Java中提供的ConcurrentHashMap、ConcurrentLinkedQueue等。
2. 基于磁盘的缓存:Ehcache、Memcached、Redis等。
3. 基于数据库的缓存:Memcached、Redis等。
4. 基于代理服务器的缓存:Squid、Varnish、Nginx等。
5. 基于内容分发网络的缓存:CDN、反向代理服务器等。