Blage's Coding Blage's Coding
Home
算法
  • 手写Spring
  • SSM
  • SpringBoot
  • JavaWeb
  • JAVA基础
  • 容器
  • Netty

    • IO模型
    • Netty初级
    • Netty原理
  • JVM
  • JUC
  • Redis基础
  • 源码分析
  • 实战应用
  • 单机缓存
  • MySQL

    • 基础部分
    • 实战与处理方案
    • 面试
  • ORM框架

    • Mybatis
    • Mybatis_Plus
  • SpringCloudAlibaba
  • MQ消息队列
  • Nginx
  • Elasticsearch
  • Gateway
  • Xxl-job
  • Feign
  • Eureka
  • 面试
  • 工具
  • 项目
  • 关于
🌏本站
🧸GitHub (opens new window)
Home
算法
  • 手写Spring
  • SSM
  • SpringBoot
  • JavaWeb
  • JAVA基础
  • 容器
  • Netty

    • IO模型
    • Netty初级
    • Netty原理
  • JVM
  • JUC
  • Redis基础
  • 源码分析
  • 实战应用
  • 单机缓存
  • MySQL

    • 基础部分
    • 实战与处理方案
    • 面试
  • ORM框架

    • Mybatis
    • Mybatis_Plus
  • SpringCloudAlibaba
  • MQ消息队列
  • Nginx
  • Elasticsearch
  • Gateway
  • Xxl-job
  • Feign
  • Eureka
  • 面试
  • 工具
  • 项目
  • 关于
🌏本站
🧸GitHub (opens new window)
  • Redis基础

  • 源码分析

  • 实战应用

    • 缓存问题&更新策略
      • 缓存穿透
      • 缓存雪崩
      • 缓存击穿
      • 封装工具类代码复用
    • 缓存最佳实践
    • Redis面试
  • 单机缓存

  • Redis
  • 实战应用
phan
2023-05-15
目录

缓存问题&更新策略

# 缓存问题&更新策略

  • 保证数据库和缓存的原子性
  • 写操作:先写数据库,然后再删除缓存(先删缓存容易出现雪崩)
  • 读操作:先查缓存,没有则查数据库,然后写进缓存,并设置超时时间。

# 缓存穿透

缓存穿透:客户端请求的数据在缓存中和数据库中都不存在。缓存永远不会生效,最终打到数据库。

①缓存空对象:为了不让请求再次打到数据库,当数据库查询没有结果时,向redis中缓存一个null。

缺点:内存中缓存了过多的垃圾(可以设置TTL)

stringRedisTemplate.opsForValue().set(id.toString(), "",2,TimeUnit.MINUTES);
1

②布隆过滤器:在客户端和Redis之间添加一层布隆过滤器,请求过来时,布隆过滤器会告诉客户端数据在redis还是数据库,如果都不在则直接返回。布隆过滤器说不在,那么数据肯定不在DB;而如果布隆过滤器说在,那么数据可能在DB,出现误判。

布隆过滤器原理可以认为是一个值只为0或1的数组,添加数据时,比如说我要添加A,就会同通过一个hash散列映射为一个0101的字符串,写入数组的时候只需要在字符串对应为1的位置记为1即可(0位置是否为0不需要关注)。查询数据时,假设我要查询B,就会先通过hash散列映射为一个字符串,然后在布隆过滤器数组中查看字符串所有对应为1的位置是否都为1,如果存在不为1的说明该数据不在集合中。这种方法只能加数据不能减数据,也会存在误判,在于如果映射的字符串为0100,那么就会出现误判。因此误判率高低取决于hash散列的复杂程度,以及数组的长度。

# 缓存雪崩

缓存雪崩:Redis大量的缓存key同时失效过期,或者redis宕机,导致大量请求到达数据库。

①给不同key的TTL添加随机值

②Redis集群,服务降级限流

# 缓存击穿

缓存击穿:热点key问题。高并发访问的key失效了,并且这个热点key请求的时间较长业务复杂,在这段时间内无数的请求都会瞬间达到数据库。

①互斥锁:线程需要等待,保证一致性。使用setnx实现互斥锁,释放锁时则删除key

  • 查redis:①有数据直接返回②有空数据直接返回(解决缓存穿透)
  • 申请锁:①如果没有获得则重新进入函数②如果获得锁,再检查一次redis有数据直接返回(二次检查),redis没有则查数据库。
  • 释放锁
public Shop queryWithmutex(Long id) {
        //1.查redis
        //2.申请锁查数据库
        String lockkey = "lock" + id;
        Shop shop = null;
        try {
            boolean b = tryLock(lockkey);
            if (!b) {
                Thread.sleep(50);
                return queryWithmutex(id);
            }
            //二次查询数据库
            String res = stringRedisTemplate.opsForValue().get(id.toString());
            if (StrUtil.isNotBlank(res)) {
                return JSONUtil.toBean(res, Shop.class);
            }
            shop = getById(id);
            Thread.sleep(200);
            if (shop == null) {
                //解决缓存穿透
                stringRedisTemplate.opsForValue().set(id.toString(), "",2,TimeUnit.MINUTES);
                return null;
            }
            stringRedisTemplate.opsForValue().set(id.toString(), JSONUtil.toJsonStr(shop), 30, TimeUnit.MINUTES);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }finally {
            unlock(lockkey);
        }
        return shop;
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

②逻辑过期:线程无需等待,性能好。不保证一致性。(互斥锁+线程池)

image-20230312190116955

# 封装工具类代码复用

public <R,ID> R queryWithPassThrough(
   String keyPrefix, ID id, Class<R> type, Function<ID, R> dbFallback, Long time, TimeUnit unit){
        String key = keyPrefix + id;
        // 1.从redis查询商铺缓存
        String json = stringRedisTemplate.opsForValue().get(key);
        // 2.判断是否存在
        if (StrUtil.isNotBlank(json)) {
            // 3.存在,直接返回
            return JSONUtil.toBean(json, type);
        }
        // 判断命中的是否是空值
        if (json != null) {
            // 返回一个错误信息
            return null;
        }
        // 4.不存在,根据id查询数据库
        R r = dbFallback.apply(id);
        // 5.不存在,返回错误
        if (r == null) {
            // 将空值写入redis
            stringRedisTemplate.opsForValue().set(key, "", CACHE_NULL_TTL, TimeUnit.MINUTES);
            // 返回错误信息
            return null;
        }
        // 6.存在,写入redis
        this.set(key, r, time, unit);
        return r;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
编辑 (opens new window)
#Redis#高并发
上次更新: 2023/12/15, 15:49:57
优化
缓存最佳实践

← 优化 缓存最佳实践→

Theme by Vdoing | Copyright © 2023-2024 blageCoder
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式