点评项目(Redis中间件)&第四部分&缓存常见问题

缓存穿透

请求的数据在Redis里面没有,这种请求直接打到数据库,然后数据库里面也没有这个数据,数据库只能返回空,如果继续请求,就会一直打到数据库,可能数据库就会因此被打崩。

缓存雪崩

缓存击穿

第二种办法保证了绝对不会因为过期问题使访问直接打到数据库,但是为了实现过期这个功能我们就得用更复杂的办法,额外维护一个过期时间来实现过期这个功能。

逻辑过期里面也有锁的使用,但是是交给别的线程,所以不在我们的对比行列里面。

互斥锁解决击穿问题

Redis设置锁的方法

SETNX 是 Redis 的一个字符串(String)操作命令,它的全称是 SET if Not eXists

  • 功能 :当且仅当指定的 key 不存在 时,为这个 key 设置一个值。

  • 返回值

    • 1:表示设置成功(key 原本不存在)。

    • 0:表示设置失败(key 已经存在)。

这是一个原子性 操作,意味着它是不可分割的。在并发环境下,多个客户端同时执行 SETNX 时,Redis 能确保只有一个客户端会成功。

关于拆箱

核心概念:值类型 vs 引用类型

许多语言中,数据类型分为两大类:

  • 值类型 :变量直接存储数据本身。例如:int, float, char, bool 等基本数据类型。

    • 就像你口袋里直接揣着现金。
  • 引用类型 :变量存储的是一个内存地址(引用),这个地址指向实际的数据所在的内存位置。例如:接口数组字符串

    • 就像你口袋里揣着一张银行卡,钱存在银行的保险柜里。

为什么需要装箱和拆箱?

有时候,我们需要在需要引用类型 的地方使用一个值类型

一个经典例子:Java 中的集合(如 ArrayList)

Java 的 ArrayList 类的 add 方法定义是 add(Object obj),它只能接收引用类型 (因为 Object 是所有类的超类)。但如果我们想往里面存一个整数(int,它是值类型),该怎么办?

这就需要 "装箱"------ 把值类型"包装"成一个引用类型的对象。

装箱

装箱 就是将值类型 转换为对应的引用类型的过程。

这个过程通常是隐式的(编译器自动完成)。

Java 示例(在 Java 中称为"自动装箱"):

拆箱

拆箱 是装箱的逆过程,它将引用类型 转换回对应的值类型

这个过程有时需要显式地进行(需要手动指定目标类型)。

Java 示例(在 Java 中称为"自动拆箱"):

拆箱操作本质上是一个强制类型转换。你告诉编译器:"相信我,这个引用类型变量里面装的一定是某个特定的值类型,现在请把它拿出来。"

编译器在编译时无法100%确定你的这个"信任"是否正确,所以它允许代码通过编译。但如果在运行时发现你的"信任"是错的,就会立即抛出一个异常来中断程序。

操作 代码 背后原理 比喻
手动装箱 Integer box = Integer.valueOf(100); 调用静态工厂方法 valueOf,创建一个新对象。 去工厂订做一个盒子。
自动装箱 Integer box = 100; 编译器帮你写 Integer.valueOf(100) 告诉助理"我要个盒子",助理帮你订做。
手动拆箱 int i = box.intValue(); 调用对象实例方法 intValue(),取出其内部值。 自己动手打开盒子拿钱。
自动拆箱 int i = box; 编译器帮你写 box.intValue()
特性 boolean (基本类型) Boolean (包装类)
数据类型 基本数据类型 (引用类型)
默认值 false null
存储位置 栈内存 堆内存(对象本身),栈上存引用
占用空间 约1位(实际按1字节处理) 一整个对象的内存开销(更大)
比较方式 == 比较 == 比较内存地址equals() 比较包装的值
功能 仅能表示 true/false 是一个类,拥有方法(如 toString(), parseBoolean()
允许为null 不允许 允许
用途 普通的条件判断、循环控制 需要对象的地方(如泛型、集合)
java 复制代码
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;
    }

逻辑过期解决击穿问题

相关推荐
qq_4017004136 分钟前
Qt QUrl 详解与代码示例
开发语言·数据库·qt
程序员阿鹏1 小时前
如何实现MySQL分库分表?
数据结构·数据库·sql·mysql·缓存
爱和冰阔落1 小时前
【Linux】条件变量为什么必须配合互斥锁:从 pthread_cond_wait 到阻塞队列
linux·运维·c++·缓存·中间件·安卓
风哥2号1 小时前
数据库教程FGMT33‑MySQL主从复制项目实施与维护06(MySQL8.4/9.7 MGR组复制)
数据库·mysql
liangsheng_g2 小时前
Spring事务传播行为分派与挂起恢复源码实战
数据库·sql·spring
蓝速科技2 小时前
信创终端 POC 测试实战与选型避坑指南丨蓝速科技
运维·数据库·人工智能·科技·自然语言处理
wdfk_prog2 小时前
VMware Ubuntu 虚拟机从 124 GB 压缩到 33 GB
运维·缓存·docker·容器
风哥2号2 小时前
数据库教程FGMT29‑Linux平台MySQL5.7安装配置与管理入门
数据库
杨云龙UP3 小时前
MySQL Host is blocked because of many connection errors 导致 JDBC 连接失败排查与解决
linux·运维·网络·数据库·sql·mysql·登录失败
这个DBA有点耶4 小时前
数据库数据同步解决方案怎么选?6款主流工具横向对比与信创选型指南
数据库·架构·dba