Redis

发布于:2025-08-31 ⋅ 阅读:(61) ⋅ 点赞:(0)

Redis

一、基础篇

1. Redis核心定义与特性

  • 定义:基于键值对的NoSQL数据库,数据存储于内存,支持持久化,响应速度达微秒级。
  • 与MySQL区别:Redis是非关系型(键值对,内存存储),MySQL是关系型(行列结构,磁盘存储);实际开发中常搭配使用,Redis作为缓存减轻MySQL压力。
  • 部署方式
    • 单机版:本地解压运行redis-server,或Docker部署(docker run -d --name redis -p 6379:6379 redis:7.0-alpine)。
    • 哨兵模式:一主两从+3个哨兵节点,配置主从地址、故障判定时间(如sentinel down-after-milliseconds mymaster 5000)。
    • 集群模式(Redis Cluster):3主3从起步,自动分片,通过redis-cli --cluster create创建。

2. 数据类型

(1)基础数据类型
类型 底层结构 核心特性 应用场景
字符串String SDS(简单动态字符串) 存储文本/数字/二进制,最大512MB;O(1)获取长度,二进制安全,自动扩容避免缓冲区溢出。 缓存对象、计数器(INCR)、分布式锁(SET NX)、共享Session。
列表List Redis 3.2前:压缩列表+双向链表;3.2后:quicklist(双向链表+压缩列表);7.0后:listpack替代压缩列表。 有序,支持首尾增删(LPUSH/RPOP),适合FIFO场景,查询中间元素慢(O(N))。 消息队列(简单版)、任务列表、最新消息排行。
哈希Hash 压缩列表(元素少+小)/哈希表(元素多+大) 键值对集合,适合存储对象,支持单个字段增删改查(HSET/HGET)。 缓存用户信息、商品属性、购物车。
集合Set 整数集合(整数元素+少)/哈希表 无序无重复,支持交并差集(SINTER/SUNION/SDIFF),查询效率O(1)。 点赞/取消点赞、共同关注、标签去重、抽奖。
ZSet(有序集合) 压缩列表(元素<128+值<64B)/跳表+哈希表 按score排序,支持范围查询(ZRANGE/ZREVRANGE),查询/插入O(logN)。 排行榜(点赞/访问量排行)、优先级队列、电话姓名排序。
(2)扩展数据类型
  • Bitmap:二进制位存储,1亿用户签到仅需12MB,用于状态标记(签到、活跃)。
  • HyperLogLog:概率性基数统计,12KB内存统计海量数据去重个数,误差率0.81%,用于UV统计。
  • GEO:存储地理坐标,支持距离计算、范围查询,底层基于ZSet+Geohash编码,用于“附近的人”“商家定位”。

3. 高性能原因

  • 内存存储:数据全在内存,读写速度远快于磁盘(内存读写≈100ns,磁盘≈1ms)。
  • IO多路复用:单线程通过(IO多路复用程序)epoll(Linux)/kqueue(macOS)监听多客户端连接,避免多线程上下文切换开销,高效处理并发请求。
  • 单线程模型(6.0前):无锁竞争,命令执行原子性;6.0后引入多线程,仅处理网络IO(读写/解析请求),命令执行仍单线程,充分利用多核CPU。
  • 优化数据结构:如String用SDS(动态字符串,预分配空间减少内存碎片)、ZSet用跳表(高效范围查询)。

4. 常用命令

  • 通用命令:EXPIRE(设置过期)、DEL(删除键)、KEYS(模糊匹配键)。
  • String关键命令SET key value [EX seconds] [NX](支持过期、条件写入,如NX仅键不存在时设置,用于分布式锁);INCR(原子自增,用于计数器)。
  • ZSet核心命令:ZADD(添加带分数成员)、ZRANGE(按索引范围查询)、ZREVRANGE(按分数降序查询)。

二、持久化篇

1. 两种持久化方式

(1)RDB(快照持久化)
  • 原理:指定时间间隔内生成内存数据二进制快照(.rdb文件),通过save(阻塞主进程)或bgsave(fork子进程,主进程不阻塞)触发。
  • 自动触发场景:配置save 900 1(15分钟1个键变更)、主从复制首次连接、关闭Redis(未开启AOF时)。
  • 优缺点:恢复快、文件小;但可能丢失两次快照间数据。
(2)AOF(命令日志持久化)
  • 原理:记录所有写命令到AOF文件,重启时重放命令恢复数据;写命令先存AOF缓冲区,再按刷盘策略同步到磁盘。
  • 刷盘策略
    • always:每次命令后立即刷盘,数据无丢失,性能差。
    • everysec:每秒刷盘,最多丢1秒数据,性能均衡(默认)。
    • no:依赖OS刷盘,性能好,数据丢失风险高。
  • 重写机制:解决AOF文件膨胀,通过BGREWRITEAOF(手动)或配置auto-aof-rewrite-percentage 100(文件增长100%且≥64MB时自动重写),剔除冗余命令(如多次SET同一键合并为1次)。

2. 混合持久化(Redis 4.0+)

  • 原理:AOF重写时,先以RDB格式存储内存快照,再追加重写期间的AOF命令,兼顾RDB恢复速度和AOF数据完整性。
  • 配置aof-use-rdb-preamble yes;恢复时先加载RDB,再重放AOF命令。

3. 数据恢复

  • 启动优先级:优先加载AOF文件,无AOF则加载RDB;AOF损坏可通过redis-check-aof --repair修复,RDB仅支持完整性检查(redis-check-rdb)。

三、高可用篇

1. 主从复制

(1)核心概念
  • 架构:主节点处理写请求,从节点同步主节点数据并处理读请求,实现读写分离;支持一主多从、树状主从(跨地域部署,减少主节点负载)。
  • 同步过程
    1. 建立连接:从节点执行replicaof 主节点IP 端口,发送psync命令请求同步。
    2. 数据同步:首次连接触发全量同步(主节点生成RDB、发送RDB+缓存命令,从节点清空数据加载RDB);后续增量同步(主节点将写命令存入复制积压缓冲区,从节点断连重连后按需同步)。
(2)问题与解决
  • 数据不一致:异步复制导致,优化方案:部署同区域节点、增大复制积压缓冲区(repl-backlog-size 1mb)、监控主从偏移量差值。
  • 脑裂问题:主节点与哨兵/从节点断连但仍接客户端写请求,配置min-slaves-to-write 1(主节点需至少1个从节点在线才接受写请求)、min-slaves-max-lag 10(从节点最大延迟10秒)避免数据丢失。

2. 哨兵机制(Sentinel)

  • 核心功能:监控主从节点、自动故障转移、通知客户端。
  • 工作流程
    1. 定时监控:每秒发送PING检测节点存活,超时标记“主观下线”。
    2. 客观下线:哨兵间交互,超过quorum(如2个)哨兵认为主节点下线,标记“客观下线”。
    3. 领导者选举:通过Raft算法,候选哨兵请求投票,获半数以上选票成为领导者。
    4. 故障转移:领导者挑选新主节点→向新主节点发SLAVEOF NO ONE→其他从节点指向新主节点→通知客户端。

3. Redis Cluster(集群)

  • 数据分区:将16384个槽位(slot)分配给主节点,键通过CRC16(key) % 16384映射到对应槽位,实现数据分片。
  • 高可用:每个主节点配从节点,主节点故障时从节点自动升为主节点;支持动态伸缩(添加/删除节点,重新分配槽位)。

四、缓存设计篇

1. 缓存问题与解决方案

缓存三大核心问题

问题类型 定义 发生场景 解决方案
缓存击穿 热点Key(高并发访问的Key)过期失效,大量请求瞬间直达数据库,导致DB压力骤增甚至宕机 1. 电商大促期间爆款商品详情Key过期;2. 热点新闻话题的缓存Key过期 1. 互斥锁机制:请求未命中缓存时,通过Redis的SET key value NX EX获取互斥锁,仅获锁线程查询DB并回写缓存,其他线程等待重试(如用Redisson的RLock);
2. 热点Key永不过期:对核心热点Key不设置过期时间,通过后台定时任务更新缓存数据,避免过期失效;
3. 预热+主动更新:提前将热点数据加载到缓存(缓存预热),并在数据变更时主动更新缓存,而非依赖过期淘汰;
4. 备用缓存:为热点Key设置两个缓存,主缓存设正常过期时间,备用缓存设较长过期时间,主缓存失效时切换到备用缓存
缓存穿透 客户端请求的Key在缓存和数据库中均不存在,请求直接穿透缓存冲击数据库,若恶意伪造大量不存在的Key,会导致DB过载 1. 黑客伪造不存在的用户ID、商品ID发起请求;2. 业务逻辑设计缺陷导致查询不存在的数据 1. 布隆过滤器:在缓存前加布隆过滤器,将DB中所有存在的Key哈希到过滤器中,请求先经过过滤器,不存在的Key直接拦截(误判率可通过哈希函数数量和位数组大小调整,如12KB内存支持百万级Key,误判率0.81%);
2. 缓存空值:对不存在的Key,在缓存中存储空值(如null)并设置较短过期时间(如5分钟),避免同一Key重复穿透;
3. 接口参数校验:在业务层对请求参数进行合法性校验(如用户ID格式、商品ID范围),直接拦截非法请求;
4. IP限流+黑名单:对高频请求不存在Key的IP进行限流,或加入黑名单,防止恶意攻击
缓存雪崩 大量缓存Key在同一时间段内集中过期,或缓存集群整体故障(如断电、网络中断),导致所有请求瞬间涌向数据库,引发DB雪崩 1. 缓存服务重启,所有Key同时失效;2. 批量设置Key时使用相同过期时间(如大促前批量预热缓存,均设24小时过期);3. 缓存集群宕机 1. 过期时间随机化:为Key设置过期时间时添加随机值(如基础过期时间±5分钟),避免大量Key同时过期;
2. 缓存集群高可用:部署Redis哨兵或Cluster集群,主节点故障时从节点自动切换,避免单点失效;
3. 多级缓存架构:引入本地缓存(如Caffeine、Guava Cache),当分布式缓存失效时,先从本地缓存获取数据,减少DB冲击;
4. 熔断降级机制:使用Sentinel、Hystrix等组件,当DB压力超过阈值时,触发熔断,返回默认数据(如“服务繁忙,请稍后再试”),保护DB;
5. 持久化保障:开启RDB+AOF混合持久化,缓存集群故障重启后快速恢复数据,缩短缓存不可用时间

热Key、大Key、无底洞

问题类型 定义 解决方案
热Key 短时间内被高频访问的Key(如每秒上万次请求),导致存储该Key的Redis节点CPU、网络占用飙升,成为系统瓶颈 1. Key分片:将热Key拆分为多个子Key(如“商品:1001”拆为“商品:1001_01”“商品:1001_02”),分散到不同Redis节点;
2. 双层缓存:本地缓存(如应用内存)+分布式缓存,优先从本地缓存获取,减少分布式缓存访问压力;
3. 独立节点存储:将热Key迁移到专门的Redis节点,避免影响其他业务Key;
4. 预计算+异步更新:对热Key对应的高频查询结果预计算,通过异步任务定时更新缓存,减少实时计算压力
大Key 单个Key占用内存过大(通常指超过100MB,如存储大量元素的Hash、List),导致Redis内存占用过高、网络传输延迟大、删除时阻塞主进程 1. Key拆分
- Hash类型:按字段拆分(如“user:1001”拆为“user:1001:info”“user:1001:orders”);
- List类型:按时间/页数拆分(如“log:202405”拆为“log:20240501”“log:20240502”);
2. 数据压缩:对存储的Value进行压缩(如用Gzip、Snappy),减少内存占用;
3. 异步删除:使用Redis的UNLINK命令(替代DEL),异步删除大Key,避免阻塞主进程;
4. 存储优化:避免用String存储大二进制数据(如图片),改用OSS存储,Redis仅存文件URL
缓存无底洞 随着客户端并发量增加,缓存节点的连接数、网络IO激增,即使增加缓存节点,性能提升也不明显,出现“越扩越慢”的现象 1. 减少缓存访问次数
- 合并请求(如批量查询用户信息时,用MGET替代多次GET);
- 结果缓存(将多步查询的合并结果缓存,避免多次访问缓存);
2. 客户端优化
- 使用连接池(如Jedis Pool、Redisson),减少TCP连接建立/关闭开销;
- 开启Pipeline,批量发送命令,减少网络往返次数;
3. 缓存架构优化
- 引入代理层(如Twemproxy、Codis),统一管理缓存节点,减少客户端与节点的直接连接;
- 按业务模块拆分缓存集群,避免单一集群承载所有业务

2. 缓存一致性

  • 缓存与数据库一致性:更新策略(先更DB再删缓存,避免缓存脏读)、定时任务校验、Canal监听DB binlog同步缓存。
  • 本地缓存与分布式缓存一致性:本地缓存设置短过期时间、分布式缓存更新时主动刷新本地缓存、避免本地缓存存储易变数据。

3. 缓存预热

  • 定义:系统启动前加载热点数据到缓存,避免启动后大量请求直达DB。
  • 实现方式:写脚本批量加载、预热程序读取DB热点数据写入缓存、利用Redis持久化文件快速加载。

五、运维篇

1. 内存管理

  • 内存不足处理:增大Redis内存(maxmemory 4gb)、启用内存淘汰策略、删除大Key/过期Key。
  • 过期策略
    • 惰性删除:访问Key时检查过期,不消耗CPU,可能浪费内存。
    • 定期删除:每隔一段时间扫描部分过期Key,平衡CPU和内存。
  • 内存淘汰策略
    • allkeys-lru:淘汰所有Key中最近最少使用的(默认)。
    • volatile-lru:仅淘汰设置过期时间的Key中最近最少使用的。
    • allkeys-lfu/volatile-lfu:基于最近最少频率使用淘汰。

2. 阻塞问题排查

  • 原因:大Key删除、持久化(如save阻塞)、网络IO瓶颈。
  • 解决:用redis-cli info stats查看阻塞统计、避免KEYS(用SCAN替代)、异步删除大Key(UNLINK命令)、优化持久化策略(如用bgsave替代save)。

六、应用篇

1. 分布式锁

  • 实现方式SET lock:key random_value EX 10 NX(原子性获取锁),释放时用Lua脚本校验value(避免误删他人锁)。
  • 优化:锁自动续期(如Redisson的watch dog机制)、避免死锁(设置过期时间)。

2. 消息队列

  • 异步队列:用List实现,LPUSH生产消息,BRPOP消费消息(阻塞避免轮询)。
  • 延时队列:用ZSet实现,消息作为成员,过期时间作为score,定时ZRANGEBYSCORE获取到期消息。

3. 限流

  • 令牌桶算法:用Lua脚本实现,如每秒生成2个令牌,桶容量10,请求时消耗1个令牌,无令牌则限流。

七、底层结构篇

1. 核心底层结构

数据类型 底层结构 特点
String SDS(简单动态字符串) 记录长度,预分配空间,避免内存碎片。
List quicklist(双向链表+压缩列表) 平衡内存和性能,小数据用压缩列表,大数据拆分为多个quicklist节点。
Hash 哈希表+压缩列表 元素少且小时用压缩列表,否则用哈希表。
Set 整数集合+哈希表 元素为整数且少时用整数集合,否则用哈希表。
ZSet 跳表+哈希表 跳表支持高效范围查询,哈希表快速查找成员分数。

2. 关键结构详解

  • 跳表:多层索引结构,平均查找时间O(log n),支持快速插入、删除、范围查询,是ZSet的核心结构。
  • 压缩列表(ziplist):连续内存存储,节省空间;Redis 7.0用listpack替代,解决ziplist的“连锁更新”问题(每个节点记录自身长度,而非前一节点长度)。
  • 哈希表:链式哈希解决冲突,负载因子超过1时扩容,采用渐进式rehash避免阻塞。

网站公告

今日签到

点亮在社区的每一天
去签到