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

wordpress吐槽插件郑州seo课程

wordpress吐槽插件,郑州seo课程,20m做网站,影业的网站怎么做缓存 数据交换的缓冲区&#xff0c;俗称的缓存是缓冲区内的数据&#xff0c;一般从数据库中获取&#xff0c; 例1:Static final ConcurrentHashMap<K,V> map new ConcurrentHashMap<>(); 本地用于高并发例2:static final Cache<K,V> USER_CACHE CacheBuild…

缓存

数据交换的缓冲区,俗称的缓存是缓冲区内的数据,一般从数据库中获取,

1:Static final ConcurrentHashMap<K,V> map = new ConcurrentHashMap<>(); 本地用于高并发例2:static final Cache<K,V> USER_CACHE = CacheBuilder.newBuilder().build(); 用于redis等缓存例3:Static final Map<K,V> map =  new HashMap(); 本地缓存
  1. ConcurrentHashMap
    线程安全的哈希表,支持高并发读写。适用于本地内存缓存,无需序列化,直接操作 Java 对象。但无法持久化或分布式共享。

  2. CacheBuilder(Guava Cache):
    功能更丰富的本地缓存,支持过期策略、最大容量、弱引用等。通常用于本地二级缓存,配合 Redis 等远程缓存使用,减少远程访问压力。

  3. HashMap
    非线程安全的哈希表,直接用于缓存会有并发问题(如数据不一致、死循环)。不推荐在高并发场景使用,除非通过外部同步(如Collections.synchronizedMap)。

使用缓存的目的

速度快,提高读写效率,降低响应时间
缓存数据存储在内存中,而内存读写性能远高于磁盘,缓存可以大大降低用户访问并发量带来的服务器读写压力(降低后端负载)
![[Pasted image 20250711171217.png]]

  1. 数据一致性成本
    若后端数据更新(如商品价格修改),缓存未及时同步,会出现 “缓存与源数据不一致” 的问题。需设计 缓存失效策略(如超时、主动更新),但这会增加代码复杂度和异常处理成本。
  2. 代码维护成本
    引入缓存后,代码需新增 “缓存读写、失效、回源(缓存未命中时查后端)” 等逻辑,还需处理缓存穿透、击穿、雪崩等异常场景,导致代码更复杂,维护难度提升。
  3. 运维成本
    缓存系统(如 Redis、Memcached)需独立部署、监控(内存、命中率、连接数)、扩容(集群化)、故障恢复,增加运维人力和资源投入。

如何使用缓存:
构建多级缓存,例如本地缓存和redis缓存并发使用

浏览器缓存:保存在浏览器端的缓存
应用层缓存:分为tomcat本地缓存,如使用map或redis
数据库缓存:数据库中有一个缓存池,增改查数据都会先加载到mysql缓存中
CPU缓存:CPU的L1、L2、L3级缓存
![[Pasted image 20250711171515.png]]

添加商户缓存

在查询商户信息时,先到缓存中查询
这里添加redis缓存
查询时先访问Redis,若没有命中再访问数据库,同时写缓存到Redis
![[Pasted image 20250711171743.png]]

String key = "cache:shop:" + id;  
String shopJson = stringRedisTemplate.opsForValue().get(key);  
if (StrUtil.isNotBlank(shopJson)) {  Shop shop = JSONUtil.toBean(shopJson, Shop.class);  return Result.ok(shop);  
}  
Shop shop = getById(id);  
if (shop == null) {  return Result.fail("店铺不存在哦");  
}  
stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop));  
return Result.ok(shop);

先从redis查询店铺数据
如果存在,将JSOn格式的字符串通过JSONUtil反序列化成Shop类对象的实例
如果不存在,去数据库中查找,将返回值写入redis,这里同样将Shop类对象的实例转化成String类型

缓存更新策略

若缓存中数据过多,redis会对部分数据进行更新或淘汰

内存淘汰

当内存达到设定的最大值时,自动淘汰一些不重要的数据

超时剔除

设置过期时间,redis会将超时的数据进行删除

主动更新

手动调用方法删除缓存,通常用于解决缓存和数据库不一致的问题

![[Pasted image 20250711172822.png]]

数据库缓存不一致解决方案

由于缓存数据来源于数据库,而数据库中的数据是会发生变化的,若数据库数据发生变化,而缓存未同步,就会出现一致性问题
后果是用户可能使用缓存中过时数据,从而产生类似多线程数据安全问题
有如下解决方案:
人工编码:内存调用者在更新完数据库后更新缓存
读写穿透模式:系统作为中间层,同时管理缓存与数据库的读写操作
写回缓存模式:应用层仅操作缓存,数据库更新由异步线程批量处理,调用者写入缓存后直接返回,由异步线程定期将缓存数据批量写入数据库

综合推荐使用方案一,
在操作数据库时,我们可以将缓存删除,待查询时再从缓存中加载数据
为保证数据库操作同时成功或失败:
采用单体系统的情况,则将缓存与数据库放在一个事务中
采用分布式系统,则利用TCC等分布式事务方案

具体操作缓存和数据库时,应该采用先操作数据库,后删除缓存的操作
若先删除缓存,再操作数据库:
当有两个线程并发查询的时候,假设线程1先查询,删除缓存后此时线程2发现没有缓存数据,从数据库中读取旧数据写入到缓存中,此时线程1再进行更新数据库的操作,那么缓存就是旧数据

![[Pasted image 20250711174001.png]]

@Override  
@Transactional  
public Result update(Shop shop) {  Long id = shop.getId();  if (id == null) {  return Result.fail("店铺id不能为空");  }  // 1.更新数据库  updateById(shop);  // 2.删除缓存  stringRedisTemplate.delete(CACHE_SHOP_KEY + id);  return Result.ok();  
}

具体到代码在执行更新操作是,先更新数据库,再删除缓存

缓存穿透问题的解决思路

缓存穿透:客户端请求的数据在缓存中和数据库总都不存在,都有缓存永远不会生效,这些请求都会打到数据库。
解决方案:

将空对象缓存:哪怕数据在数据库中不存在也存入redis中,这样就不会访问数据库
实现简单,但会造成额外的内存消耗

布隆过滤:通过一个庞大的二进制数据,走哈希的思想判断这个数据是否存在,若存在才会放行
内存占用少,但实现复杂并有误判可能
![[Pasted image 20250711174637.png]]

缓存雪崩及解决思路

缓存雪崩是指同一时间大量缓存key同时失效导致Redis服务宕机,导致大量请求到达数据库,从而造成巨大的压力
解决方案:
给不同Key的TTL添加随机值
使用Redis集群
给缓存业务进行降级限流
给业务添加多级缓存
![[Pasted image 20250711174901.png]]

缓存击穿及解决思路

也叫热点key,就是一个高并发且缓存重建业务比较复杂(重建时间长)的key突然失效,无数请求的访问会瞬间给数据库带来巨大的冲击
解决方案:

互斥锁

将并行查询改为串行,一次只能一个线程访问数据库,使用tryLock和double check解决问题
![[Pasted image 20250711175126.png]]

逻辑过期

不设置过期时间,将过期时间设置在redis的value中,当线程1查询缓存时,发现数据已经过期了,他会开启一个新的线程去进行重构数据的逻辑,而线程1直接返回过期数据,假设线程3过来访问,由于线程2持有锁,线程3无法获得锁,它也直接返回过期数据
特点是在完成缓存重建之前,所有线程返回的都是脏数据
![[Pasted image 20250711175417.png]]

对比:

互斥锁:简单,保证数据一致,可能存在死锁风险且性能低
逻辑过期:读取不需要等待,性能好,在重构之前都是脏数据,实现复杂
![[Pasted image 20250711175513.png]]

使用互斥锁解决缓存击穿问题

 public Shop queryWithMutex(Long id)  {String key = CACHE_SHOP_KEY + id;// 1、从redis中查询商铺缓存String shopJson = stringRedisTemplate.opsForValue().get("key");// 2、判断是否存在if (StrUtil.isNotBlank(shopJson)) {// 存在,直接返回return JSONUtil.toBean(shopJson, Shop.class);}//判断命中的值是否是空值if (shopJson != null) {//返回一个错误信息return null;}// 4.实现缓存重构//4.1 获取互斥锁String lockKey = "lock:shop:" + id;Shop shop = null;try {boolean isLock = tryLock(lockKey);// 4.2 判断否获取成功if(!isLock){//4.3 失败,则休眠重试Thread.sleep(50);return queryWithMutex(id);}//4.4 成功,根据id查询数据库shop = getById(id);// 5.不存在,返回错误if(shop == null){//将空值写入redisstringRedisTemplate.opsForValue().set(key,"",CACHE_NULL_TTL,TimeUnit.MINUTES);//返回错误信息return null;}//6.写入redisstringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shop),CACHE_NULL_TTL,TimeUnit.MINUTES);}catch (Exception e){throw new RuntimeException(e);}finally {//7.释放互斥锁unlock(lockKey);}return shop;}

先进行获取锁,未获取到迭代继续获取,知道拿到后查询数据库,如果数据库没有,将空对象写入redis并返回null
如果有就写入redis,然后释放锁,最后返回数据库的结果

使用逻辑过期解决缓存击穿问题

在查询redis时,先判断是否命中,如果没有命中直接返回空数据,不查询数据库,一旦命中将value取出,判断value的过期时间,如果没过期直接返回数据,过期则开启独立线程后返回之前的数据,独立线程单独重构数据,重构完成后释放互斥锁
![[Pasted image 20250711204829.png]]

private static final ExecutorService CACHE_REBUILD_EXECUTOR = Executors.newFixedThreadPool(10);
public Shop queryWithLogicalExpire( Long id ) {String key = CACHE_SHOP_KEY + id;// 1.从redis查询商铺缓存String json = stringRedisTemplate.opsForValue().get(key);// 2.判断是否存在if (StrUtil.isBlank(json)) {// 3.存在,直接返回return null;}// 4.命中,需要先把json反序列化为对象RedisData redisData = JSONUtil.toBean(json, RedisData.class);Shop shop = JSONUtil.toBean((JSONObject) redisData.getData(), Shop.class);LocalDateTime expireTime = redisData.getExpireTime();// 5.判断是否过期if(expireTime.isAfter(LocalDateTime.now())) {// 5.1.未过期,直接返回店铺信息return shop;}// 5.2.已过期,需要缓存重建// 6.缓存重建// 6.1.获取互斥锁String lockKey = LOCK_SHOP_KEY + id;boolean isLock = tryLock(lockKey);// 6.2.判断是否获取锁成功if (isLock){CACHE_REBUILD_EXECUTOR.submit( ()->{try{//重建缓存this.saveShop2Redis(id,20L);}catch (Exception e){throw new RuntimeException(e);}finally {unlock(lockKey);}});}// 6.4.返回过期的商铺信息return shop;
}
  1. 线程池的运用
    private static final ExecutorService CACHE_REBUILD_EXECUTOR = Executors.newFixedThreadPool(10);
    
    这里创建了一个固定大小为 10 的线程池,目的是管控缓存重建任务。借助线程池,可以避免因大量创建线程而导致系统资源被过度占用。
  2. 异步任务的提交
    CACHE_REBUILD_EXECUTOR.submit( ()->{// 任务内容
    });
    
    当商铺缓存过期后,会向线程池提交一个重建缓存的任务,这样可以让主线程继续执行后续操作,不用等待缓存重建完成。
  3. 缓存重建的流程
    try{// 重建缓存this.saveShop2Redis(id, 20L);
    } catch (Exception e) {throw new RuntimeException(e);
    }
    
    • saveShop2Redis(id, 20L) 方法会从数据库获取最新的商铺数据,然后把这些数据存入 Redis,同时设置 20 秒的逻辑过期时间。
    • 对可能出现的异常进行捕获,将其封装成运行时异常后重新抛出。
  4. 锁的释放操作
    finally {unlock(lockKey);
    }
    
    不管缓存重建成功与否,最终都会执行 unlock(lockKey) 方法来释放锁,防止出现死锁的情况。

封装redis工具类

基于StringRedisTemplate封装一个缓存工具类
方法1:将任意Java对象序列化为Json并储存在string类型的key中,可设置TTL过期时间
方法2:将任意Java对象序列化为Json并储存在String类型的key中,可以设置逻辑过期时间,用于处理缓存击穿问题
方法3:根据指定的key查询缓存,并反序列化为指定类型,利用缓存空值的方式解决缓存穿透的问题
根据指定的key查询缓存,并反序列化为指定类型,需要利用逻辑过期解决缓存击穿问题

Shop shop = cacheClient.queryWithPassThrough( CACHE_SHOP_KEY, // 缓存键前缀 
id, // 商铺ID 
Shop.class, // 返回类型 
this::getById, // 数据库查询回调
CACHE_SHOP_TTL, // 缓存时间 
TimeUnit.MINUTES // 时间单位 );
public <R, ID> R queryWithPassThrough( String keyPrefix, ID id, Class<R> type, Function<ID, R> dbFallback, Long time, TimeUnit unit) { // ... R r = dbFallback.apply(id); // 调用传入的函数 // ... }

这里的 Function<ID, R> 是一个函数式接口,表示接受一个 ID 类型的参数,返回一个 R 类型的结果。
等价于id->getById(id),表示传入一个id参数,调用当前对象的getById方法处理它,并返回结果

总结

  1. 缓存的核心作用是什么?
    缓存通过将高频访问数据存储在内存中,显著提升读写效率、降低响应时间,同时减少后端数据库的访问压力,缓解高并发场景下的服务器负载。

  2. 常见的本地缓存实现有哪些?核心区别是什么?
    常见实现包括ConcurrentHashMap(线程安全,适用于高并发本地缓存,无持久化)、Guava Cache(功能丰富,支持过期策略、容量控制等,适合本地二级缓存)、HashMap(非线程安全,高并发下易出问题,不推荐直接使用)。核心区别在于线程安全性、功能丰富度及适用场景。

  3. 如何解决缓存与数据库的数据一致性问题?
    推荐 “先更新数据库,后删除缓存” 的策略:更新操作时,先保证数据库数据正确,再删除对应缓存,避免旧数据残留。单体系统中可通过事务保证操作原子性,分布式系统需结合 TCC 等分布式事务方案。

  4. 什么是缓存穿透?如何解决?
    缓存穿透指请求数据在缓存和数据库中均不存在,导致请求直接穿透缓存冲击数据库。解决方式包括:①缓存空对象(将不存在的数据以空值存入缓存,避免重复穿透);②布隆过滤(通过哈希判断数据是否存在,提前拦截无效请求)。

  5. 缓存击穿的解决方式有哪些?各有什么特点?
    缓存击穿指高并发下热点 Key 突然失效,大量请求瞬间冲击数据库。解决方式包括:①互斥锁(串行化请求,保证缓存重建时仅一个线程访问数据库,数据一致但性能略低);②逻辑过期(不设置物理过期,通过 value 中的逻辑时间判断,过期时异步重建缓存,性能高但可能返回脏数据)。

  6. 缓存雪崩的成因及预防措施是什么?
    缓存雪崩指大量缓存 Key 同时失效,导致 Redis 压力骤降、请求集中冲击数据库。预防措施包括:①给 Key 的 TTL 添加随机值,避免集中过期;②使用 Redis 集群提高可用性;③对缓存业务降级限流;④引入多级缓存减少单一层级依赖。

http://www.dtcms.com/wzjs/810648.html

相关文章:

  • 网站品牌形象设计怎么做网站推广岗位的要求
  • wordpress快站平台课程资源网站的建设
  • 织梦cms官方网站手机asp网站开发工具
  • 深圳网站制作的公司排名网站开发类投标文件
  • 郑州人才市场网站网站 制作公司
  • 设计网站排名哪个浏览器可以进wordpress
  • 青海建设厅网站黑名单门户网站建设管理总则
  • 电子商务网站功能设计与分析如何用wordpress做视频网站
  • 大连城乡住房建设厅网站网页界面设计课程
  • 苏州网站建设服务wordpress无法编辑页面
  • 网站搜索下拉是怎么做的发展历程 网站建设
  • 长春高铁站内容型网站
  • 三种常用的网站设计软件做电商网站价钱
  • 云南省网站建设收费调查报告网站设计计划书的要求
  • 电子商务+网站建设做网站如何不被忽悠
  • 河北三河建设厅网站6做国外贸易哪个网站好
  • 品牌网站制作流程付费主题wordpress
  • 美发网站怎么做广州有做虚拟货币网站
  • 网站图片用什么做的区块链开发工程师要求
  • 个人备案 网站名淄博网站建设公司三农
  • 高平网站优化公司地方门户网站系统有哪些
  • 长春网站建设模板样式做视频找素材的网站有哪些
  • 网站建设费用报价网站建设公司华网天下北京
  • idea做网站登录厦门网站建设工程
  • 山西通州集团网站建设国际进出口贸易网站
  • 网站建设 数据归属微信平台可以做微网站吗
  • 大连投诉网站网页升级访问最新区域每天自动更新
  • 无忧网站优化淘宝店招图片大全免费
  • 做网站编辑需要看什么书全网营销的四大优势
  • 集团网站建设报价企业局域网做网站屏蔽