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

Redis技术笔记-从三大缓存问题到高可用集群落地实战

目录

前言

一、非关系数据库介绍

(一)定义

(二)分类

二、Redis 介绍

(一)简介

(二)核心特点

(三)Redis 适用场景

(四)Redis 不适用场景

三、Redis的容错机制

四、缓存策略与优化

(一)缓存穿透

问题

解决方案

(二)缓存击穿

问题

解决方案

(三)缓存雪崩

问题

解决方案

五、搭建Redis集群

(一)Redis集群介绍

(二)基本环境准备

主机准备

集群环境准备

(三)创建集群

(四)查看集群

(五)测试集群

验证数据分布

测试数据自动备份

 测试服务高可用(故障转移)

搭建Web服务器并连接Redis集群

前言

  • 前面的博客详细介绍了MySQL的基本使用和优化,可在主页查看相关的MySQL博客进行了解。
  • 本文介绍Redis及缓存的三大问题缓存穿透、缓存击穿、缓存雪崩和Redis集群的搭建,下一篇介绍Redis的主从配置和Redis的哨兵及Redis的数据持久化

:如果想进一步了解Redis的使用的可以参考Redis中文文档教程

一、非关系数据库介绍

(一)定义

  • 非关系型数据库泛指不遵循传统关系模型(表、行、列、SQL)的数据存储系统
  • 它们通过更灵活的数据模型横向扩展架构以及针对特定场景的优化,解决高并发、海量数据、高可用与多活等问题。

(二)分类

数据模型代表系统典型场景关键特性与注意事项
键值对Redis缓存、会话、分布式锁全内存、单线程命令执行、RDB/AOF 持久化、主从+哨兵/集群高可用
文档MongoDB内容管理、订单详情BSON 存储、动态 Schema、副本集、分片集群
检索ElasticSearch全文检索、日志分析倒排索引、近实时搜索、分片+副本、DSL 查询
列族HBase写密集型、宽表存储LSM-Tree、Rowkey 设计决定性能、依赖 HDFS
Neo4j关系网络、推荐ACID 事务、Cypher 查询、节点-边-属性模型
时序Prometheus监控指标拉模式采集、PromQL、本地 TSDB、Alertmanager 告警
向量Milvus / Qdrant语义检索、推荐支持 IVF、HNSW 索引、GPU 加速、高维向量近似搜索

二、Redis 介绍

(一)简介

Redis(Remote Dictionary Server)是开源的、基于内存的键值存储系统,支持多种数据结构(String、Hash、List、Set、Sorted Set、Bitmap、HyperLogLog、Stream、Geo 等)。

(二)核心特点

特性维度技术实现优点
高性能全内存存储 + 单线程网络模型 + 非阻塞 I/O微秒级响应,缓存层 QPS 10 万级,读写极快,显著降低后端负载
多数据结构原生支持 String、Hash、List、Set、Sorted Set、Bitmap、HyperLogLog、Geo、Stream同一实例即可覆盖缓存、计数、队列、排行榜、去重、位置服务等场景,减少组件数量
原子操作单线程顺序执行命令,无锁竞争无需显式加锁即可实现 INCR、SET NX、Lua 脚本等业务级原子逻辑,避免并发问题
高可用主从复制 + Sentinel 自动故障转移 + Cluster 分片故障秒级切换,水平扩展无业务侵入,确保服务持续在线
双持久化RDB 快照(快速恢复)+ AOF 追加日志(低丢数)宕机后分钟级恢复,数据丢失控制在秒级,兼顾速度与可靠性
轻量消息Pub/Sub、Stream 消费组、Lua 原子脚本无需引入 Kafka 即可完成低延迟消息与事件驱动,架构更简洁

(三)Redis 适用场景

  • 缓存:将热点查询结果、会话状态或配置信息缓存在内存降低后端数据库压力
  • 排行榜:使用有序集合(ZSET)按分数排序,实现实时积分、销量或热度排行。
  • 计数器:利用 INCR、DECR 等原子指令,支持高并发 UV、PV、库存扣减。
  • 社交网络:以集合(SET)或哈希(HASH)存储好友关系、共同关注、点赞列表。
  • 消息队列:通过 LIST LPUSH/BRPOP 或 STREAM 实现轻量级任务队列与事件流。
  • 地理位置:基于 GEO 命令计算两点距离、附近人搜索。
  • 分布式锁SET key value NX PX ttl 保证跨节点互斥。
  • 发布/订阅:频道广播实现实时通知、配置推送。

(四)Redis 不适用场景

  • 超大规模冷数据:单实例内存有限,亿级冷日志建议落盘到 HDFS/S3
  • 复杂事务不支持跨键 ACID 事务,金融转账等场景需关系型数据库。
  • 大对象存储:单值超过 512 MB 时需拆片或使用对象存储。
  • 长时间持久化RDB/AOF 极端宕机仍可能丢数,关键订单需双写

三、Redis的容错机制

机制作用实现要点
哈希槽分片 16384 个槽均匀分布到各主节点,实现水平扩展;迁移期间新旧节点均可用,ASK 重定向保障读写不中断节点增减时自动迁移槽,客户端按 CRC16(key) mod 16384 定位槽
主从复制每个主节点拥有 ≥1 个从节点异步复制数据,支持读写分离;主故障时复制偏移量最大的从节点晋升全量 RDB + 增量 AOF,可配置 min-replicas-max-lag 控制复制延迟
故障检测每秒心跳探测,主观下线 → 客观下线;仅主节点投票避免脑裂基于半数以上主节点投票,cluster-node-timeout 定义超时阈值
故障转移哨兵或集群自动选主,<1 秒完成切换;切换后通过 CLUSTER SETSLOT 广播新拓扑从节点按复制偏移量与节点 ID 选举,重新分配槽并更新客户端路由表
Gossip 协议节点间周期性交换状态,维护无中心集群视图;网络分区时依赖多数派收敛每秒随机选 3 个节点交换 ping/pong 消息,消息体含节点 ID、槽位、故障标记
客户端重定向节点返回 MOVED/ASK 响应,客户端重发请求;主流客户端内置缓存无需停机MOVED 指示槽位已迁移,ASK 临时转发请求,客户端缓存槽位映射

四、缓存策略与优化

(一)缓存穿透

问题

原因目标
大量非法请求查询数据库不存在数据 → 既无法命中缓存,也查不到 DB → 每次请求都穿透缓存直达数据库。让“空结果”也具备缓存能力,堵住流量洪峰。

解决方案

  • 缓存空结果
    • 对于查询结果为空的数据,在缓存中记录短时间的空值标记(如 ##),后续相同查询直接返回空值避免重复回源
    • 作用:阻断针对不存在数据的恶意或异常高频请求。
    • 建议:空值标记设置 30–120 秒过期,既防止缓存穿透,又避免长期占用内存
  • 参数合法性校验
    • 在网关或业务入口层对查询参数进行范围、格式、签名等校验非法请求直接拒绝
    • 作用:在缓存层之前拦截无效流量,降低缓存与数据库压力。
    • 建议:将 ID 范围、正则规则、签名密钥配置化,支持热更新,提升灵活性。
  • 使用布隆过滤器
    • 所有可能存在的数据键写入布隆过滤器;查询先过过滤器不存在则立即返回存在再走缓存与数据库
    • 作用:以极小内存代价拦截 100% 不存在的数据请求,提升缓存命中率。
    • 建议:过滤器采用位图或 Redis 模块实现,定期异步重建,保持假阳性率低于 1%。

(二)缓存击穿

问题

原因目标
热点 Key 失效瞬间,大量并发请求同时回源 → DB 瞬时 QPS 飙升,甚至崩溃。保证同一时刻仅一个线程回源,其余线程等待。

解决方案

  • 互斥锁
    • 缓存失效瞬间,通过分布式锁(如 Redis SET NX PX)仅允许一个线程回源数据库,其余线程阻塞等待。
    • 作用避免并发回源穿透数据库,有效防止击穿风险。
    • 建议:注意加锁的开销,可能会导致大量线程阻塞等待锁,形成锁竞争,降低并发性能。
  • 软过期+互斥锁
    • 在缓存 value 中写入逻辑过期时间 T1(T1 < 实际 TTL T2)。
    • 读取时先检查 T1若未过期直接返回;若已过期,仅让一个线程获取分布式锁回源更新,其余线程仍返回旧值
    • 作用:相比单纯的互斥锁方案,能进一步减少读请求线程的阻塞时间。
    • 建议:合理设置逻辑过期时间和互斥锁的过期时间,结合重试机制,可以提高系统在高并发场景下的稳定性和性能。
  • 静态数据+Lazy Expiration
    • 在 Redis 中不设置 TTL,使 key 表面“永不过期”;在 value 头部写入 8-byte 逻辑过期时间戳。读取时先比对当前时间:
      • 未过期直接返回
      • 已过期:仅让一个线程抢分布式锁,后台异步线程执行回源写回新值,其余线程仍读旧值无阻塞
    • 作用:性能最好,避免了频繁的缓存失效和重建,系统吞吐最稳定。
    • 建议
      • 逻辑过期时间:在缓存值中设置一个逻辑过期时间,当取值时判断过期时间是否已经到达。
      • 异步更新:如果逻辑过期时间已过,则启动一个异步线程来更新缓存,而不是阻塞当前请求。
      • 互斥锁:避免多线程同时更新缓存,使用互斥锁来保证只有一个线程能进行更新操作。
  • 自动续期
    • 为热点 key 预设 30 min TTL,并启动周期任务:在 key 剩余 10 min 时回源刷新数据,成功后重置 TTL 为 30 min。
    • 作用避免缓存频繁失效,减少对数据库的压力。
    • 建议
      • 续期任务使用 Lua 脚本保证 GET + SET PX 原子操作,避免并发回源。
      • 采用分布式定时框架(如 Quartz + Redis 分布式锁)单实例执行,防止多节点重复刷新。
  • 暂时缓存不失效
    • 活动开始前,通过预热脚本把热点数据一次性加载到 Redis 并 不设置 TTL;活动结束后,统一脚本或消息触发 批量删除
    • 作用:避免热点数据频繁失效,提升系统的性能。
    • 建议
      • 预热阶段
        • 使用预热任务灰度执行,写入完成后通过 CLUSTER NODES 确认所有槽位同步成功,再开放流量。
        • 写入命令:SET key value(无 PX/EX 参数)。
      • 活动结束清理
        • 采用 SCAN + UNLINK 分批删除,避免一次性 DEL 大 key 导致节点阻塞;
        • 或把热点 key 统一放在 hot:{bizId}:* 命名空间,活动后用 FLUSHDB/UNLINK 通配删除。
      • 兜底策略
        • 预热失败或数据变更时,通过后台补偿任务以 Lazy Expiration 方式异步修正,保证最终一致。

(三)缓存雪崩

问题

原因目标
大批 Key 同时失效 或 缓存集群整体故障 → 流量瞬间压垮 DB让失效分散化 + 集群高可用 + 下游限流降级

解决方案

  • 分散过期时间
    • 在原始 TTL 上叠加 0-300 秒随机偏移,使用纳秒级随机避免伪随机聚集;对同一业务批次 key 采用滑动窗口方式错峰。
    • 作用:避免缓存key在同一时间失效防止大量 key 同时回源
  • 提前演练压测
    • 上线前模拟缓存全量失效、节点宕机、网络延迟三种场景;输出 QPS-RT 曲线与数据库安全阈值,据此调整连接池、线程池及分片数。
    • 作用提前暴露瓶颈,给出容量基线。
  • 缓存高可用+后端数据库限流
    • 双缓存热备:主备集群跨机架部署,使用客户端双写或异步复制;
    • 数据库限流:Hystrix 线程池隔离,熔断阈值按压测峰值 80% 设置,熔断后降级本地缓存或默认值。
    • 作用:缓存故障时仍保证核心链路可用。
  • 服务降级
    • 全局开关:30 秒内 Redis 失败 ≥5 次即开启,请求直接返回配置中心兜底数据; 
    • 自愈探针:后台任务每 30 秒探测 Redis,连续两次成功即关闭开关,恢复实时数据。
    • 作用:缓存连续异常时快速兜底,保护下游。

五、搭建Redis集群

  • 步骤基本环境准备 → 创建集群 查看集群信息 测试集群

(一)Redis集群介绍

  • Redis 集群通过数据分片与复制机制,实现了高可用性和水平扩展
  • 集群将 16384 哈希槽(Hash Slot)均匀分布到各个主节点上,每个主节点负责一部分槽的数据。
  • 每个槽通常会有多个副本存储在不同的从节点上,以防止节点故障导致数据丢失
  • 当有新的节点加入或节点移除时,槽会自动迁移,确保数据分布的均衡性

(二)基本环境准备

主机准备

主机名IP地址角色
Redis50192.168.88.50Master
Redis51192.168.88.51Slave
Redis52192.168.88.52Master
Redis53192.168.88.53Slave
Redis54192.168.88.54Master
Redis55192.168.88.55Slave
Nginx56192.168.88.56Web服务器

  • :用于构建集群的主机不应存储任何数据,并且不应设置连接密码,配置SELINUX和关闭防火墙。

集群环境准备

  • :所有主机都需要执行以下步骤,仅将bind之后的IP地址修改为对应主机的IP地址
    yum -y install redis #安装Redis服务vim /etc/redis.conf #修改配置文件
    #69行 
    bind   192.168.88.50 #指定 Redis 服务监听的 IP 地址#92行 
    port   6379 #指定 Redis 服务监听的端口号#838行 
    cluster-enabled yes #启用 Redis 集群模式#846行
    cluster-config-file nodes-6379.conf #指定集群配置文件的路径和名称#852行 
    cluster-node-timeout 5000 #设置集群节点间通信的超时时间(单位:毫秒)systemctl start redis #启动Redis服务ss -antlup | grep redis 
    #检查Redis服务的端口信息
    客户端端口(6379)处理客户端请求
    集群总线端口(16379)Redis节点之间的内部通信

(三)创建集群

  • :在任意一台主机执行一次即可。
    redis-cli --cluster create \192.168.88.50:6379 192.168.88.52:6379 192.168.88.54:6379 \192.168.88.51:6379 192.168.88.53:6379 192.168.88.55:6379 \--cluster-replicas 1#解释
    #redis-cli --cluster create 创建 Redis 集群,自动分配哈希槽(16384 个槽)并建立主从关系
    #IP地址 指定节点列表
    #-cluster-replicas 1 指定每个主节点的从节点数量
    #Redis集群的创建工具会严格按照节点列表的顺序分配主从角色,比如节点列表中的前三个作为主节点,后三个作为从节点

(四)查看集群

redis-cli --cluster info 192.168.88.50:6379 
#连接到指定的 Redis 集群节点(192.168.88.50:6379),并输出该节点所在集群的整体状态信息

(五)测试集群

验证数据分布

  • 目的:确认写入的三条测试键已根据 CRC16 算法均匀落入三个主节点的哈希槽,且可通过任意节点透明访问
    redis-cli -c -h 192.168.88.51 -p 6379 
    #-c 启用集群模式 -h <host> 指定Redis服务器的IP地址 -p <port> 指定Redis服务器的端口号set test1 t1 #设置测试用的键值对
    set test2 t2
    set test3 t3redis-cli --cluster info 192.168.88.50:6379 
    #查看集群状态信息,预期数据均匀分布在三个主节点上
    #比如分布在50、52、54主机上
    

测试数据自动备份

  • 目的:验证 Slave 实时同步 Master 数据,实现零人工干预的备份
    #通过命令行连接Slave节点查看数据,预期能看到之前存储的test1和test2和test3键
    redis-cli -c -h 192.168.88.51 -p 6379
    192.168.88.51:6379> keys *redis-cli -c -h 192.168.88.53 -p 6379
    192.168.88.53:6379> keys *redis-cli -c -h 192.168.88.55 -p 6379
    192.168.88.55:6379> keys *
    

 测试服务高可用(故障转移)

  • 目的:验证 Master 宕机Slave 自动升主恢复后自动降级为从 的完整流程。
  • 当前集群拓扑
    Master        Slot Range       Slave
    Redis50        0-5460          Redis51
    Redis52        5461-10922      Redis53
    Redis54        10923-16383     Redis55
    
  • 模拟 Redis50 Master 宕机
    #Redis52执行
    systemctl stop redis
  • 查看故障转移结果
    #Redis50主机执行
    #查看集群状态信息
    redis-cli --cluster info 192.168.88.50:6379#预期能看到Redis53节点上升为主节点
  • 原 Master 恢复并自动降级
    #Redis52执行
    systemctl start redis#Redis50执行
    redis-cli --cluster info 192.168.88.50:6379#预期能看到Redis52自动成为Redis53的从节点

搭建Web服务器并连接Redis集群

  • 目的:在 Nginx56 主机上完成 LNMP + Redis-Cluster 的端到端集成,实现 高并发缓存读写、水平扩展、故障自愈 三大目标。
  • :在Nginx56主机上搭建LNMP平台,php-fpm以非sock的方式运行,搭建可以参考Nginx技术笔记-从LNMP架构到反向代理、负载均衡、灰度发布的全攻略
  • 配置Redis PHP扩展
    tar -xf redis-cluster-4.3.0.tgz 
    #解压 Redis 集群的源代码包(redis-cluster-4.3.0.tgz)到当前目录,需要提前下载cd redis-4.3.0
    phpize
    #生成 PHP 扩展的配置脚本(configure),为后续编译做准备./configure --with-php-config=/usr/bin/php-config
    #配置 PHP 扩展的编译参数,指定 PHP 的配置路径(php-config)make && make install
    #make 根据 Makefile 编译 Redis 扩展
    #make install 将编译后的扩展文件(如 redis.so)安装到指定目录(如 /usr/lib64/php/modules/)ls /usr/lib64/php/modules/redis.so
    #验证 redis.so 文件是否已成功生成vim /etc/php.ini #修改 PHP 的全局配置文件,启用 Redis 扩展
    #737行 
    extension_dir = "/usr/lib64/php/modules/" #指定 PHP 扩展文件的存放路径#739行 
    extension = "redis.so" #启用 Redis 扩展systemctl restart php-fpm #重启 PHP-FPM 服务,使配置生效
    php -m | grep redis #验证 Redis 扩展是否已成功加载
  • 编写存储脚本
    vim /usr/local/nginx/html/set.php
    <?php
    #定义一个包含 Redis 集群节点地址和端口的数组
    $redis_list = ['192.168.88.50:6379','192.168.88.51:6379','192.168.88.52:6379','192.168.88.53:6379','192.168.88.54:6379','192.168.88.55:6379'
    ];#初始化一个 Redis 集群客户端对象
    #NULL 表示不使用密码认证
    #$redis_list 传入的 Redis 节点列表
    $client = new RedisCluster(NULL, $redis_list);#向 Redis 集群中写入三个键值对
    $client->set("test50","t50");
    $client->set("test52","t52");
    $client->set("test54","t54C");
    echo "SAVE OK\n";
    ?>
  • 编写查看脚本
    vim /usr/local/nginx/html/get.php
    <?php#定义Redis集群节点列表,用于建立 RedisCluster 客户端连接
    $redis_list = ['192.168.88.50:6379','192.168.88.51:6379','192.168.88.52:6379','192.168.88.53:6379','192.168.88.54:6379','192.168.88.55:6379'
    ];#初始化一个 Redis 集群客户端对象
    $client = new RedisCluster(NULL, $redis_list);#从 Redis 集群中读取三个键的值,并输出到客户端
    echo $client->get("test50");
    echo $client->get("test52");
    echo $client->get("test54");
    ?>
  • 访问测试
    curl http://192.168.88.56/set.php
    #返回结果
    SAVE OKcurl http://192.168.88.56/get.php
    #返回结果
    t50 t52 t54
  • 命令行验证数据分布
    # 以集群模式连接到 Redis 节点并查看数据
    redis-cli -c -h 192.168.88.50 -p 6379
    192.168.88.50:6379> keys *  #列出当前节点上所有的键redis-cli -c -h 192.168.88.52 -p 6379
    192.168.88.52:6379> keys *redis-cli -c -h 192.168.88.54 -p 6379
    192.168.88.54:6379> keys *
    

http://www.dtcms.com/a/276935.html

相关文章:

  • ESP-Timer入门(基于ESP-IDF-5.4)
  • JVM:内存、类加载与垃圾回收
  • 每天一个前端小知识 Day 30 - 前端文件处理与浏览器存储机制实践
  • [Rust 基础课程]选一个合适的 Rust 编辑器
  • 通用定时器GPT
  • 输入npm install后发生了什么
  • # 通过wifi共享打印机只有手动翻页正反打印没有自动翻页正反打印,而通过网线连接的主机电脑可以自动翻页正反打印
  • OneCode3.0 VFS分布式文件管理API速查手册
  • Codeforces Round 855 (Div. 3)
  • 【iOS】方法与消息底层分析
  • 动物世界一语乾坤韵芳华 人工智能应用大学毕业论文 -仙界AI——仙盟创梦IDE
  • Docker Compose文件内容解释
  • 鸿蒙选择本地视频文件,并获取首帧预览图
  • 14.ResourceMangaer启动解析
  • 【java】AI内容用SSE流式输出
  • 【读书笔记】《C++ Software Design》第七章:Bridge、Prototype 与 External Polymorphism
  • 数据库3.0
  • IPC框架
  • DAY01:【ML 第一弹】机器学习概述
  • php生成二维码
  • 15.手动实现BatchNorm(BN)
  • Spring Boot中的路径变量
  • JavaEE Tomcat
  • AI大模型计数能力的深度剖析:从理论缺陷到技术改进
  • 傅里叶变换中相位作用
  • 并查集 UnionFind Test01
  • 字符串问题(哈希表解决)
  • linux:进程详解(2)
  • Java结构型模式---享元模式
  • 代码随想录|图论|14有向图的完全可达性