Skip to main content

redis

📅 2026-03-19 ✏️ 2026-04-01 CS INFRA
No related notes

1 · redis#

Redis is a data structure server.

Help you solve problems from caching to queuing to event processing.

1.1 · 数据结构总览

https://redis.io/docs/latest/develop/data-types

Redis 常见数据结构主要有 9 类,其中前 5 个是最核心、最常用的基础结构,后 4 个更偏向解决特定问题的专项结构。

1. 常用核心数据结构

  • String:最基础,字节序列。
  • List:有序String序列(按插入顺序),偏双端操作。
  • Hash:键值对。
  • Set:无序唯一String集合。
  • Sorted Set:带分值的有序唯一String集合。

2. 专项能力型数据结构

  • Stream:可持久化消息流(看作仅追加日志)。
  • Geo:地理坐标与附近检索。
  • Bitmap:对字符串进行按位操作。
  • Bitfields:在字符串值中编码多个计数器。
  • HyperLogLog:近似去重计数。
  • Bloom filter:检查集合中元素的存在与否。
  • Cuckoo filter:同 Bloom,功能和性能之间的权衡略有不同。
  • 其他

1.2 · 特性

Redis 组件能力

  • 高性能内存访问:适合高频读写、低延迟场景。
  • 单线程命令原子性:适合计数、限流、锁、自增 ID。
  • 过期机制:适合缓存、会话、限时锁、临时数据自动清理。
  • 丰富数据结构与原生命令支持:许多常见业务动作都可以直接映射到 Redis 的数据结构和命令。

Redis 数据结构特性

  • 丰富的数据结构:可以直接表达对象、队列、集合关系、排行榜、消息流。
  • String 值存储与原子操作能力:适合缓存、计数器、限流标记、分布式锁、会话等场景。
  • Hash 字段组织能力:适合对象属性、用户信息、配置项、统计字段等结构化记录场景。
  • Set 集合运算能力:适合集合去重、交集、并集、差集、推荐、标签筛选。
  • ZSet 排序与范围查询能力:适合排行榜、时间线、延时任务、区间检索。
  • List 顺序存储与阻塞消费能力:适合简单消息队列和任务分发。
  • Stream 消费组与确认机制:适合更可靠的消息处理和事件流消费。
  • 位级存储与概率统计能力:适合海量状态位、签到、布尔标记、UV 统计等节省内存场景。

数据选型

  • 如果是一个 key 对一个值,先想 String。
  • 如果是一个对象多个字段,先想 Hash。
  • 如果是按顺序进出,先想 List。
  • 如果是去重 + 关系运算,先想 Set。
  • 如果是排名 / 排序 / 分值区间,先想 Sorted Set。
  • 如果是可靠消息队列,先想 Stream。
  • 如果是海量布尔状态,先想 Bitmap。
  • 如果是海量元素的存在性判断去重预过滤,先想 Bloom Filter。
  • 如果是附近的人/地点,先想 GEO。
  • 如果是只要近似去重计数,先想 HyperLogLog。

1.3 · 不同数据结构的使用场景

快速选型

  • 缓存单值、计数、锁:String
  • 存对象字段:Hash
  • 简单双端队列、阻塞消费:List
  • 去重、标签、关系运算:Set
  • 排行榜、时间线、延迟任务:Sorted Set
  • 海量 UV 统计:HyperLogLog
  • 紧凑状态记录、签到:Bitmap
  • 附近的人/门店:GEO
  • 可确认的消息队列:Stream

1.3.1 · 1. String#

最通用的数据结构,本质上是一个二进制安全的值,既能存文本,也能存数字、序列化对象、位图片段。

  • 适合场景:缓存对象、简单 KV、计数器、限流器、分布式锁、ID 生成器、日志追加、文章内容/预览存储。
  • 常用能力:SET/GETINCR/DECRAPPENDGETRANGE/SETRANGE
  • 典型判断:如果一个 key 只需要对应一个值,优先先想 String。
  • 不足:字段级更新不如 Hash 自然;结构化数据全部覆盖写时不够经济。

1.3.2 · 2. Hash#

适合存一个对象的多个字段,本质上是 key -> field -> value

  • 适合场景:用户资料、文章元数据、商品信息、会话信息、配置项、图节点属性、短网址映射。
  • 常用能力:HSET/HGET/HMGET/HGETALL/HINCRBY
  • 典型判断:当数据天然是“一个对象 + 多个属性”时,用 Hash 比多个 String key 更清晰。
  • 优势:字段可单独读写,便于局部更新;对象聚合更自然。
  • 不足:不适合需要保持顺序、做范围查询或做复杂关系运算的场景。

1.3.3 · 3. List#

适合维护有序、可重复的数据序列,偏向两端插入弹出。

  • 适合场景:消息队列、任务队列、操作日志、时间顺序评论流、待办事项、分页读取。
  • 常用能力:LPUSH/RPUSH/LPOP/RPOP/LRANGE/BLPOP/BRPOP
  • 典型判断:如果你只关心“先进先出 / 后进先出 / 按写入顺序消费”,List 很合适。
  • 优势:天然保序,双端操作快,可配合阻塞命令做简单队列。
  • 不足:按值查找、随机访问、大范围中间插入都不理想;需要多消费者确认时通常不如 Stream。

1.3.4 · 4. Set#

适合存无序且唯一的数据集合,重点在“去重”和“集合关系计算”。

  • 适合场景:标签系统、点赞用户集合、投票去重、好友/关注关系、共同关注、抽奖池、商品筛选的倒排索引。
  • 常用能力:SADD/SREM/SISMEMBER/SINTER/SUNION/SDIFF
  • 典型判断:只要业务关键词里出现“去重”“是否存在”“共同/差异/并集”,优先考虑 Set。
  • 优势:集合运算直接由 Redis 提供,写法简单,性能稳定。
  • 不足:无序,不能按分数排序,也不适合范围分页。

1.3.5 · 5. Sorted Set#

在 Set 的唯一性基础上,为每个成员增加一个 score,适合“有序集合”问题。

  • 适合场景:排行榜、热度榜、延迟队列、时间线、按分值范围检索、推荐结果排序、自动补全。
  • 常用能力:ZADD/ZINCRBY/ZRANGE/ZREVRANGE/ZRANGEBYSCORE
  • 典型判断:只要既要“成员唯一”,又要“可排序 / 可排名 / 可按区间取值”,就该想到 ZSet。
  • 优势:同时支持排名、范围查询、按 score 排序,表达力很强。
  • 不足:比普通 Set/String 更重;如果只是简单去重,不必上 ZSet。

1.3.6 · 6. HyperLogLog#

用于近似基数统计(Probabilistic Filtering),核心价值是极小内存下统计“有多少个不同元素”。

  • 适合场景:UV、日活/月活、去重访客数、大规模唯一计数。
  • 常用能力:PFADD/PFCOUNT/PFMERGE
  • 典型判断:只关心“大概有多少不同用户”,不关心“这些用户是谁”,就适合 HyperLogLog。
  • 优势:非常省内存,适合海量去重计数。
  • 不足:结果是近似值,不能取回成员明细,也不能做精确集合运算。

1.3.7 · 7. Bitmap#

本质上是按位存储的紧凑二进制数组,适合状态位和稠密布尔记录。

  • 适合场景:签到、在线状态、活跃记录、用户行为标记、布尔矩阵、紧凑计数器。
  • 常用能力:SETBIT/GETBIT/BITCOUNT/BITOP/BITFIELD
  • 典型判断:当对象可映射为连续编号,且每个对象只需 1 bit 或少量 bit 状态时,Bitmap 很划算。
  • 优势:极致节省空间,按位统计和批量位运算很强。
  • 不足:对业务建模要求高;编号稀疏时浪费空间;可读性较差。

1.3.8 · 8. GEO#

底层基于有序集合封装的地理位置能力,用于经纬度存储与附近检索。

  • 适合场景:附近的人、门店搜索、配送范围、位置打卡、距离计算。
  • 常用能力:GEOADD/GEODIST/GEORADIUS/GEORADIUSBYMEMBER/GEOHASH
  • 典型判断:只要需求是“基于经纬度找附近”,直接用 GEO,而不是自己维护坐标计算。
  • 优势:内建距离计算和范围查找,开发成本低。
  • 不足:适合中轻量位置检索;复杂 GIS 分析仍应交给专业地理系统。

1.3.9 · 9. Stream#

适合可持久化的消息流,支持消费组、消息确认、待处理队列。

  • 适合场景:消息队列、事件总线、异步任务分发、日志采集、订单事件流。
  • 常用能力:XADD/XREAD/XGROUP/XREADGROUP/XACK/XPENDING
  • 典型判断:当你需要“消息可回放、多消费者组、消费确认、未处理追踪”时,用 Stream。
  • 优势:比 List/PubSub 更适合可靠消息处理。
  • 不足:模型和运维复杂度更高;如果只是即时广播,Pub/Sub 更轻。

1.4 · 可编程性

https://redis.io/docs/latest/develop/programmability/

Redis 可编程性(Programmability)= 在服务器端执行用户自定义逻辑的能力。

核心价值:

  • 数据局部性:逻辑在数据所在处执行,减少网络往返和传输开销。
  • 原子性:脚本执行期间阻塞所有其他客户端,效果要么全部生效、要么全部未发生,等同于事务语义。
  • 组合能力:将多个命令 + 条件逻辑封装为一次调用,跨多个 key、多种数据结构原子操作。

Redis 提供两种可编程方式:Eval Scripts(Redis 2.6+)和 Redis Functions(Redis 7.0+,推荐)。

1.4.1 · Eval Scripts(Lua 脚本)#

脚本被视为客户端应用的一部分,服务端仅做缓存,不持久化。

基本用法EVAL <script> <numkeys> [key ...] [arg ...]

> EVAL "return redis.call('SET', KEYS[1], ARGV[1])" 1 foo bar
OK
  • KEYS 表:所有 key 名参数(必须显式传入,Cluster 路由依赖于此)。
  • ARGV 表:所有非 key 的普通参数。
  • redis.call():执行 Redis 命令,出错直接返回错误给客户端。
  • redis.pcall():执行 Redis 命令,出错返回错误对象给脚本上下文,可由脚本自行处理。

脚本缓存与 EVALSHA

  • 每个 EVAL 执行的脚本会按 SHA1 摘要缓存在服务端。
  • SCRIPT LOAD <script> → 返回 SHA1 摘要,之后用 EVALSHA <sha1> <numkeys> ... 调用,节省网络带宽。
  • 缓存是易失的:服务器重启、故障转移、SCRIPT FLUSH 都会清空。应用需自行处理 NOSCRIPT 错误并重新加载。
  • 反模式:动态拼接脚本内容 → 缓存膨胀;应始终参数化脚本,通过 KEYS/ARGV 传参。

SCRIPT 命令族SCRIPT LOAD / SCRIPT EXISTS / SCRIPT FLUSH / SCRIPT KILL

1.4.2 · Redis Functions(推荐,7.0+)#

函数是数据库的一等公民,随数据一起持久化(AOF)和复制(主从),不需要应用在运行时加载。

Eval Scripts 的痛点(Functions 解决的问题):

  • 所有客户端实例必须维护脚本副本,同步更新困难。
  • 缓存随时可能丢失,事务中调用缓存脚本容易失败。
  • SHA1 摘要无语义,调试困难(如 MONITOR 中看不出含义)。
  • 脚本之间不能互相调用,无法复用代码。

核心概念

  • Library(库):函数的容器,一个库包含一个或多个函数,整体加载/替换/删除。
  • Function(函数):库中注册的命名入口点,通过 redis.register_function() 注册。
  • 库的 Shebang 格式:#!lua name=<library_name>

加载与调用

-- 加载库(从文件)
$ cat mylib.lua | redis-cli -x FUNCTION LOAD REPLACE

-- 调用函数
> FCALL my_hset 1 myhash field1 "value1"
  • FUNCTION LOAD <code>:加载库,返回库名。加 REPLACE 可覆盖已有同名库。
  • FCALL <function> <numkeys> [key ...] [arg ...]:调用函数。
  • FCALL_RO:调用只读函数(可在只读副本上执行)。
  • FUNCTION LIST / FUNCTION DELETE / FUNCTION DUMP / FUNCTION RESTORE:管理函数。

代码复用:同一 library 内的函数可以调用库内部的公共辅助函数(普通 Lua 函数,不需注册)。

Function Flags:注册时声明函数行为,告知 Redis 如何执行策略控制。

redis.register_function{
  function_name = 'my_hgetall',
  callback = my_hgetall,
  flags = { 'no-writes' }  -- 声明为只读
}
  • no-writes:只读函数,允许通过 FCALL_RO 在副本上执行。
  • 默认 Redis 假设函数可读可写,会拒绝在只读副本上运行。

集群部署:Redis 不会自动将 Functions 同步到所有集群节点,需管理员手动加载:

$ redis-cli --cluster-only-masters --cluster call host:port FUNCTION LOAD ...

1.4.3 · Eval Scripts vs Functions 选型#

维度Eval ScriptsRedis Functions
版本2.6+7.0+
定位应用的一部分数据库的扩展
持久化不持久化,缓存易失随 AOF 持久化,主从复制
标识SHA1 摘要(无语义)用户定义的函数名
代码复用脚本间不能互调同库内函数可共享代码
部署应用负责加载管理员预加载,应用直接调用
适用场景简单、临时、轻量脚本正式的服务端逻辑封装

1.4.4 · 共性约束

  • 原子执行:脚本/函数执行期间阻塞所有客户端,不能被其他命令穿插。
  • 沙箱环境:不能访问文件系统、网络或其他系统调用,只能操作 Redis 数据。
  • 最大执行时间:默认 5 秒(busy-reply-threshold 可配)。超时后不会自动终止(会破坏原子性),而是开始对其他客户端返回 BUSY 错误,可通过 SCRIPT KILL(只读脚本)或 SHUTDOWN NOSAVE(已写入数据的脚本)中断。
  • 只读脚本:可通过 no-writes flag 或 EVAL_RO/EVALSHA_RO/FCALL_RO 执行,可在副本运行、可被 SCRIPT KILL、不受 OOM 限制、不受写暂停阻塞。