Redis从入门到精通:数据结构、持久化与高可用设计

一、Redis基础知识

1、Redis概述

Redis(Remote Dictionary Server)是一个开源的内存型键值数据库,支持分布式架构和持久化存储。其核心特性如下:

  1. 内存存储
    Redis将所有数据存储在内存中,读写操作无需磁盘I/O,响应速度达到微秒级(读11万次/秒,写8.1万次/秒)。同时支持RDB快照和AOF日志两种持久化机制,确保数据安全。

  2. 高性能

    • 单线程模型:早期版本通过单线程处理命令,避免多线程竞争问题。
    • I/O多路复用:基于Epoll/Kqueue实现高并发请求处理。
    • 网络优化:Redis 6.0引入多线程网络I/O,提升吞吐量。
  3. 丰富数据结构
    支持多种数据类型,包括:

    • 基础类型:字符串(String)、列表(List)、哈希(Hash)、集合(Set)、有序集合(ZSet)。

    • 扩展类型:位图(Bitmap)、地理位置(GEO)、流(Stream)、HyperLogLog。
      这些结构支持原子操作(如集合交并差运算、有序集合排序),适用于复杂业务场景。
  4. 适用场景
    • 缓存:
      • 核心作用:缓解数据库压力,加速热点数据访问(如商品信息、用户会话)
      • 典型流程:客户端请求→查询Redis缓存→命中则返回,未命中则查数据库并更新缓存
      • 穿透防御:结合布隆过滤器(Bloom Filter)过滤无效请求
    • 分布式锁:
      • 实现方式:通过SETNX命令(或RedLock算法)实现跨服务的互斥资源访问
      • 应用场景:秒杀库存扣减、分布式任务调度
    • 消息队列:
      • 基础功能:使用列表(List)实现生产者-消费者模型(如LPUSH/BRPOP命令)
      • 高级模式:通过发布订阅(Pub/Sub)支持实时消息广播(如聊天室、通知系统)
    • 排行榜:
      • 技术实现:利用有序集合(ZSet)按分数(Score)排序,支持实时更新和范围查询
      • 典型应用:游戏积分榜、电商销量排名、热搜榜单

2、安装与配置

1)安装准备

环境要求

  • 操作系统:支持Linux(CentOS/Ubuntu等)、Windows
  • 依赖工具:gcc编译器(Linux)、wget或压缩包下载工具

版本选择

  • 推荐使用最新稳定版(如7.x),可通过官网(Downloads - Redis)或镜像站(如华为云镜像)下载。

2)Linux系统安装(以CentOS/Ubuntu为例)

安装依赖
# CentOS
sudo yum install -y gcc make wget
# Ubuntu
sudo apt update && sudo apt install -y build-essential tcl
下载与解压源码
wget http://download.redis.io/releases/redis-7.2.4.tar.gz
tar xzf redis-7.2.4.tar.gz
cd redis-7.2.4
 编译安装
make && sudo make install

默认安装路径为 /usr/local/bin,包含 redis-server(服务端)和 redis-cli(客户端)。

配置Redis

 创建配置文件目录

sudo mkdir /etc/redis
sudo cp redis.conf /etc/redis/6379.conf

修改关键参数(使用 vim /etc/redis/6379.conf

bind 0.0.0.0          # 允许远程访问
port 6379             # 默认端口
daemonize yes         # 后台运行
dir /var/lib/redis    # 数据存储路径
requirepass yourpass  # 设置访问密码
protected-mode no     # 关闭保护模式(远程访问需关闭)

 启动Redis服务

redis-server /etc/redis/6379.conf

验证安装

redis-cli -h 127.0.0.1 -p 6379 -a yourpass
127.0.0.1:6379> ping  # 返回 "PONG" 表示成功

3、核心数据结构

Redis 的核心数据结构分为两个层面:对外暴露的 5 种基本数据类型底层实现的高效数据结构。以下从这两个维度详细解析:

1)对外数据类型(value类型)

Redis 提供 5 种基础数据结构,所有键(Key)均为字符串,而值(Value)支持以下类型:

  1. String(字符串)

    • 功能:支持原子增减(INCR/DECR)、二进制安全存储。
    • 应用场景:缓存、分布式锁(SETNX)、计数器(如文章阅读量统计)。
    • 底层实现:根据内容长度和类型,可能使用 int(整数)、embstr(≤39字节的短字符串)或 raw(动态字符串 SDS)编码。
  2. Hash(哈希表)

    • 功能:存储字段-值对,支持部分字段操作(HGET/HSET)。
    • 应用场景:对象缓存(如用户信息)、购物车(商品 ID 与数量映射)。
    • 底层实现:默认使用 ziplist(压缩列表)存储小规模数据,超过阈值后转为 hashtable(哈希表)。
  3. List(列表)

    • 功能:支持双端插入/弹出(LPUSH/RPOP),可实现栈或队列。
    • 应用场景:消息队列(BLPOP 实现阻塞消费)、微博关注列表。
    • 底层实现:早期用双向链表,后优化为 quicklist(快速列表,由多个压缩链表片段组成)。
  4. Set(集合)

    • 功能:无序唯一元素集合,支持交/并/差集运算(SINTER/SUNION)。
    • 应用场景:标签系统、共同好友计算。
    • 底层实现:小规模时用 intset(整数集合),大规模时转为 hashtable
  5. ZSet(有序集合)

    • 功能:按分值(Score)排序,支持范围查询(ZRANGEBYSCORE)。
    • 应用场景:排行榜、延迟队列(按时间戳排序)。
    • 底层实现:结合 skiplist(跳跃表)和 hashtable,前者维护有序性,后者快速访问元素。

2)底层数据结构(实现基础)

  1. 简单动态字符串(SDS)

    • 特点:预分配空间和惰性释放策略,减少内存重分配次数。
    • 结构:包含 len(长度)、alloc(预分配空间)、buf[](字符数组)。
  2. 字典(Dict)

    • 作用:实现哈希键和 Redis 数据库的存储。
    • 特性:使用链地址法解决哈希冲突,支持渐进式 Rehash(ht[2] 双表交替迁移)。
  3. 跳跃表(Skiplist)

    • 优势:平均 O(logN) 的查询效率,支持范围操作,用于 ZSet 的有序性维护。
  4. 压缩列表(Ziplist)

    • 设计:连续内存块存储多个元素,节省内存,适用于小规模 Hash/List/ZSet。
  5. 快速列表(Quicklist)

    • 优化:将多个压缩列表片段通过双向链表连接,平衡内存效率和操作性能。

4、持久化机制

  1. RDB 持久化(快照持久化)

    1. 原理

      RDB 通过生成内存数据的二进制快照文件(默认 dump.rdb)保存数据。快照的触发方式包括:

      1. 自动触发:基于配置规则(如 save 900 1 表示 900 秒内有至少 1 次写操作时触发)。
      2. 手动触发:通过 SAVE(阻塞主进程)或 BGSAVE(异步生成,利用子进程)命令。
    2. 优点
      1. 恢复速度快:二进制文件紧凑,加载效率高。
      2. 性能影响小BGSAVE 通过子进程操作,主进程可继续处理请求。
      3. 适合备份与归档:文件体积小,便于长期存储和灾难恢复。
    3. 缺点
      1. 数据丢失风险:若两次快照间发生故障,最后一次快照后的数据会丢失。
      2. 资源占用高:生成快照时可能因数据量大导致 CPU 和内存压力。
    4. 适用场景
      1. 定期备份(如每日全量备份)。
      2. 容忍部分数据丢失的场景(如缓存数据、统计类数据)。
  2. AOF 持久化(日志追加持久化 append only file)

    1. 原理

      AOF 记录所有写操作命令(如 SETDEL),以追加方式写入日志文件(默认 appendonly.aof)。通过 appendfsync 参数控制同步策略:

      1. always:每次写操作后同步(最安全,性能最低)。
      2. everysec:每秒同步(默认,平衡安全与性能)。
      3. no:依赖操作系统同步(性能最好,安全性最低)。
    2. 优点

      1. 数据完整性高:可恢复至故障前的最后一条命令。
      2. 可读性强:日志文件为文本格式,便于人工检查和修复。
    3. 缺点

      1. 文件体积大:频繁写操作导致日志膨胀,需定期执行 BGREWRITEAOF 压缩。
      2. 恢复速度慢:需逐条重放命令,数据量大时耗时较长。
    4. 适用场景

      1. 对数据安全性要求极高的场景(如金融交易记录)。
      2. 需要精确恢复至特定时间点的场景。
  3. 混合持久化(RDB + AOF)

    1.  原理

      结合 RDB 和 AOF 的优势:

      1. 定期生成 RDB 快照作为全量备份。
      2. 两次快照间通过 AOF 记录增量写操作。
      3. 重启时优先加载 AOF 文件,再结合 RDB 实现快速恢复。
    2. 优点

      1. 兼顾数据安全性与恢复效率。
      2. 适合对数据完整性和恢复速度均有高要求的场景。
  4. 持久化策略选择建议

    1. 仅用 RDB:适合缓存场景或对数据丢失容忍度较高的业务。
    2. 仅用 AOF:适合对数据一致性要求严苛的业务(需接受性能损耗)。
    3. 混合模式:推荐生产环境使用,平衡安全性与效率。

5、基础命令与操作

以下是 Redis 的基础命令与操作分类整理,结合了数据存储、键管理、事务处理等核心功能:

  1. 键(Key)操作

    1. 基础操作

      • SET key value:设置键值对(支持字符串、数值等类型)
      • GET key:获取键对应的值
      • EXISTS key:检查键是否存在,返回 1(存在)或 0(不存在)
      • DEL key:删除指定键
      • KEYS pattern:按模式匹配查询键(如 KEYS * 查询所有键,但生产环境慎用)
    2. 过期与生命周期

      • EXPIRE key seconds:设置键的过期时间(秒)
      • TTL key:查看键剩余过期时间(-1 表示永不过期,-2 表示已过期)
      • PERSIST key:移除键的过期时间,使其永久有效
    3. 数据库管理

      • SELECT index:切换数据库(默认 0-15 共 16 个库)
      • FLUSHDB:清空当前数据库
      • FLUSHALL:清空所有数据库
  2. 数据类型操作

    1. 字符串(String)
      1. SETEX key seconds value:设置键值并指定过期时间
      2. INCR key:将键值加 1(适用于计数器)
      3. DECR key:将键值减 1
      4. APPEND key value:追加字符串到现有值末尾
      5. STRLEN key:获取字符串长度
    2. 哈希(Hash)
      1. HSET key field value:设置哈希表中字段的值
      2. HGET key field:获取哈希表字段值
      3. HGETALL key:获取哈希表所有字段和值
      4. HDEL key field:删除哈希表字段
    3. 列表(List)
      1. LPUSH key value:从列表头部插入元素
      2. RPUSH key value:从列表尾部插入元素
      3. LRANGE key start end:获取列表指定范围的元素(0 -1 表示全部)
      4. LPOP key:移除并返回列表头部元素
    4. 集合(Set)
      1. SADD key member:向集合添加元素
      2. SMEMBERS key:获取集合所有元素
      3. SINTER key1 key2:返回多个集合的交集
      4. SISMEMBER key member:判断元素是否在集合中
    5. 有序集合(ZSet)
      1. ZADD key score member:添加带权重的元素(用于排行榜)
      2. ZRANGE key start end:按分数升序返回元素
      3. ZREVRANGE key start end:按分数降序返回元素
  3. 事务与持久化

    1. 事务

      1. MULTI:开启事务
      2. EXEC:执行事务块内的所有命令
      3. DISCARD:取消事务
    2. 持久化

      • SAVE:同步保存数据到磁盘(阻塞其他操作)
      • BGSAVE:后台异步保存数据到磁盘
      • AOF:通过日志追加方式持久化(需配置策略如 appendfsync everysec
  4. 实用工具命令

    1. PING:测试服务是否连通(返回 PONG
    2. INFO:查看服务器基本信息(如内存、持久化状态)
    3. CONFIG GET parameter:获取配置参数(如 maxmemory
  5. 注意事项

    1. 性能优化:避免在生产环境使用 KEYS *,改用 SCAN 分批次查询
    2. 数据类型选择:根据场景选择合适的数据结构(如计数器用字符串,社交关系用集合)
    3. 事务限制:Redis 事务不支持回滚,需通过 WATCH 实现乐观锁

二、Redis进阶知识

1、主从复制与集群

  1. 主从复制(Master-Slave Replication)
    1. 核心概念
      • 主节点(Master):负责写入数据,数据变更后自动同步到从节点
      • 从节点(Slave):只读节点,通过全量/增量复制机制从主节点同步数据
      • 数据单向性:复制仅从主到从,无法反向同步
    2. 复制机制

      • 全量复制:首次连接或数据差异较大时触发。主节点生成RDB快照发送给从节点,并记录后续写命令到缓冲区,传输完成后从节点加载快照并执行缓冲区命令
      • 增量复制:主节点通过PSYNC命令将网络中断期间的写命令发送给从节点,依赖复制偏移量(offset)和复制积压缓冲区(repl_backlog)实现断点续传
      • 命令传播:主节点实时将写命令发送给从节点,保持数据一致性
    3. 作用与优缺点

      • 作用:数据冗余备份、读写分离(主写从读)、故障恢复
      • 优点:提升读性能,支持横向扩展
      • 缺点:主节点单点故障需手动切换;复制延迟可能导致数据不一致
  2. 哨兵模式(Sentinel)
    1. 核心功能
      • 监控:持续检测主从节点健康状态,通过心跳机制判断节点是否存活
      • 自动故障转移:主节点宕机时,哨兵选举新主节点(优先级:复制偏移量 > 运行ID),并通知其他从节点切换主节点
      • 通知客户端:向客户端推送主节点变更信息,实现动态服务发现
    2. 选举规则

      • 排除断开时间过长的从节点。
      • 优先选择slave-priority值低、复制偏移量高、运行ID小的从节点
    3. 适用场景

      • 高可用需求场景,避免人工干预主从切换
  3. 集群模式(Redis Cluster)
    1. 架构原理
      • 数据分片:将数据划分为16384个哈希槽(hash slot),每个节点负责部分槽位
      • 主从结构:每组槽位对应一个主节点和多个从节点,主节点处理读写,从节点备份数据
      • 动态扩容:支持在线增删节点,通过槽位迁移实现负载均衡
    2. 高可用机制

      • 故障转移:主节点宕机时,从节点自动升级为主节点
      • Gossip协议:节点间通过PING-PONG通信同步状态,检测故障并触发恢复
    3. 优缺点

      • 优点:支持水平扩展、高吞吐量、自动容灾
      • 缺点:配置复杂;跨槽操作需使用{}标签强制路由,部分命令不支持(如多键事务)
  4. 主从复制与集群对比
    特性主从复制哨兵模式Redis Cluster
    数据分布单一主节点,全量数据复制主从结构+哨兵监控分片存储,多主多从
    故障恢复手动切换自动切换主节点自动故障转移
    扩展性从节点扩展读能力主从扩展+哨兵高可用支持动态水平扩展
    适用场景读写分离、基础容灾高可用但数据量中等海量数据、高并发场景
  5. 核心配置与命令
    1. 主从复制配置

      1. 从节点配置:REPLICAOF <master_ip> <master_port>
      2. 查看角色:ROLE命令显示节点状态(主/从)及复制偏移量
    2. 哨兵配置

      1. 哨兵节点监控主节点:sentinel monitor <master_name> <ip> <port> <quorum>
    3. 集群搭建

      • 创建集群:redis-cli --cluster create <nodes> --cluster-replicas <num>
      • 数据迁移:CLUSTER ADDSLOTS分配槽位,CLUSTER MEET添加节点

2、性能优化

  1. 配置调优

    1. maxmemory:设定Redis实例的最大内存使用量。这不仅有助于防止Redis占用过多系统资源,还能在达到内存限制时触发相应的淘汰策略。
    2. maxclients:设置最大客户端连接数,确保服务器能够处理预期数量的并发请求。
    3. tcp-keepalive:保持TCP连接活跃,避免长时间无数据传输导致的连接中断问题。
  2. 数据结构选择与优化

    1. Hashes、Lists、Sets和Sorted Sets:对于某些特定场景,这些数据结构比基本的字符串类型更加高效。例如,当你需要存储用户信息时,使用哈希表而不是多个单独的键值对可以显著减少内存消耗。
    2. Pipeline:通过批量发送命令来减少网络往返时间(RTT),特别是在执行大量读写操作时效果明显。
    3. Lua脚本:利用Lua脚本在服务端执行复杂的事务逻辑,减少客户端与服务器之间的通信开销。
  3. 持久化策略

    1. RDB快照:定期创建内存数据的快照文件,适合用于备份和灾难恢复。但要注意调整save参数以平衡数据安全性和性能影响。
    2. AOF日志:记录每个写操作到日志文件中,提供更好的数据安全性。通过配置appendfsync选项控制同步频率,以权衡性能和可靠性。
  4. 内存管理

    1. 淘汰策略:如volatile-lru、allkeys-lru等,根据应用需求选择合适的淘汰策略,以保证Redis在内存满载时仍能正常工作。
    2. 对象共享:Redis内部会对小范围的整数值进行对象共享,减少内存占用。
  5. 网络优化

    1. 连接池:减少频繁建立和断开连接的成本,提升响应速度。
    2. Redis Cluster:当单个节点不足以应对负载时,可以通过集群分散流量,实现水平扩展。
  6. 监控与维护

    1. 使用监控工具(如Prometheus+Grafana)实时监测Redis运行状态,包括内存使用情况、CPU负载、命中率等关键指标。
    2. 定期分析慢查询日志,识别并优化耗时长的操作。
  7. 更新与升级

    1. 保持Redis版本更新至最新稳定版,以获得最新的功能增强和性能改进。

3、缓存问题

  1. 缓存穿透

    1. 定义
      1. 缓存穿透是指查询一个根本不存在的数据由于缓存中没有该数据的信息,导致每次请求都会直接访问数据库。这种情况不仅浪费了数据库资源,还可能因大量无效请求导致数据库过载。下面详细聊聊Redis缓存穿透的问题及其解决方案。

    2. 缓存穿透的原因
      1. 恶意攻击:黑客故意构造大量不存在的键进行频繁请求,目的是使后端数据库崩溃。
      2. 系统设计缺陷:在某些情况下,业务逻辑允许查询非存在的数据,且这些查询未被妥善处理。
    3. 解决方案
      1. 缓存空对象

        当从数据库查询到某个键对应的数据为空时,可以将这个键对应的值设置为一个特殊值(例如null),并设置较短的过期时间。这样下次再有相同的请求时,可以直接从缓存中返回结果,避免再次查询数据库。

        1. 优点:简单易行,能有效防止缓存穿透。
        2. 缺点:如果空对象过多,会占用额外的缓存空间;若过期时间设置不当,可能会导致短时间内多次查询数据库。
      2. 布隆过滤器(Bloom Filter)

        布隆过滤器是一种高效的空间利用率高的概率型数据结构,用于判断一个元素是否在一个集合中。通过预先将数据库中的所有键加入布隆过滤器,可以在查询前先检查布隆过滤器,如果它说“不在”,则肯定不在;如果说“可能存在”,则进一步查询数据库或缓存。

        1. 优点:极大地减少了对数据库的无谓查询,非常适合高并发场景。
        2. 缺点:存在一定的误判率,即可能会错误地标记某些键为“可能存在”。
      3. 接口层增加校验

        在应用层面增加必要的参数校验和合法性检查,如用户输入的有效性验证,拒绝非法请求到达服务层。

        1. 优点:可以从根本上减少不必要的请求,保护后端服务。
        2. 缺点:需要在业务逻辑中添加额外的检查逻辑,增加了开发成本。
      4. 限流

        对于同一类型的请求,在一定时间内限制其访问频率,可以通过令牌桶算法、漏桶算法等实现。

        1. 优点:有效防止因突发流量导致的服务不可用。
        2. 缺点:需根据具体业务场景调整限流策略,否则可能影响正常用户的使用体验。
  2. 缓存雪崩

    1. 定义
      1. Redis缓存雪崩是指在某一时刻,大量缓存数据同时失效,导致大量的请求直接打到后端数据库,从而可能导致数据库负载骤增,甚至崩溃的情况。这种情况类似于缓存穿透,但其原因和影响范围通常更为广泛。
    2. 缓存雪崩的原因
      1. 缓存数据过期时间集中

        1. 如果大量缓存数据设置了相同的过期时间,一旦这些缓存同时到期,将会导致大量请求瞬间涌向数据库。
      2. Redis实例宕机或重启

        1. 当Redis实例出现故障(如宕机或重启)时,所有缓存数据都会丢失,导致所有的请求都必须重新从数据库加载数据。
      3. 网络问题或其他系统故障

        • 网络波动或者系统其他部分的故障也可能导致缓存服务不可用,进而引发类似的问题。
    3. 缓存雪崩的影响
      1. 数据库压力骤增:由于大量请求直接访问数据库,可能会导致数据库响应变慢,甚至崩溃。
      2. 系统性能下降:数据库负载增加会导致整个系统的响应速度变慢,用户体验恶化。
      3. 可能引起连锁反应:如果数据库不堪重负,可能进一步影响其他依赖该数据库的服务,形成连锁反应。
    4. 预防和应对措施
      1. 设置随机过期时间

        为了避免大量缓存数据在同一时间点失效,可以在设置缓存过期时间时加入一个随机因子。例如,原定的过期时间为60分钟,可以在这个基础上加上一个0到10分钟之间的随机值。

      2. 使用互斥锁(Mutex Lock)

        当某个缓存失效时,可以通过加锁机制确保只有一个线程去重建缓存,其他线程等待该线程完成后再从缓存中读取数据。这可以避免多个线程同时查询数据库,减轻数据库的压力。

      3. 热点数据永不过期

        对于一些非常重要的热点数据,可以设置它们永不过期,并通过后台任务定期更新这些数据。这样即使发生缓存雪崩,也不会影响到这些关键数据的可用性。

      4. 多级缓存策略

        除了Redis作为一级缓存外,还可以引入二级缓存(如本地缓存)。当Redis缓存失效时,首先尝试从本地缓存中获取数据;若本地缓存也没有,则再查询数据库。

      5. Redis集群与高可用架构

        为了防止单点故障导致缓存雪崩,可以采用Redis集群方案,实现数据的分布式存储和自动故障转移。此外,配置主从复制和哨兵监控等机制,提高系统的容错能力和稳定性。

  3. 缓存击穿

    1. 定义
      1. 缓存击穿(Cache Breakdown或Cache Penetration)是指一个非常热门的键在缓存中过期时,大量并发请求同时访问这个键,导致这些请求直接穿透缓存层,全部打到后端数据库上。这种情况虽然不像缓存雪崩那样影响广泛,但对特定热点数据的影响却是致命的,可能导致数据库瞬间压力剧增。
    2. 缓存击穿的原因
      1. 热点数据过期:某些键的数据非常热门,但在某一时刻其缓存过期了,此时如果大量的并发请求同时查询该键,则所有请求都会穿透到数据库。
      2. 高并发场景:特别是在高并发的应用环境中,这种现象更容易发生,并且带来的负面影响也更大。
    3. 预防和应对措施
      1. 互斥锁(Mutex Lock)

        当某个热门键过期时,使用分布式锁来确保只有一个线程去重新加载数据到缓存中,其他线程等待该线程完成后再从缓存中读取数据。这可以避免多个线程同时查询数据库,减轻数据库的压力。

      2. 热点数据永不过期

        对于一些特别重要的热点数据,可以设置它们永不过期,并通过后台任务定期更新这些数据。这样即使发生缓存击穿的情况,也不会影响到这些关键数据的可用性。

      3. 异步更新缓存

        当检测到某条数据即将过期时,可以通过异步的方式提前更新缓存中的数据,而不是等到缓存真正过期后再进行更新。这样可以有效减少因缓存过期而导致的瞬时负载增加。

      4. 多级缓存

        除了Redis作为一级缓存外,还可以引入二级缓存(如本地缓存)。当Redis缓存失效时,首先尝试从本地缓存中获取数据;若本地缓存也没有,则再查询数据库。这种方式可以在一定程度上缓解缓存击穿的问题。

三、Redis高级知识

1、源码级解析

2、云原生与运维

3、企业级扩展方案

四、Redis实战

  1. 高性能Redis缓存实战(python版)

    1. 缓存三大问题解决方案

      1. 缓存穿透(恶意请求不存在的数据)
        1. import redis
          import hashlib
          from typing import Optional
          
          class ProductCache:
              def __init__(self):
                  self.redis = redis.Redis(host='localhost', port=6379, db=0)
                  # 使用RedisBloom模块(需提前加载)
                  self.bf_key = "product_bf"
                  
              def get_product(self, product_id: str) -> Optional[dict]:
                  # 先检查布隆过滤器
                  if not self.redis.bf.exists(self.bf_key, product_id):
                      return None
                  
                  # 查询缓存
                  cache_key = f"product:{product_id}"
                  data = self.redis.get(cache_key)
                  if data:
                      if data == b'NULL':  # 空值标识
                          return None
                      return json.loads(data)
                  
                  # 查数据库并回填缓存(此处模拟数据库查询)
                  db_data = self._get_from_db(product_id)
                  if not db_data:
                      # 空值缓存5分钟防止穿透
                      self.redis.setex(cache_key, 300, 'NULL')
                      return None
                      
                  self.redis.setex(cache_key, 3600, json.dumps(db_data))
                  return db_data
          
              def _get_from_db(self, product_id: str) -> Optional[dict]:
                  """模拟数据库查询,返回None表示数据不存在"""
                  # ...数据库查询逻辑...
      2. 缓存击穿(热点Key突然失效)
        1. ​​​​​​12
          1. import time
            from redis.exceptions import LockError
            
            def get_product_with_lock(product_id: str) -> dict:
                cache_key = f"product:{product_id}"
                data = redis.get(cache_key)
                if data:
                    return json.loads(data)
                
                # 尝试获取分布式锁
                lock_key = f"lock:{cache_key}"
                try:
                    # 使用SET命令替代已弃用的SETNX
                    if redis.set(lock_key, "1", nx=True, ex=10):
                        # 双重检查
                        data = redis.get(cache_key)
                        if data:
                            return json.loads(data)
                            
                        # 查询数据库
                        db_data = get_from_db(product_id)
                        redis.setex(cache_key, 3600, json.dumps(db_data))
                        return db_data
                    else:
                        # 等待其他线程加载
                        time.sleep(0.1)
                        return get_product_with_lock(product_id)
                finally:
                    try:
                        redis.delete(lock_key)
                    except LockError:
                        pass

      3. 缓存雪崩(大量Key同时失效)
        1. ​​​​​​​1
          1. ​​​​​​​
            import random
            
            def set_cache_with_random_ttl(key: str, value: str):
                # 基础TTL 1小时 + 随机0-300秒
                ttl = 3600 + random.randint(0, 300)
                redis.setex(key, ttl, value)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

耿雨飞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值