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

目录

前言

一、非关系数据库介绍

(一)定义

(二)分类

[二、Redis 介绍](#二、Redis 介绍)

(一)简介

(二)核心特点

[(三)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)**存储好友关系、共同关注、点赞列表。
  • 消息队列 :通过LISTLPUSH/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地址 角色
Redis50 192.168.88.50 Master
Redis51 192.168.88.51 Slave
Redis52 192.168.88.52 Master
Redis53 192.168.88.53 Slave
Redis54 192.168.88.54 Master
Redis55 192.168.88.55 Slave
Nginx56 192.168.88.56 Web服务器
  • :用于构建集群的主机不应存储任何数据,并且不应设置连接密码,配置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 t3
    
    redis-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 OK
    
    curl 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 *
相关推荐
程序猿小D32 分钟前
[附源码+数据库+毕业论文+开题报告]基于Spring+MyBatis+MySQL+Maven+jsp实现的车辆运输管理系统,推荐!
java·数据库·mysql·spring·毕业设计·开题报告·车辆运输管理系统
极限实验室2 小时前
ES 踩坑记:Set Processor 字段更新引发的 _source 污染
数据库
regret~3 小时前
【记录】Ubuntu20.04安装mysql
数据库·mysql
xd000025 小时前
ethers.js-7-事件的检索,监听,过滤
笔记
见未见过的风景5 小时前
想删除表中重复数据,只留下一条,sql怎么写
数据库·sql
_Kayo_6 小时前
项目学习笔记 display从none切换成block
windows·笔记·学习
哆啦A梦的口袋呀6 小时前
pymongo库:简易方式存取数据
数据库·mongodb
城里有一颗星星6 小时前
6.删除-demo
数据库·go
诸葛大钢铁7 小时前
Excel转PDF的三种方法
笔记·职场和发展·pdf·excel