Redis应用(缓存和分布式锁)

目录

一、缓存

1.1、缓存的本质

1.2、使用Redis作为缓存

1.3、缓存更新策略

1、定时生成

2、实时生成

3、淘汰策略

1.4、缓存四大核心问题

1、缓存预热

2、缓存穿透

3、缓存雪崩

4、缓存击穿

二、分布式锁

2.1、基本概念

2.2、分布式锁的逐步实现

1、基础实现:SETNX命令

2、添加过期时间

3、引入校验ID

4、引入Lua

[5、引入watch dog](#5、引入watch dog)

6、引入Redlock算法(解决集群中因主从切换导致锁丢失的问题)


一、缓存

1.1、缓存的本质

缓存是将高频访问的热点数据存储在 "更快的存储介质" 中(如内存),减少对底层存储(如 MySQL)的访问压力,核心遵循 "二八定律"(20% 热点数据支撑 80% 访问)。

1.2、使用Redis作为缓存

客户端访问业务服务器发起查询请求

业务服务器先查询Redis,如果数据在Redis中已经存在就直接返回不必访问MySQL,如果不存在在查询MySQL

优势:

  • 纯内存存储,访问速度比硬盘数据库快 3-4 个数量级;
  • 支持键过期、淘汰策略,适配缓存场景;
  • 单线程模型 + IO 多路复用,高并发支撑能力强;
  • 支持多种数据结构,适配不同缓存需求(如字符串存用户信息、哈希存商品属性)。
1.3、缓存更新策略

缓存的核心问题是"如何保证缓存和数据库数据一致",常见的更新策略如下

1、定时生成

每隔一定的周期(一天/一周/一个月),对于访问的数据频次进行统计。挑选出访问频次最高的前N%的数据

适用场景:非实时场景

2、实时生成

先给缓存设定容量,接下来查询的时候,如果在Redis中查到了,就直接返回;如果Redis中不存在,就从数据库中查,把查到的结果同时也写入Redis

如果缓存达到上限,就触发缓存淘汰策略,把相对不热门的数据淘汰掉

3、淘汰策略

(1)通用淘汰策略

FIFO (First In First Out)先进先出: 把缓存中存在时间最久的(也就是先来的数据)淘汰掉.
LRU(Least Recently Used)淘汰最久未使用的: 记录每个key的最近访问时间.把最近访问时间最老的key淘汰掉.
LFU(Least Frequently Used)淘汰访问次数最少的: 记录每个key最近一段时间的访问次数.把访问次数最少的淘汰掉.
Random随机淘汰**:**从所有的key中抽取幸运儿被随机淘汰掉.

(2)Redis内置淘汰策略

  • **volatile-lru:**淘汰设置过期时间的 key 中最近未使用的;
  • **allkeys-lru:**淘汰所有 key 中最近未使用的(最常用);
  • **volatile-lfu:**淘汰设置过期时间的 key 中访问次数最少的;
  • **allkeys-lfu:**淘汰所有 key 中访问次数最少的;
  • **noeviction:**默认策略,缓存满时拒绝新写入(推荐生产环境,提前扩容更安全)。
1.4、缓存四大核心问题
1、缓存预热

**定义:**Redis 启动或大量 key 失效后,提前将热点数据写入缓存,避免缓存空窗期导致数据库压力骤增。

实现方式:

离线统计热点数据(如通过日志分析),启动时批量加载;

服务启动后,模拟用户请求预热高频 key。

2、缓存穿透

**定义:**访问的 key 在 Redis 和数据库中均不存在,导致请求直接穿透到数据库,引发高负载。

原因:

(1)业务设计不合理,如缺少必要的参数检验环节,导致非法的key被进行查询

(2)开发/运维误操作,不小心把部分数据从数据库上误删了

(3)黑客恶意攻击

解决方案:

(1)参数合法性校验(如手机号格式校验);

(2)空值缓存:数据库不存在的 key,在 Redis 中存储空值(如 ""),设置短期过期时间;

(3)布隆过滤器:先使用布隆过滤器判定key是否存在,在真正查询;(布隆过滤器结合了hash+bitmap的思想,用较少空间判定某个元素是否存在)

3、缓存雪崩

**定义:**短期内大量key在缓存上失效,导致数据库压力骤增,甚至直接宕机

原因:

(1)Redis故障

(2)批量key设置相同过期时间

解决方案:

(1)过期时间设置为随机值,分散过期时间;

(2)部署 Redis 高可用集群(主从 + 哨兵 / 集群),避免单点故障;

4、缓存击穿

**定义:**热点 key 突然过期,大量并发请求直接访问数据库(缓存雪崩的 "单点案例")

解决方案:

(1)热点 key 永不过期;

(2)分布式锁限流:数据库查询加锁,同一时间仅允许一个请求查询数据库并回写缓存;

(3)延长过期时间:热点 key 过期前,通过后台任务自动续约。

二、分布式锁

2.1、基本概念

**1、定义:**分布式系统中也会涉及到多个节点访问同一个公共资源的情况,此时需要锁来做互斥控制,解决"跨进程、跨主机"的并发问题

本质是使用一个公共服务器来记录加锁的状态

这个公共服务器可以是Redis,也可以是其他组件

**2、核心要求:**互斥性、安全性(避免死锁)、可用性(集群部署)、原子性(加锁 / 解锁操作原子)。

**3、Redis实现优势:**高性能、支持过期时间、原子命令(SETNX、Lua 脚本)。

2.2、分布式锁的逐步实现
1、基础实现:SETNX命令

**核心逻辑:**SET key 服务器ID NX(key 不存在时设置成功,视为加锁),解锁时DEL key。

**问题:**服务器宕机会导致解锁操作不执行,其他服务器始终无法获取到锁

2、添加过期时间

核心逻辑: SET key 服务器ID NX EX 10(原子操作,同时设置过期时间10s)

**解决:**可以解决死锁问题(过期自动释放)

注意:此处过期时间只能使用一个命令的方式设置

如果分开多个操作,比如SETNX之后再来一个EXPIRE来设置过期时间,由于Redis多个指令之间不关联,即使使用事务也不能保证两个操作都成功,因此就可能出现SETNX成功,EXPIRE失败的情况,这种情况就和第一种实现方式一样,会导致死锁问题

**问题:**其他服务器可能误删锁(如服务器 1 加锁后超时未完成,锁自动过期,服务器 2 加锁,服务器 1 完成后删除服务器 2 的锁)。

3、引入校验ID

**核心逻辑:**加锁时存储 "服务器唯一 ID",解锁前先校验 ID 是否匹配,如果是当初加锁的服务器才能真正删除,如果不是就不能删除

**问题:**校验和删除非原子操作(可能校验时锁未过期,删除时已过期,仍可能误删)。

4、引入Lua

**核心逻辑:**解锁时执行 Lua 脚本,原子完成 "校验 + 删除":

复制代码
if redis.call('get', KEYS[1]) == ARGV[1] then
    return redis.call('del', KEYS[1])
else
    return 0
end

**解决:**避免校验和删除的竞态条件,保证解锁原子性

**问题:**还是可能会存在任务未执行完,key就过期的情况

5、引入watch dog

**核心逻辑:**加锁后启动独立线程,每隔 3 秒校验任务是否完成,如果已完成就使用Lua脚本释放锁,未完成则将锁过期时间续约至 10 秒

**解决:**避免锁过期时间过短,任务未完成导致锁提前释放

6、引入Redlock算法(解决集群中因主从切换导致锁丢失的问题)

**核心场景:**Redis 集群中,主节点加锁后宕机,从节点未同步锁信息,导致重复加锁。

核心思想:"少数服从多数",避免单点集群的一致性问题。

算法逻辑:

部署多组Redis节点(每组节点要包含1个主节点和若干从节点)

按预设顺序,向每组集群的 主节点 发送原子加锁命令

单个主节点加锁设超时阈值(如 50ms),超时未成功则跳过这个节点,给下一个节点加锁

满足两个条件则加锁成功:

加锁成功的主节点数 超总组数的一半(5 组需≥3 组,3 组需≥2 组);

整个加锁过程总耗时 ≤ 预设总超时时间。

相关推荐
2501_944521593 小时前
Flutter for OpenHarmony 微动漫App实战:底部导航实现
android·开发语言·前端·javascript·redis·flutter·ecmascript
程序媛_文乐3 小时前
【redis超过maxmemory值解决方法】
数据库·redis·缓存
予枫的编程笔记3 小时前
【Redis核心原理篇1】Redis 持久化:RDB、AOF、混合持久化,该怎么选?
数据库·redis·缓存·持久化·aof·rdb
Knight_AL3 小时前
RabbitMQ 中 Ready 和 Unacked 到底是什么意思?如何用它们判断系统是否健康
分布式·rabbitmq
007php0075 小时前
PHP与Java项目在服务器上的对接准备与过程
java·服务器·开发语言·分布式·面试·职场和发展·php
万象.6 小时前
redis数据结构set和zset的基本指令
数据结构·数据库·redis
what丶k12 小时前
深入理解Redis哨兵(Sentinel)原理:高可用架构的核心守护者
redis·缓存·架构
Go高并发架构_王工13 小时前
Kafka性能调优:从参数配置到硬件选择的全方位指南
分布式·kafka·linq
panzer_maus14 小时前
Redis的简单介绍(2)-处理过期Key的策略
数据库·redis·缓存