菜单

Redis

相关源文件

本文档全面概述了 Redis,一个流行的内存数据结构存储,可用作数据库、缓存、消息代理和流引擎。我们将探讨 Redis 的数据结构、持久化机制、缓存策略和常见用法。有关 MySQL 等其他数据库技术的信息,请参阅 MySQL

1. 什么是 Redis?

Redis(Remote Dictionary Server)是一个开源的内存数据结构存储,可用作数据库、缓存、消息代理和流引擎。与传统数据库不同,Redis 将数据存储在内存中,提供极快的读写操作。

Redis 用 C 语言编写,没有外部依赖,最适合在 Linux 系统上运行。它支持各种数据结构和功能,使其适用于广泛的用例。

来源:docs/database/redis/redis-questions-01.md:19-27

1.1 为什么 Redis 速度如此之快?

Redis 通过多项优化实现了卓越的性能

  1. 内存存储:所有数据操作都在内存中进行,访问速度以纳秒计,而磁盘数据库的操作以毫秒计。
  2. 高效的 I/O 模型:Redis 使用单线程事件循环和 I/O 多路复用,允许单个线程处理多个连接,而无需上下文切换的开销。
  3. 优化的数据结构:Redis 数据结构针对性能和空间效率进行了高度优化。
  4. 简单的协议:Redis 使用轻量级的 RESP(Redis Serialization Protocol)进行客户端-服务器通信。

来源:docs/database/redis/redis-questions-01.md:35-46

1.2 Redis 与其他解决方案的比较

Redis 相较于其他数据存储解决方案具有优势

  • 与 Memcached 相比:Redis 提供更丰富的数据结构、数据持久化、原生集群和脚本功能,而 Memcached 更简单,仅支持键值存储。
  • 与传统数据库相比:Redis 提供更快的读写操作,但代价是主要将数据存储在内存中。
  • 新兴替代方案:Dragonfly 和 KeyDB 等项目提供了与 Redis 兼容的 API,并具有额外的性能改进,但 Redis 仍然是最成熟和广泛采用的解决方案。

来源:docs/database/redis/redis-questions-01.md:61-74

2. Redis 数据结构

Redis 支持多种数据结构,使其适用于不同的用例。理解这些结构是有效使用 Redis 的关键。

2.1 基本数据类型

Redis 提供五种基本数据类型

2.1.1 String(字符串)

字符串是 Redis 最简单的数据类型——二进制安全的序列,可以存储文本、整数、浮点数或二进制数据(如图像)。

实现:Redis 使用 Simple Dynamic String (SDS) 实现字符串,它支持

  • O(1) 的长度获取
  • 与 C 字符串不同的二进制安全性
  • 防止缓冲区溢出
  • 通过预分配减少内存重新分配

常用命令:

  • SET key value
  • GET key
  • MSET key1 value1 key2 value2...
  • INCR key

用例:

  • 缓存简单值
  • 计数器
  • 存储序列化对象
  • 分布式锁实现

来源:docs/database/redis/redis-data-structures-01.md:15-132

2.1.2 List(列表)

列表是字符串值的链表,允许在列表的两端进行操作。

实现:最初实现为链表,现在使用 QuickList(链表和压缩列表的组合)以提高内存效率。

常用命令:

  • LPUSH/RPUSH key value [value ...]
  • LPOP/RPOP key
  • LRANGE key start stop

用例:

  • 任务队列
  • 近期动态(如时间线功能)
  • 消息队列

来源:docs/database/redis/redis-data-structures-01.md:135-201

2.1.3 Hash(哈希表)

哈希表将字段-值对映射起来,非常适合表示对象。

常用命令:

  • HSET key field value
  • HGET key field
  • HMSET key field1 value1 field2 value2...
  • HGETALL key

用例:

  • 存储具有多个字段的对象
  • 用户资料
  • 跟踪动态统计信息
  • 购物车

来源:docs/database/redis/redis-data-structures-01.md:203-262

2.1.4 Set(集合)

集合是无序的、不重复的字符串集合。

常用命令:

  • SADD key member [member ...]
  • SMEMBERS key
  • SINTER key1 key2
  • SCARD key

用例:

  • 跟踪唯一项
  • 标签系统
  • 存储唯一关系
  • 随机项选择

来源:docs/database/redis/redis-data-structures-01.md:264-329

2.1.5 Sorted Set(有序集合)

有序集合将每个成员与一个分数关联起来,允许有序操作。

实现:内部使用跳表和哈希表,提供 O(log N) 的操作。

常用命令:

  • ZADD key score member
  • ZRANGE key start stop
  • ZREVRANK key member

用例:

  • 排行榜
  • 优先队列
  • 基于时间的访问数据

来源:docs/database/redis/redis-data-structures-01.md:331-397

2.2 特殊数据类型

除了基本数据类型,Redis 还为特定用例提供了专门的结构

2.2.1 Bitmap(位图)

位图不是独立的数据类型,而是字符串上的位操作,其中每个位可以表示状态信息。

常用命令:

  • SETBIT key offset value
  • GETBIT key offset
  • BITCOUNT key [start end]

用例:

  • 用户活动跟踪(日活跃用户)
  • 功能标志
  • 高效的布尔数据存储

来源:docs/database/redis/redis-data-structures-02.md:17-66

2.2.2 HyperLogLog

HyperLogLog 提供概率性基数估算,内存使用量极少(只需 12KB 即可计算数百万项,标准误差为 0.81%)。

常用命令:

  • PFADD key element [element ...]
  • PFCOUNT key [key ...]

用例:

  • 计算独立访客数
  • 计算唯一搜索词
  • 跟踪用于分析的唯一指标

来源:docs/database/redis/redis-data-structures-02.md:69-111

2.2.3 Geospatial(地理空间)

地理空间索引允许存储和查询基于位置的数据。

常用命令:

  • GEOADD key longitude latitude member
  • GEODIST key member1 member2 [unit]
  • GEORADIUS key longitude latitude radius unit

用例:

  • 查找附近地点
  • 基于位置的服务
  • 地理围栏应用

来源:docs/database/redis/redis-data-structures-02.md:113-169

3. Redis 持久化

Redis 提供不同的持久化选项以确保数据持久性

3.1 RDB 持久化(快照)

RDB 在指定的时间间隔创建数据集的快照。

主要特点:

  • 创建紧凑的二进制文件
  • 适用于备份
  • 比 AOF 从磁盘重启更快
  • 比 AOF 的持久性差(可能丢失最近的数据)

配置:

  • save <seconds> <changes> - 当更改超过阈值时触发快照
  • stop-writes-on-bgsave-error - 快照失败时停止接受写入
  • rdbcompression - 启用/禁用 RDB 文件的压缩

来源:docs/database/redis/redis-questions-02.md:159-171

3.2 AOF 持久化(Append-Only File)

AOF 将每个写入操作记录到文件中,该文件可以重放以重建数据集。

主要特点:

  • 比 RDB 持久性更好
  • 当文件过大时在后台自动重写
  • 重启速度比 RDB 慢
  • 文件大小比 RDB 大

同步策略:

  • appendfsync always - 每次命令后同步(最持久,最慢)
  • appendfsync everysec - 每秒同步一次(平衡性好)
  • appendfsync no - 由操作系统决定何时同步(最快,持久性最差)

来源:docs/database/redis/redis-questions-02.md:159-176

3.3 混合持久化(Redis 4.0+)

Redis 4.0 引入了一种混合方法,结合了 RDB 和 AOF。

主要特点:

  • 以 RDB 快照开始,以实现更快的加载
  • 快照后追加 AOF 操作
  • 结合了两种方法的优点

来源:docs/database/redis/redis-questions-02.md:159-171

4. Redis 缓存策略

Redis 经常被用作缓存层。理解有效的缓存策略至关重要。

4.1 常见缓存模式

4.1.1 Cache-Aside(缓存旁路/按需加载)

应用程序首先检查缓存。如果缓存未命中,则从数据库加载数据并将其存储在缓存中。

实现:

  1. 检查缓存中的数据
  2. 如果找到,则返回数据
  3. 如果未找到,则查询数据库
  4. 将结果存储在缓存中
  5. 返回数据

优点:

  • 仅缓存实际请求的数据
  • 对缓存故障具有弹性

缺点:

  • 初始请求会导致缓存未命中和响应变慢
  • 如果未进行适当的无效化,可能会发生数据不一致

来源:docs/database/redis/redis-questions-01.md:127-131

4.2 Redis 驱逐策略

Redis 提供了多种驱逐策略来处理内存限制

  • noeviction:达到内存限制时返回错误(默认)
  • allkeys-lru:首先驱逐最近最少使用(LRU)的键
  • allkeys-random:驱逐随机键
  • volatile-lru:首先驱逐设置了过期时间的最近最少使用(LRU)的键
  • volatile-random:驱逐设置了过期时间的随机键
  • volatile-ttl:首先驱逐剩余生存时间(TTL)最短的键
  • allkeys-lfu:驱逐最少使用(LFU)的键(Redis 4.0+)
  • volatile-lfu:驱逐设置了过期时间的、最少使用(LFU)的键(Redis 4.0+)

驱逐策略通过 maxmemory-policy 配置参数设置。

来源:docs/database/redis/redis-questions-01.md:803-832

5. Redis 常见问题及解决方案

5.1 缓存问题

5.1.1 缓存穿透

问题:对不存在数据的请求绕过缓存和数据库,导致数据库负载增加。

解决方案:

  • 缓存空结果并设置短暂过期时间
  • 使用布隆过滤器拒绝无效请求
  • 实现请求校验和速率限制

来源:docs/database/redis/redis-questions-02.md:605-667

5.1.2 缓存击穿(缓存失效风暴)

问题:热点数据过期,导致数据库请求激增。

解决方案:

  • 实现互斥锁以防止多次数据库请求
  • 在过期前预热/重新加载缓存
  • 为热点键延长过期时间

来源:docs/database/redis/redis-questions-02.md:670-691

5.1.3 缓存雪崩

问题:大量缓存条目同时过期,导致数据库过载。

解决方案:

  • 为过期时间添加随机变差
  • 实现熔断器模式
  • 使用 Redis Cluster 以获得更高的可用性
  • 实现本地缓存作为回退

来源:docs/database/redis/redis-questions-02.md:693-732

5.2 Redis 性能优化

5.2.1 批量操作

使用批量操作可显著降低网络开销

  • 原生命令:使用多键命令,如 MGETMSETHMGET 等。
  • 管道(Pipeline):将多个命令分组以减少往返时间(RTT)
  • Lua 脚本:在服务器端原子地执行多个操作

来源:docs/database/redis/redis-questions-02.md:196-256

5.2.2 大键管理

大键可能导致性能问题

  • 检测:使用 redis-cli --bigkeysMEMORY USAGE 命令
  • 预防:将大值分割到多个键中
  • 处理:使用 UNLINK 代替 DEL 进行异步删除
  • 数据结构选择:选择合适的数据结构(例如,哈希表用于对象)

来源:docs/database/redis/redis-questions-02.md:281-386

5.2.3 热点键处理

热点键(频繁访问的键)可能成为瓶颈

  • 检测:使用 redis-cli --hotkeys 或监控工具
  • 解决方案:
    • 为读密集型键实现读副本
    • 添加应用程序级别的缓存
    • 将热点键分片到多个 Redis 实例

来源:docs/database/redis/redis-questions-02.md:388-493

6. Redis 的更多用途(超越缓存)

Redis 的用途远不止简单的缓存

6.1 Redis 作为消息代理

Redis 可用作消息队列或发布/订阅系统

  • 列表 可通过 LPUSH/RPOP 实现简单的队列
  • 发布/订阅(Pub/Sub)允许将消息广播给多个订阅者
  • Stream(Redis 5.0+)提供更强大的消息传递功能,支持消费者组

来源:docs/database/redis/redis-questions-01.md:166-262

6.2 Redis 用于分布式锁

Redis 可用于实现分布式锁,以协调跨服务的访问

  • 使用带过期的 SETNX 进行基本实现
  • 使用 Redisson 或 Lua 脚本进行更健壮的实现
  • 考虑可靠性和故障转移场景

来源:docs/database/redis/redis-questions-01.md:162-165

6.3 使用 Redis 进行速率限制

Redis 可有效地实现速率限制

  • 使用带过期的计数器
  • 实现滑动窗口算法
  • 使用 Lua 脚本实现令牌桶

来源:docs/database/redis/redis-questions-01.md:155-156

6.4 使用 Redis 进行搜索

Redis 通过模块提供搜索功能

  • RediSearch:全文搜索引擎
  • RedisJSON:JSON 文档存储和查询
  • 与 Elasticsearch 等专用搜索引擎的比较

来源:docs/database/redis/redis-questions-01.md:264-286

7. Redis 架构

7.1 Redis 线程模型

Redis 已从单线程模型演变为混合线程模型

  • 6.0 之前:所有命令处理都是单线程的
  • Redis 4.0+:增加了后台线程来处理 UNLINKFLUSHALL ASYNC 等操作
  • Redis 6.0+:增加了多线程 I/O 来处理网络连接

尽管有这些变化,Redis 仍在主线程中顺序处理命令,避免了复杂的锁机制。

来源:docs/database/redis/redis-questions-01.md:553-636

7.2 Redis 内存管理

Redis 实现了复杂的内存管理技术

  • Jemalloc:为 Redis 优化的内存分配器
  • 驱逐策略:内存限制达到时自动移除键
  • 延迟释放(Lazy freeing):异步内存去分配
  • maxmemory:可配置的内存限制
  • 内存优化:小整数和共享对象的特殊编码

来源:docs/database/redis/redis-questions-01.md:671-858

8. Redis 高可用和扩展

8.1 Redis Sentinel

Redis Sentinel 通过以下方式提供高可用性:

  • 监控 Redis 实例
  • 主节点宕机时自动故障转移
  • 通知客户端配置更改
  • 充当配置提供者

来源:docs/database/redis/redis-questions-02.md:753-756

8.2 Redis Cluster

Redis Cluster 支持水平扩展和分片

  • 数据自动分片到节点
  • 在部分节点不可用时继续运行
  • 使用哈希槽(16384 个)进行数据分发
  • 支持无需停机的数据重分片操作

来源:docs/database/redis/redis-questions-02.md:764-772

总结

Redis 是一款多功能的内存数据结构存储,在缓存方面表现出色,但通过其丰富的数据结构和功能提供了更多用途。了解其内部机制、数据结构和常见用法模式,可以使开发人员能够有效地利用 Redis 来实现各种超越简单键值存储的应用。