当前位置: 首页 > news >正文

缓存三部曲:从线程到分布式

背景

在业务中,我们通常会访问数据库(DB)或远程服务(API)来获取数据,这些访问:

  • 延迟高(I/O开销大)
  • 数据变化不频繁
  • 多次重复查询相同的数据

针对这些数据我们可以进行缓存,减少对数据库的查询次数,减轻数据库的访问压力。但是在不同的场景下缓存实现的是方式也各有不同。所以记录一下常见的缓存实现方式叭~

常见的缓存实现方式

线程级缓存(ThreadLocal)

当前线程独有,随着线程销毁,不同线程下不共享。

知识点

1.ThreadLocal内部关系图

每一个线程内部都有一个ThreadLocalMap

ThreadLocalMap的key是对ThreadLocal 的弱引用

value是你存进去的实际数据(强引用)

不同线程的ThreadLocalMap相互独立,不共享

2.内存泄漏

ThreadLocal 对象被 GC 了

key 变成 null(因为是弱引用被 GC 回收)

value 还在内存中(因为 ThreadLocalMap 还在当前线程中)

若线程是线程池管理(长生命周期),value 就永远不会释放

应当在业务处理完之后调用remove()方法

3.线程的生命周期

在典型的web应用(springboot)中,每一个用户请求都会分配一个独立的线程来执行。

不同用户 → 不同线程 → 各自独立运行递归 → 各自拥有独立的 ThreadLocal 缓存空间

方法之间涉及调用或者递归调用自己都是在一个线程里面,调用过程中只是函数调用栈的变化,例如

public class DemoService {public void totalMethod() {methodA();methodB();methodC();methodD();}private void methodA() { ... }private void methodB() { ... }private void methodC() { ... }private void methodD() { ... }
}

当执行:

new DemoService().totalMethod();

在这个过程中:

所有的这些方法(totalMethod、methodA、methodB...)都是在同一个线程里顺序执行的。

调用过程只是函数调用栈的变化,不会创建新线程。

4.什么时候才会有多个线程

显示创建线程或线程池中执行才会在不同的线程中执行

场景是否多线程执行方式调用关系
普通调用(方法里互相调用)❌ 否同一线程内,顺序执行依次入栈出栈
递归调用❌ 否同一线程内,多层入栈自己调用自己
new Thread(...)✅ 是不同线程并发执行各自独立
线程池 / CompletableFuture✅ 是不同线程并发执行异步

应用场景

当我们了解了ThreadLocal基本知识点后,就可以开始利用它的特点进行线程级别的缓存啦。当涉及线程级别的缓存,同时在同线程下多次查询相同的数据的。

实现方式

//1.初始化一个ThreadLocal对象
private static final ThreadLocal<Map<Long, List<Benefit>>> BENEFIT_CACHE = ThreadLocal.withInitial(HashMap::new);//2.从ThreadLocal对象里面取出数据,无则直接返回,有则新增
list = BENEFIT_CACHE.get().computeIfAbsent(customBenefit.getId(), k -> lambdaQuery().in(Benefit::getId, benefitIds).list());//3.如果有递归
unpacking(current.getIncludes(), current, benefits, depth + 1);//4. 递归深度 == 0时 清空ThreadLocal 
if (depth == 0) { BENEFIT_CACHE.remove(); }

本地缓存

本地缓存应该是我们最常用一种方式,应用场景也比较多,大多数是通过map进行实现的。存储位置在JVM内存中,随着应用进程销毁,不同线程可共享。

分布式缓存

当涉及跨服务之间的数据共享的场景下,使用分布式缓存就比较合适了。常用的缓存方式就是通过redis实现了,长期存在,可过期。

总结

每个缓存适应的场景不同,可以在不同的情况下使用不同的缓存方式,来减轻I/O消耗,减轻数据库的访问压力

缓存方式存储位置生命周期并发访问常用场景
本地缓存(Local Cache)JVM 内存(如 MapThreadLocal、Guava Cache)随应用进程销毁线程内共享(可加锁)单机服务、小数据量、低延迟查询
分布式缓存(Distributed Cache)独立服务(如 Redis、Memcached)长期存在,可过期多线程/多实例共享大型系统、微服务集群间共享数据
线程级缓存(ThreadLocal)当前线程独有随线程销毁不同线程不共享临时缓存,如递归、批量操作
http://www.dtcms.com/a/568684.html

相关文章:

  • LS67211_VC1:48KHz低延时AI降噪USB直播麦克风音频处理器
  • 【C++】分治-快速排序算法习题
  • MySQL第四次作业(索引、视图)
  • Partial Prompt Templates in LangChain
  • 泉州网站平台建设公司网站建设素材图
  • 计算机技术员网站建设怎么网站底部 设计
  • 第50届ICPC亚洲区域赛·成都站,非凸科技持续护航顶尖赛事
  • 企业微信自建应用开发详细教程,如何获取授权链接?如何使用js-sdk?
  • html css js网页制作成品——高定晚礼服HTML+CSS网页设计(5页)附源码
  • 蓝牙钥匙 第43次 特殊用户群体场景下的汽车数字钥匙系统:包容性设计与技术创新
  • 万网如何建设购物网站wordpress分类目录 菜单 页面
  • 智能网联汽车 HD map架构解析
  • HTML常用单标签速查手册
  • 告别算法死记硬背,Hello-Algo 让抽象知识变直观,搭配cpolar穿透工具更自由
  • Go从入门到精通(27) - 并行任务处理器
  • Claude Code 使用 MiniMax M2 模型
  • Auto CAD二次开发——复制和旋转图形对象
  • 全屏响应式网站模板网站seo综合公司
  • php做简单网站教程视频教程企业门户网站模板 下载
  • Rust开发实战之WebSocket通信实现(tokio-tungstenite)
  • 编译缓存利器 ccahce、sccahce
  • Rust开发实战之使用 Reqwest 实现 HTTP 客户端请求
  • 各大公司开源网站广州出台21条措施扶持餐饮住宿
  • gmt_create为啥叫gmt
  • 从 NGINX 到 Kubernetes Ingress:现代微服务流量管理实战
  • 【C++】继承(2):继承与友元,静态成员,多继承黑/白盒复用
  • css实战:常用伪元素选择器介绍
  • 4.4 路由算法与路由协议【2013统考真题】
  • 营销型网站建设需要备案吗上饶网站建设企业
  • 福建网站建设科技有限公司品牌建设还需持续力