在高并发系统中,需要一些特殊的机制来保障系统的稳定、高效。缓存则是系统高效运行的重中之重。
缓存在日常开发中启动至关重要的作用,由于是存储在内存中,数据的读取速度是非常快的,能大量减少对数据库的访问,减少数据库的压力。
今天我们主要聊一聊多级缓存那些事。
在传统的缓存策略。我们以java为例,当然其他的语言也是类似的。
在java中策略一般是请求到达Tomcat后,在应用程序中先去查询Redis,如果命中则返回,未命中则继续查询数据库。
那么这种策略会有什么问题呢?
所以在这种情况下,我们要增加多级别缓存,防止应用服务的冲击。
多级缓存的目的就是充分利用一个请求中的各个环节,在每个环节都分别添加缓存,以减少打到应用的流量,提升服务性能。
多级缓存主要分为以下几点,主要包括于:
目前大部分的浏览器本身也会有缓存。但也可以通过后台响应针对性的进行一些缓存。我们这里不过多的赘述。简单给一些响应demo
response.setHeader("Cache-Control", "max-age=3600, public"); // 缓存一小时
response.setDateHeader("Expires", System.currentTimeMillis() + 3600 * 1000); // 缓存一小时
long lastModifiedDate = getLastModifiedDate();
response.setDateHeader("Last-Modified", lastModifiedDate);
对于 nginx 的缓存,可以参考我的文章 : 实战:使用lua脚本在nginx层解决高并发访问问题
当然,这里补充一下 nginx的本地缓存
OpenResty为Nginx提供了shard dict的功能,可以在nginx的多个worker之间共享数据,实现缓存功能。
1)开启共享字典,在nginx.conf的http下添加配置:
# 共享字典,也就是本地缓存,名称叫做:item_cache,大小150m
lua_shared_dict item_cache 150m;
2)操作共享字典:
-- 获取本地缓存对象
local item_cache = ngx.shared.item_cache
-- 存储, 指定key、value、过期时间,单位s,默认为0代表永不过期
item_cache:set('key', 'value', 1000)
-- 读取
local val = item_cache:get('key')
为什么需要jvm进程缓存呢?
因为在jvm进程中进行本地缓存,例如HashMap、GuavaCache。它们读取的是本地的内存,没有丝毫的网络开销,速度非常快。但也有缺点,就是依赖于系统本身存储容量有限,单jvm中,无法进行多jvm共享。
所以在一些并发场景,针对一些不太大的数据信息缓存,可以产生不小的效果。
目前来说,在java中使用进程缓存,一般都是使用 HashMap 、Guava 、Caffeine .我们这里主要介绍Caffeine
Caffeine是一个基于Java8开发的,提供了近乎最佳命中率的高性能的本地缓存库。目前Spring内部的缓存使用的就是Caffeine。性能相当棒。
<!-- coffeine 本地缓存 版本自己选择 -->
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
创建caffeine.yml 当然也可以自行定义在application.yml中 或者直接写死在代码中
caffeine:
initialSize: 1
maximumSize: 3
创建配置文件
@Configuration
@PropertySource(value = "classpath:caffeine.yml")
@ConfigurationProperties(prefix = "caffeine")
public class CacheConfig {
@Value(value = "${initialSize}")
int initialSize;
@Value(value = "${maximumSize}")
int maximumSize;
@Bean
public Cache<String,Object> caffeineCache(){
return Caffeine.newBuilder()
.initialCapacity(initialSize)//初始容积
.maximumSize(maximumSize)//设置缓存最大上限值
.expireAfterAccess(Duration.ofSeconds(5))//设置有效时间 多久后未访问过期 访问包括读写
// .maximumWeight(30) //最大权重
// .weigher((p) -> p.getAge()) //计算权重的参数值
// .expireAfterWrite(5, TimeUnit.SECONDS)//设置有效期,写入多久后过期
.build();
}
}
方法 | 使用 | 介绍 |
---|---|---|
getIfPresent(key) | caffeineCache.getIfPresent("a"); | 当前是否有缓存,如果有缓存返回缓存,没有则返回null |
get(key,Function(k,v)) | caffeineCache.get("a",k->{ System.out.println("key不存在"); return "val"; }); | 获取缓存,当缓存不存在时,执行function方法:正常应该是去数据库获取数据并写入缓存 |
put(key,val) | caffeineCache.put("a",1); | caffeineCache.put("a",1); 写入值 |
invalidateAll(Iterable keys) | String[] s = {"a","b","c","d"}; caffeineCache.invalidateAll((Iterable<String>) () -> Arrays.stream(s).iterator()); | 批量删除缓存 |
invalidate(key) | caffeineCache.invalidate("a"); | 删除某个缓存 |
invalidateAll() | 清空缓存 | |
getAllPresent(Iterable keys) | 参考批量删除 批量获取缓存 | 批量获取缓存 |
estimatedSize() | estimatedSize() caffeineCache.estimatedSize() | 获取缓存中的个数 |
基于容量:设置缓存的数量上限
/ 创建缓存对象
Cache<String,String>cache =Caffeine.newBuilder().maximumSize(1).build();//设置缓存大小上限为1
基于时间:设置缓存的有效时间
Cache<String,String>cache =Caffeine.newBuilder().expireAfterwrite(Duration.ofseconds(10)).build();//设置缓存有效期为 10 秒,从最后一次写入开始计时
基于引用:设置缓存为软引用或弱引用,利用GC来回收缓存数据。涉及到GC性能较差,不建议使用。
在默认情况下,当一个缓存元素过期的时候,caffeine不会自动立即将其清理和驱逐。而是在一次读或写操作后,或者在空闲时间完成对失效数据的驱逐。
@RestController
@RequestMapping(value = "/caffeine")
public class CacheCaffeineController {
private String name;
@Autowired(required = true)
@Qualifier("caffeineCache")
Cache<String,Object> caffeineCache;
@GetMapping(value = "/get")
public void get(){
System.out.println(caffeineCache.estimatedSize());
String a1 = (String) caffeineCache.getIfPresent("a");
System.out.println("如果a有缓存的话返回a,没值的话返回null,a1的值:{}");
System.out.println(a1);
System.out.println("--------------");
String a = (String) caffeineCache.get("a",k->{
System.out.println("key不存在");
return "val";
});
if(StrUtil.isEmpty(a)){
System.out.println("无缓存");
}else{
System.out.println(a);
}
System.out.println(caffeineCache.stats());
}
@GetMapping(value = "/delAll")
public void delAll(){
String[] s = {"a","b","c","d"};
caffeineCache.invalidateAll((Iterable<String>) () -> Arrays.stream(s).iterator());
caffeineCache.invalidate("a");
}
@GetMapping(value = "/set/{key}/{val}")
public void set(
@PathVariable String key,
@PathVariable String val
){
caffeineCache.put(key,val);
}
}
ConcurrentHashMap
和HashMap
一样,是一个存放键值对。使用hash
算法来获取值的对应的地址,因此时间复杂度是O(1)。查询非常快。
ConcurrentHashMap
是线程安全的,ConcurrentHashMap
并非锁住整个方法,而是通过原子操作和局部加锁的方法保证了多线程的线程安全,且尽可能减少了性能损耗。
所以经常通过 ConcurrentHashMap 来进行本地缓存。下面是使用案例
@Component
public class CacheConcurrentHashMapFactory {
/**
* 默认缓存时长
*/
public static final int DEFAULT_CACHE_TIMEOUT = 30;
/**
* 默认初始化容积
*/
public static final int DEFAULT_INIT_CAPACITY = 1000;
/**
* 存储数据
*/
public static Map<String,Object> data;
public static ScheduledExecutorService executorService;
static {
data = new ConcurrentHashMap<>(DEFAULT_INIT_CAPACITY);
executorService = new ScheduledThreadPoolExecutor(2);
}
/**
* 增加缓存
* @param k
* @param v
*/
public static void put(String k , Object v){
data.put(k,v);
executorService.schedule(new TimerTask() {
@Override
public void run() {
data.remove(k);
}
},DEFAULT_CACHE_TIMEOUT, TimeUnit.SECONDS);
}
/**
* 增加缓存:自定义时间
* @param k
* @param v
* @param timeOut
*/
public static void put(String k , Object v,int timeOut) {
data.put(k,v);
executorService.schedule(()->data.remove(k),timeOut,TimeUnit.SECONDS);
}
/**
* 获取缓存
* @param k
* @return
*/
public static Object get(String k) {
return data.get(k);
}
/**
* 获取所有缓存
* @return
*/
public static Set<String> getAll(){
return data.keySet();
}
private CacheConcurrentHashMapFactory(){
}
}
以上介绍了从 浏览器缓存、nginx使用lua操作redis缓存、nginx本地缓存、jvm本地缓存。以此来构建了一个多级缓存。
当然有很多细节没在这里介绍。这只是一个基础的入门。让大家明白多级缓存是怎么回事以及有哪些缓存节点。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有