Skip to main content

01小节:用户查询优惠券之缓存击穿

作者:程序员马丁

在线博客:https://nageoffer.com

note

热门项目实战社群,收获国内众多知名公司面试青睐,近千名同学面试成功!助力你在校招或社招上拿个offer。

用户查询优惠券之缓存击穿,元数据信息:

©版权所有 - 拿个offer-开源&项目实战星球专属学习项目,依据《中华人民共和国著作权法实施条例》《知识星球产权保护》,严禁未经本项目原作者明确书面授权擅自分享至 GitHub、Gitee 等任何开放平台。违者将面临法律追究。


内容摘要:在购物时,优惠券对用户的吸引力非常大,毕竟谁都喜欢省钱。然而,在业务上,我们无法使用数据库来处理大量用户的优惠券查询请求,只能依靠缓存来应对高并发访问。当一个热点优惠券不在缓存中或缓存过期时,可能会有大量用户同时请求访问,这就引发了缓存击穿问题

课程目录如下所示:

  • Git 分支
  • 业务背景
  • 什么是缓存击穿
  • 缓存击穿解决方案
  • 开发优惠券模板查询
  • 文末总结

Git 分支

20240826_dev_coupon-template-query_cache_ding.ma

业务背景

在分发服务中,我们需要调用优惠券模板的相关信息。为了避免各个服务中重复实现模板查询功能,我们首先编写了一个通用的引擎层模板查询方法,以支持 C 端用户和内部应用的查询。像这种大流量的接口,肯定是需要放到缓存的。

用户常规访问优惠券模板时序图如下:

什么是缓存击穿?

缓存击穿指在高并发的系统中,一个热点数据缓存过期或者在缓存中不存在,导致大量并发请求直接访问数据库,从而给数据库造成巨大压力,甚至可能引起宕机。

具体来说,当某个热点数据在缓存中过期时,如果此时有大量并发请求同时访问这个数据,由于缓存中不存在,所有请求都会直接访问数据库,导致数据库负载急剧增加。

伪代码如下所示:

public String selectTrain(String id) {
String cacheData = cache.get(id);
// 查询缓存不存在,去数据库查询并放入到缓存
if (StrUtil.isBlank(cacheData)) {
// 获取数据库中存在的数据
String dbData = trainMapper.selectId(id);
if (StrUtil.isNotBlank(dbData)) {
// 将查询到的数据放入缓存,下次查询就有数据了
cahce.set(id, dbData);
cacheData = dbData;
}
}
return cacheData;
}

缓存击穿解决方案

1. 预热和缓存永不过期

一般来说,我们会通过预热和缓存永不过期的机制让缓存不击穿,这样即使再大的流量也可以通过缓存去抗。

  • 缓存预热:热点数据预加载,指的是在活动或者大促开始前,针对已知的热点数据从数据库加载到缓存中,这样可以避免海量请求第一次访问热点数据需要从数据库读取的流程。
  • 永不过期:热点数据永不过期,指的就是可以预知的热点数据,在活动开始前,设置过期时间为 -1。这样的话,就不会有缓存击穿的风险。

上面两个一般都是搭配一起使用的。等对应热点缓存的活动结束后,这些数据访问量就比较低了,可以通过后台任务的方案对指定缓存设置过期时间,这样可以有效降低 Redis 存储压力。

2. 分布式锁之双重判定锁

分布式锁的解决方案就是保证只有一个请求可以访问数据库,其它请求等待结果。这样可以避免大量的请求同时访问数据库。

在原有基础上继续改进,伪代码如下:

public String selectTrain(String id) {
String cacheData = cache.get(id);
// 查询缓存不存在,去数据库查询并放入到缓存
if (StrUtil.isBlank(cacheData)) {
// 为避免大量请求同时访问数据库,通过分布式锁减少数据库访问量
Lock lock = getLock(id);
lock.lock();
try {
// 获取数据库中存在的数据
String dbData = trainMapper.selectId(id);
if (StrUtil.isNotBlank(dbData)) {
// 将查询到的数据放入缓存,下次查询就有数据了
cahce.set(id, dbData);
cacheData = dbData;
}
} finally {
lock.unlock();
}
}
return cacheData;
}

但是这种的话有一个弊端,那就是获取分布式锁的请求,都会执行一遍查询数据库,并更新到缓存。理论上只有第一个加载数据库记录请求是有效的

解锁付费内容,👉 戳