前言
Redis 几乎成了所有后端项目的标配组件,从简单的 KV 存储到分布式缓存,从消息队列到流处理,它的角色越来越多样。但当单机 Redis 无法满足需求时,分布式缓存就登场了。
本文将系统性地梳理分布式缓存的核心问题:Redis Cluster 的架构原理、一致性哈希算法、以及实战中最头疼的缓存穿透、击穿、雪崩三大问题及其解决方案。这些不是面试题,而是我在实际项目中真金白银踩过的坑。
一、单机 Redis 的瓶颈
1.1 为什么需要分布式?
单机 Redis 的瓶颈通常在三个方面:
Redis 几乎成了所有后端项目的标配组件,从简单的 KV 存储到分布式缓存,从消息队列到流处理,它的角色越来越多样。但当单机 Redis 无法满足需求时,分布式缓存就登场了。
本文将系统性地梳理分布式缓存的核心问题:Redis Cluster 的架构原理、一致性哈希算法、以及实战中最头疼的缓存穿透、击穿、雪崩三大问题及其解决方案。这些不是面试题,而是我在实际项目中真金白银踩过的坑。
单机 Redis 的瓶颈通常在三个方面:
在高并发系统中,限流是一种重要的保护机制,用于控制请求流量,防止系统被过多请求冲击导致服务不可用。本文将基于提供的代码实现,详细介绍如何使用Spring AOP和Redis实现一个灵活的分布式限流组件。
该例子利用mysql进行黑名单的自动登记。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RateLimiter {
String key() default "rate_limit:";
int time() default 60;
int count() default 5;
LimitType limitType() default LimitType.DEFAULT;
boolean joinBlackList() default false;
}
BigKey 问题是指在 Redis 中某些键包含的数据量过大,导致这些键的操作(如读取、写入、删除等)消耗过多的内存和CPU资源,进而影响整个Redis实例的性能。解决 BigKey 问题的关键在于识别并优化这些大键,以提高系统的稳定性和响应速度。以下是几种常见的解决方法:
redis-cli 工具MEMORY USAGE 命令:可以用来查看某个键占用的内存量。redis-cli --raw MEMORY USAGE <key>
SCAN 命令:结合 MEMORY USAGE 可以批量扫描并检查多个键的大小。redis-cli --bigkeys
Redis 的字典使用哈希表作为底层实现,一个哈希表里面可以有多个哈希表节点,而每个哈希表节点就保存了字典中的一个键值对。
接下来的三个小节将分别介绍 Redis 的哈希表、哈希表节点、以及字典的实现。
Redis 字典所使用的哈希表由 dict.h/dictht 结构定义:
typedef struct dictht {
// 哈希表数组
dictEntry **table;
// 哈希表大小
unsigned long size;
// 哈希表大小掩码,用于计算索引值
// 总是等于 size - 1
unsigned long sizemask;
// 该哈希表已有节点的数量
unsigned long used;
} dictht;
链表提供了高效的节点重排能力,以及顺序性的节点访问方式,并且可以通过增删节点来灵活地调整链表的长度。
作为一种常用数据结构,链表内置在很多高级的编程语言里面,因为 Redis 使用的 C 语言并没有内置这种数据结构,所以 Redis 构建了自己的链表实现。
链表在 Redis 中的应用非常广泛,比如列表键的底层实现之一就是链表:当一个列表键包含了数量比较多的元素,又或者列表中包含的元素都是比较长的字符串时,Redis 就会使用链表作为列表键的底层实现。
举个例子,以下展示的 integers 列表键包含了从 1 到 1024 共一千零二十四个整数:
每个 sds.h/sdshdr 结构表示一个 SDS 值:
struct sdshdr {
// 记录 buf 数组中已使用字节的数量
// 等于 SDS 所保存字符串的长度
int len;
// 记录 buf 数组中未使用字节的数量
int free;
// 字节数组,用于保存字符串
char buf[];
};
Redis 提供 “主 - 从” 的数据复制:“从” Redis 即作为 “主” Redis 的数据副本。“从” Redis,既能够用于读性能的扩展,亦能够作为数据备份的一种手段。
同时,Redis 支持 Redis Sentinel,实现 “主 - 从” 监控、故障迁移,限于篇幅,本文不予以展开。
合理的 Redis 实例,内存的占有量不应当超过 60%,当内存使用率过高时,应该予以清理及优化。
ziplist 实现了 “紧凑” 的数据结构,通过尽可能减少非数据节点的占用,以提供内存密度。

Redis 的排序操作和其他编程语言的排序操作一样,都可以根据某种比较规则对一系列元素进行有序的排列。负责执行排序操作的SORT命令可以根据字符串、列表、集合、有序集合、散列这5种键里面存储着的数据,对列表、集合以及有序集合进行排序。如果读者之前曾经使用过关系数据库的话,那么可以将SORT命令看作是SQL语言里的order by子句。
SORT source-key [BY pattern] [LIMIT offset count] [GET pattern [GETpattern ...] ] [ASC|DESC] [ALPHA] [STORE dest-key] 根据给定的选项,对输入列表、集合或者有序集合进行排序,然后返回或者存储排序的结果
Redis 的数据持久化,即:将内存中的数据存储到硬盘(本文中亦称之为 “落地”)。Redis 提供了 RDB 和 AOF 两种持久化的方法:
• RDB:基于特定的时间间隔将数据 “全量快照”,生成 RDB 文件并落地
• AOF (Append Only File):将 Redis 接收到命令以 “增量追加” 的方式,写入 AOF 文件
Redis 允许使用任意一种持久化方法,亦允许同时使用或同时不使用。以下将阐述两者涉及的配置选项、命令以及优缺点。