Redis 是一种内存数据库,以具名键存储对应的值,值可以是字符串、哈希表、列表、集合和有序集合等。它通常被称为数据结构服务器,允许应用程序通过服务端命令操作这些值,而不必读取并重写整个对象。其数据模型支持从缓存、计数器到队列和事件处理等多种用途。(redis.io)
Redis 将基于内存的数据访问与可选的磁盘持久化、复制和分布式部署相结合。这些特性使它既能作为临时存储与其他数据库配合使用,也能作为持久化数据库使用;具体的持久性和可用性则取决于配置。其架构体现了分布式计算中的一些重要权衡,包括延迟、内存容量、写入持久性与故障转移之间的关系。(redis.io)
Redis 由 Salvatore Sanfilippo 于 2009 年创建,此后逐步扩展了存储和查询能力。2025 年 5 月 1 日发布的 Redis 8 集成了此前通过独立模块提供的功能,包括 JSON、时间序列、概率数据结构和搜索。该项目的发展历程还包括维护工作和许可方式的变化,以及独立开发的分支项目的出现。(redis.io)
起源、发展与项目治理
网名为 antirez 的 Salvatore Sanfilippo 于 2009 年创建了 Redis,以解决其初创企业面临的数据访问和扩展问题。当时直接服务的应用是网站流量监测服务 LLOOGG,其工作负载已超出现有数据库架构的承载能力。Redis 将工作数据存放在内存中,同时提供磁盘持久化功能,并直接提供针对数据结构的操作,而不要求所有操作都通过关系型查询来表达。在 2009 年 6 月 19 日的一篇文章中,Sanfilippo 表示,LLOOGG 已完全切换到 Redis,原有的 MySQL 服务器也已关闭。因此,Redis 最初是为特定应用构建的基础设施,后来才发展为独立维护的开源软件项目。(redis.io)
这一软件项目早于后来为其开发提供支持的商业公司。该公司成立于 2011 年,当时名为 Redis Labs,并非 Redis 最初诞生时所在的那家初创企业。2015 年 7 月 15 日,Redis Labs 宣布 Sanfilippo 将加入公司,担任开源 Redis 开发负责人,继续履行此前由 VMware 和 Pivotal 支持的职责。这一安排使项目创建者加入了公司,但既不标志着 Redis 的诞生,也不意味着他从此才开始担任技术领导者。因此,尽管公司提供的工程资源日益增多,企业赞助与项目维护仍有各自不同的发展时间线。(redis.io)
2020 年 6 月 30 日,在担任主要维护者约十一年后,Sanfilippo 宣布退出日常维护工作。Yossi Gottlieb 和 Oran Agra 接任项目负责人,并引入了以小型核心团队为中心的轻量级治理模式。此前,Sanfilippo 对功能采纳、错误修复和设计取舍拥有最终决定权;新模式则将职责分配给凭借专业知识、贡献和投入程度获选的开发者。Itamar Haber 被宣布为首位新增成员。这次交接涵盖了审查贡献、修复错误和准备发布等实际职责;同期公告还明确,Sanfilippo 将在公司担任技术顾问。(redis.io)
Redis 在核心服务器之外的功能扩展,最初依赖单独提供的模块。2022 年 3 月 23 日,Redis 推出 Redis Stack,将其中若干扩展打包在一起,以简化安装、文档和客户端支持。其最初的服务器发行版将 Redis 与 RediSearch、RedisJSON、RedisGraph、RedisTimeSeries 和 RedisBloom 整合,增加了搜索、文档、图、时间序列和概率型数据结构等功能。更广泛的套件还包括用于可视化和调试的 RedisInsight,以及客户端库和对象映射库。发布时,Redis Stack 被明确定位为一种额外的发行版,而非核心 Redis 服务器的替代品。(redis.io)
Redis 8 于 2025 年 5 月 1 日正式发布,将社区服务器与 Redis Stack 整合为统一的 Redis Open Source 发行版。该发行版内置搜索与查询功能、JSON 文档、时间序列存储以及五种概率型数据结构,减少了单独选择模块版本并处理版本匹配的需要。除 Redis Query Engine 已有的向量搜索功能外,此次发布还引入了由 Sanfilippo 开发、处于测试阶段的向量集合。这种整合改变了功能的交付方式:此前需要安装扩展版 Stack 才能获得的功能,成为了 Redis 8 发行版的一部分。(redis.io)
键空间与基本数据类型
Redis 将每个数据库组织为一个键空间,其中每个唯一的键都标识一个具有特定类型的值。值可以是单个字符串,也可以是由原生数据结构表示的集合。键是二进制安全的字节序列:不必是有效的文本,也可以包含空白字符、标点符号或非文本字节。user:42 和 counter:visits 等名称按惯例使用冒号作为分隔符;这种分隔符并不会建立目录层次结构,也不会形成强制隔离的命名空间。这些名称由应用程序指定,应用程序需避免同一数据库内的名称冲突。TYPE user:42 可查询所存值的类型,而 EXISTS 和 DEL 等命令则直接对键进行操作,与值的类型无关。(redis.io)
字符串值同样是二进制安全的,可以存放文本、序列化后的应用程序数据或任意字节;其默认大小上限为 512 MB。SET counter:visits 10 存储数字十的字符串表示,GET counter:visits 则将其取回。数值命令会将符合要求的字符串内容解释为数字:INCR counter:visits 将本例中的值增加到十一,随后执行 INCRBY counter:visits 5 则会将其增加到十六。每次递增都是原子操作,无须由应用程序执行“读取—修改—写回”的操作序列,从而避免并发客户端相互覆盖更新。对不存在的键执行递增操作时,初始值按零处理。因此,计数器是字符串类型的一种用途,而不是独立的基本数据类型。(redis.io)
哈希在一个顶层键下存储字段—值对。例如,HSET user:42 name Ada visits 10 会在 user:42 所标识的哈希中创建或更新两个字段。这里的 name 和 visits 是字段,而不是数据库键空间中的独立键。HGET user:42 name 只获取姓名,HMGET user:42 name visits 获取指定的两个值,HGETALL user:42 则获取所有字段—值对。HINCRBY user:42 visits 1 只会将其中的数值字段增加到十一。这些命令使客户端能够访问和修改单个属性,而不必获取、反序列化并替换整个应用程序对象。哈希字段的值是字符串,而不是递归嵌套的 Redis 集合。(redis.io)
列表按位置维护一个字符串序列,允许值重复。在头部和尾部插入元素的效果不同:LPUSH 在头部添加元素,RPUSH 则在尾部追加元素;LPOP 和 RPOP 分别从对应的两端移除元素。因此,向一个起初不存在的列表依次追加 a、b 和 a,会按此顺序保留全部三个元素。LRANGE 可获取指定位置区间内的元素。相比之下,无序集合中的每个不同字符串只存储一次:添加已有成员不会产生重复项,遍历时也不保证按插入顺序返回。SISMEMBER 用于检查成员是否存在,而交集、并集和差集命令则按照集合语义组合各个集合。(redis.io)
有序集合将唯一的字符串成员与浮点数分值关联起来。成员按分值递增排序,而不是按插入时间排序;分值相同的成员按字典序排列。例如,ZADD scores 120 Ada 95 Ben 会使 Ben 在升序排列中位于 Ada 之前。再次添加 Ada 并指定不同分值,会更新已有成员,而不会产生重复项。相关命令支持按排名或分值区间获取成员,ZRANGE scores 0 1 WITHSCORES 会返回前两个成员及其分值。关联的分值用于决定排序,并不是可包含额外字段的任意记录。(redis.io)
命令的开销取决于具体操作,而不仅仅取决于操作对象的类型。采用大O记号表示时,Redis 文档将 SISMEMBER 的复杂度列为 ,与集合的成员数量无关;而 SUNION 的复杂度为 ,其中 表示所有输入集合的成员总数。使用 HGETALL 获取整个哈希的复杂度同样为 ,此时取决于字段数量。有序集合每插入一个成员的复杂度为 ,按排名区间获取成员的复杂度则为 ,其中 为返回的成员数量。这些时间复杂度上的差异,体现了直接检查成员是否存在的操作,与需要遍历集合、合并集合内容或构建大量结果的命令之间的区别。(redis.io)
专用结构、索引与搜索
位图和位域并不引入独立的存储类型,而是以位为粒度解释 Redis 字符串值。SETBIT 和 GETBIT 用于访问单个位;BITCOUNT 统计已置位的位数,BITOP 则通过按位运算组合字符串。因此,每日活跃情况位图可以为每个用户标识符分配一个位,无须将标识符存为集合成员,就能统计活跃用户数。Redis 3.2 引入的 BITFIELD 可以将指定位宽的有符号或无符号整数打包到字符串中,支持原子的读取、赋值和递增操作,并可在溢出时选择回绕、饱和或失败。地理空间索引则将有名称的位置与经纬度坐标关联起来:GEOADD 用于记录位置点,GEOSEARCH 用于检索指定半径或矩形区域内的位置点。(redis.io)
HyperLogLog 用于估计基数,即观测到的不同元素的数量,而不保留这些元素的可枚举集合。Redis 的实现为该估计器最多使用约 12 KB 空间,标准误差为 0.81%;这是统计意义上的误差度量,并不保证每次结果的误差都在这一范围内。PFADD 用于纳入观测值,PFCOUNT 用于估计不同元素的数量,PFMERGE 用于合并估计器。例如,可以合并每日访客估计器,估计多天内的去重访客数,而不是错误地将存在重叠的每日计数相加。与普通集合不同,这种结构无法返回原始成员,也无法精确查询某个元素是否属于集合。(redis.io)
其他概率型结构各自解决不同的问题。布隆过滤器 用于测试元素是否存在,但可能出现假阳性:“可能存在”不同于阴性结果所表示的“确定不存在”。布谷鸟过滤器使用紧凑的指纹提供类似的成员测试,并支持删除。Count-min sketch 通过哈希让多个元素共用计数器,以估计元素的出现频次;哈希冲突可能导致计数偏高。Top-K 近似维护出现频次最高的一组元素,而不是对所有元素进行精确排名;t-digest 则用于估计分位数,例如延迟分布的第 99 百分位值。这些结构以紧凑的表示换取近似答案,不能与精确的集合成员查询、计数器或有序集合排名互相替代。(redis.io)
JSON 值保留对象和数组的层级结构,而不是仅将文档视为不透明的序列化字符串。JSONPath 表达式可以选择嵌套元素进行读取和修改;应用程序可以更新单个属性或数组,而无须读取并替换整个文档。时间序列结构存储“时间戳—数值”样本,时间戳以 Unix 毫秒时间表示。TS.ADD 用于记录测量值,TS.RANGE 用于读取某个时间区间内的测量值,还可选择将其聚合到时间桶中,例如根据传感器读数计算每分钟的平均值。保留期限和压缩规则控制原始测量值与降采样后测量值的保留方式。(redis.io)
Redis 查询引擎提供基于索引的信息检索,不再局限于按键查找。FT.CREATE 为哈希或 JSON 文档定义二级索引模式,可包含文本、数值、标签、地理位置和向量字段。查询可以将全文匹配与字段限制相结合,例如同时指定商品类别和价格区间。向量字段支持最近邻搜索和距离范围搜索,也支持元数据过滤;可用的距离度量包括欧氏距离,以及基于余弦相似度的余弦距离。索引会随着文档的变化而更新。JSON、时间序列、另外五种概率型结构以及查询引擎此前通过模块或 Redis Stack 提供,后来一并集成到 Redis 8 中;Redis 8 于 2025 年 5 月 1 日正式发布。不应假定未安装扩展的旧版服务器也具备这些功能。(redis.io)
向量集合是一种独立的键级数据类型,并非查询引擎向量索引的另一种名称。它由 Salvatore Sanfilippo 开发,在 Redis 8.0 中以测试版形式引入,可将元素名称直接关联到高维向量。VADD 用于插入元素,VSIM 则通过分层可导航小世界图检索相似元素。相比之下,查询引擎的向量搜索是在显式定义的模式下,为哈希或 JSON 文档中的向量字段建立索引。引入时的测试版标记针对的是向量集合及其可能变化的接口,而非 Redis 中的所有向量搜索功能。(redis.io)
服务器架构与内存管理
Redis 开源版将工作数据集保存在主内存中:常规读取和更新直接操作驻留内存的对象,而不是从磁盘读取数据库页面。它的事件循环通过多路复用管理客户端连接,处理网络就绪事件,无须为每个连接分配专用的执行线程。大多数核心命令在主线程上依次执行,因此计算开销较大的命令可能会使无关客户端的请求也受到延迟。这种执行模型并不意味着整个服务器只使用一个线程。后台线程会执行部分任务,包括缓慢的磁盘 I/O;持久化也可能涉及子进程。Redis 6 引入了可选的 I/O 多线程功能,用于网络读写和协议解析;Redis 8 则引入了经过改进的实现,通过 io-threads 控制。网络处理的并行化本身并不会让常规命令也并行执行。(redis.io)
值对外呈现的数据类型与其内部编码是两回事。小型聚合数据结构可以采用紧凑表示,避免较大结构所需的内存分配和指针开销。例如,根据 Redis 7.0 文档中的默认配置,只要单个字段和值均不超过 64 字节,包含至多 512 个字段—值对的哈希就可以使用 listpack;有序集合使用 listpack 的默认上限为 128 个成员,值的大小阈值为 64 字节。listpack 以紧凑形式存储条目,通过增加一些 CPU 运算来降低内存消耗。小型集合可以接受顺序查找,而较大的集合则更适合使用哈希表等结构。当插入操作使对象超出配置的大小或元素数量阈值时,Redis 会自动将其转换为更通用的编码,而不改变其命令接口。提高这些阈值可以节省内存,但也会增加查找以及最终进行编码转换时所需的工作量。(redis.io)
maxmemory 设置的是触发键淘汰的内存阈值,而不是进程总内存的硬性上限。部分复制缓冲区和仅追加文件缓冲区不计入淘汰所依据的内存用量,大型操作也可能使内存用量暂时超出阈值。maxmemory-policy 用于选择应对策略:allkeys-lru 近似实现最近最少使用淘汰,allkeys-lfu 近似实现最不经常使用淘汰,allkeys-random 则随机选择要淘汰的键。对应的 volatile-* 策略仅将带有过期元数据的键纳入淘汰范围;volatile-ttl 优先淘汰剩余存活时间最短的键。在 noeviction 策略下,Redis 会拒绝需要为数据集额外分配内存的命令,而不是丢弃现有键,但仍可执行读取操作。(redis.io)
Redis 不会维护精确的全局 LRU 顺序,而是对候选键进行抽样,优先淘汰上次访问时间更早的键;从 Redis 3.0 开始,Redis 通过保留候选池来改进选择效果。增大 maxmemory-samples 可以提高近似精度,但也会增加 CPU 开销。Redis 4.0 引入的 LFU 使用带衰减机制的概率访问计数器,使曾经热门但如今不再活跃的键也能成为淘汰候选。(redis.io)
过期机制独立于内存压力。EXPIRE 等命令会为键附加存活时间元数据;Redis 将其到期时间存储为绝对 Unix 时间戳,自 2.6 版本起精度达到毫秒。过期键会在被访问时被动删除,也会通过定期检查主动删除,包括那些客户端再也不会请求的键。例如,即使内存仍然充足,EXPIRE session:42 1800 也会将一个会话键的存活时间设为 30 分钟。反过来,淘汰策略可能会在到期之前就丢弃该键。PERSIST 会移除过期元数据,但不能保护该键免受 allkeys-* 淘汰策略的影响。因此,将临时应用状态保留到指定时间,与在内存压力下仍保留该状态,是两个不同的属性;仅仅移除超时设置并不能保证数据会被保留。(redis.io)
客户端协议、事务与可编程性
客户端通过 Redis 序列化协议(RESP)进行通信,通常使用持久 TCP 连接,而不是为每条命令单独建立连接。请求是由二进制安全的批量字符串组成的数组,其中包含命令名称和参数。RESP2 的回复类型包括简单字符串、错误、整数、批量字符串和数组,并用空值表示缺失的值。RESP3 新增了显式空值、布尔值、双精度浮点数、大数、映射、集合、属性、批量错误、原样字符串以及带外推送消息;客户端可以通过 HELLO 3 选择使用该版本。客户端库会将这些表示形式转换为编程语言中的值,并且通常通过连接池或多路复用来管理可复用的连接。redis-cli 工具既支持交互式命令提示符,也支持通过终端参数传入单条命令。(redis.io)
流水线机制允许连续发送多条命令,而不必等待前一条命令的回复,随后再按顺序读取响应。例如,可以一次发送四个 INCR counter 请求,而无须依次进行四轮请求与响应交换。除了减少网络往返开销,批量处理还允许多个请求或回复共用 read() 和 write() 系统调用。服务器必须缓冲尚未被读取的回复,因此过大的流水线会增加内存消耗;Redis 文档以每批 10,000 条命令为例,说明如何限制批次大小。流水线本身既不提供事务隔离,也不保证整组命令的原子性:它改变的是通信调度方式,而不是其中各条命令的执行保证。(redis.io)
Redis 的数据库事务使用 MULTI 开始将命令加入队列,使用 EXEC 按顺序执行这些命令,期间不会插入其他客户端的命令,并使用 DISCARD 放弃队列中的命令。EXEC 返回一个包含各条命令执行结果的数组。因此,依次执行 MULTI、INCR foo、INCR bar 和 EXEC,会将两个计数器的递增作为一次隔离操作完成。这并不提供通用的回滚机制。自 2.6.5 版本起,在命令入队时检测到的错误,例如未知命令或参数数量不正确,会导致事务被拒绝执行。执行时发生的错误,例如对字符串执行列表操作,则会出现在返回结果中;队列中的其他命令仍会执行,成功产生的更改也会保留。(redis.io)
WATCH 提供乐观并发控制,而不是锁定键。客户端可以监视 balance,读取其值,计算替换值,然后提交事务。如果被监视的键在 EXEC 执行前发生变化,Redis 就会中止事务,不执行队列中的任何命令,让应用程序重新读取最新值后重试。这些变化包括其他客户端的写入,以及服务器端的过期或逐出操作;从 Redis 6.0.9 开始,键过期也会使监视该键的事务失效。必须先设置监视,再进行需要检查其有效性的读取。无论 EXEC 是否成功,都会清除对键的监视,DISCARD 也是如此;UNWATCH 则用于显式清除监视。(redis.io)
服务器端 Lua 脚本支持带条件判断的“读取—计算—写入”逻辑,各步骤之间无须与客户端进行网络往返通信。Redis 2.6 引入了 EVAL,该命令接受脚本源代码、键的数量、键名以及附加参数;脚本通过 KEYS 和 ARGV 访问这些内容。SCRIPT LOAD 和 EVALSHA 允许后续通过 SHA-1 摘要调用脚本,但脚本缓存并不是持久保存应用程序代码的存储:客户端必须处理脚本缺失的情况,并重新加载脚本。7.0 版本引入的 Redis Functions 则将具名函数组织为函数库,使用 FUNCTION LOAD 加载,并通过 FCALL 调用。函数库由数据库管理,并纳入持久化和复制机制。(redis.io)
相对于其他客户端的操作,脚本和函数以原子方式执行,因此运行时间较长的逻辑会阻塞普通命令的执行。超过配置的执行时间阈值,并不会自动终止脚本或撤销其写入。模块提供了另一种扩展机制:动态加载的原生库使用 Redis 的 C 应用程序编程接口注册命令并实现新的数据类型,而不只是用 Lua 组合现有命令。模块定义的类型可以提供用于保存、加载以及仅追加文件重写的回调函数。模块 API 还支持异步操作,可以只阻塞发起请求的客户端,而不阻塞整个服务器。(redis.io)
持久化、恢复与持久性
Redis 数据库(RDB)持久化会将数据集在某个时间点的状态保存为紧凑的二进制映像,通常存放在 dump.rdb 中。恢复时直接还原该映像,而不重放此前的命令历史。快照规则结合了经过的时间和变更次数:例如,save 60 1000 表示经过 60 秒且至少发生 1,000 次变更后创建快照。若发生崩溃,未包含在最近一次已完成快照中的写入可能丢失。因此,RDB 提供的是离散的恢复点,而非连续的持久性保障。其紧凑的单文件形式也便于传输和保留数据集的历史副本。(redis.io)
后台快照通过 BGSAVE 和操作系统的 fork() 机制实现。子进程写入一个临时 RDB 文件,只有在写入完成后才替换先前的快照;与此同时,父进程继续处理请求。借助写时复制,父子进程起初共享内存页;后续修改则需要生成私有副本,因此内存消耗会随保存期间发生变更的内存页数量而增加。创建子进程本身可能暂停请求处理,所以“后台”并不意味着没有延迟。相比之下,SAVE 会同步序列化数据集,并在整个操作期间阻塞其他客户端。(redis.io)
仅追加文件(AOF)持久化会记录改变数据集的操作,并在启动时重放已持久化的操作序列。其同步策略决定了接受写入与强制将相应日志记录写入存储之间的时间间隔。appendfsync always 会在发送响应之前同步每批追加记录;多条命令可以共用一次同步。默认设置 appendfsync everysec 通常通过后台线程每秒同步一次,以可能丢失约一秒写入为代价,降低同步开销。appendfsync no 则将刷盘交由操作系统处理,因此可能丢失数据的时间窗口取决于操作系统的行为。所以,将数据写入操作系统缓冲区与完成 fsync 并不是一回事。(redis.io)
AOF 重写通过将累积的历史记录替换为足以重建当前数据集的表示形式,来限制文件增长。例如,对同一个计数器反复更新时,并不需要保留每个中间值。BGREWRITEAOF 用于请求后台重写;自 Redis 2.4 起,也支持自动触发重写。重写失败不会破坏现有 AOF;若在后台快照期间请求重写,则会安排在该快照完成后执行。自 Redis 7.0 起,AOF 持久化使用基础文件、增量文件,以及用于标识当前有效文件集合的清单文件。重写期间,新的写入进入一个新的增量文件,同时由子进程生成替代用的基础文件;随后,通过原子替换清单文件来启用生成的文件集合。(redis.io)
RDB 和 AOF 可以同时使用。按照常规启动规则,两者均启用时,Redis 会选择 AOF,因为预计其中包含更完整的恢复历史;它不会将两种方式各自恢复的数据集合并。也可以禁用持久化,例如在仅存储可丢弃缓存值的部署中;此时,磁盘上不会保有当前可用的恢复状态。(redis.io)
可用于恢复的持久化文件并不等同于单独保留的备份:位于同一故障存储设备上的文件无法防范该设备损毁造成的数据丢失,而替换当前快照也不会保留更早的恢复点。对于多部分 AOF,备份需要包含相互一致的清单文件及其引用的基础文件和增量文件。在重写期间复制这些文件,可能会得到无效的文件集合,因为当前有效的文件列表会发生变化。因此,Redis 文档说明了如何协调文件复制与重写活动;将副本另行保留,还能保存实例当前持久化文件之外的恢复点。(redis.io)
复制、Sentinel 与 Redis Cluster
Redis 采用异步主从数据库复制:主节点以流的形式向副本节点发送数据集的变更,通常不会等待副本确认便向客户端返回响应。因此,从副本读取可能得到过时的值。复制 ID 用于标识数据集的历史,而字节偏移量则标识复制流的传输进度。连接断开后,副本会携带此前的 ID 和偏移量发出 PSYNC。如果主节点能够识别这段历史,且其内存中的复制积压缓冲区仍保留着缺失的字节,就会进行部分重同步,仅传输这些字节。否则,就会执行全量同步,先传输 RDB 快照,再传输生成快照期间缓冲的变更。Redis 2.8.18 引入了无盘复制,可由子进程直接以流的形式传输快照,无须借助中间磁盘文件。(redis.io)
Redis Sentinel 为非集群部署提供监控、故障检测、自动故障转移和主节点发现功能。单个 Sentinel 会将无响应的实例标记为主观下线;当达到配置的法定数量的 Sentinel 一致认定主节点故障时,该主节点便被判定为客观下线。要授权执行故障转移,还需要获得过半数 Sentinel 的选举支持;如果配置的法定数量更大,则须达到该数量。因此,在有五个 Sentinel、法定数量为二的情况下,两个 Sentinel 即可判定故障,但至少需要三个 Sentinel 才能授权选出故障转移领导者。该领导者会将一个副本提升为主节点,并重新配置其余副本,使其跟随新主节点。Sentinel 负责管理可用性,而不负责数据分区。(redis.io) 支持 Sentinel 的客户端通过 SENTINEL get-master-addr-by-name 查询指定名称的主节点地址,并在重新连接时再次执行发现流程,而不是将该地址视为永久不变。(redis.io)
Redis Cluster 将数据分区、各主节点的复制机制及自身的故障转移机制结合起来。它将键分配到 16,384 个哈希槽中,槽位通常按 计算。每个主节点负责一部分槽位;副本只复制各自对应的主节点,而非整个集群。哈希标签通过对花括号内指定的非空子串计算哈希来改变键的分布位置:例如,user:{123}:profile 和 user:{123}:account 会落入同一个槽位。多键命令、事务和 Lua 脚本通常要求所有涉及的键都位于同一个槽位;仅位于同一个节点上并不足够。(redis.io) 重新分片会在主节点之间转移槽位及其中的键。MOVED 响应会将客户端指向槽位当前所属的节点,客户端可据此更新其路由映射。迁移期间,ASK 则仅重定向当前请求:客户端在重试之前向目标节点发送 ASKING,但不永久更改其槽位映射。(redis.io)
如果主节点发生故障时,其复制流尚未到达随后被提升为主节点的副本,那么即使已经确认的写入也可能丢失。网络分区还会带来另一个数据丢失窗口:被隔离的主节点最初可能仍接受写入,而多数派一侧已将其副本提升为主节点。旧主节点重新加入时,隔离期间的这些写入可能被丢弃。在 Redis Cluster 中,如果某个主节点无法与过半数主节点通信,那么在配置的节点超时时间过后,它就会停止接受写入。如果发生故障的主节点有合适的副本可用,多数派一侧就能通过选举恢复服务;仅仅拥有多数节点并不能补足缺失的数据。(redis.io)
WAIT numreplicas timeout 允许客户端等待副本确认同一连接上此前的写入。例如,WAIT 2 1000 最多等待 1,000 毫秒,并返回已确认的副本数量,该数量可能少于两个。它能降低复制延迟带来的风险,但在未达到所要求的副本数量时不会回滚写入,也不会使 Redis 具备强一致性。故障转移时的副本选择只能尽力而为,因此,即使某次写入已获多个副本确认,也仍有可能丢失。复制确认也不代表这些副本上的数据已持久化同步到存储介质;崩溃恢复仍取决于持久化配置。(redis.io)
消息传递、事件处理与应用模式
Redis 通过命名频道实现发布—订阅模式:PUBLISH orders.created payload 会向当前使用 SUBSCRIBE orders.created 监听该频道的客户端发送消息。模式订阅采用 glob 风格的通配符匹配;例如,PSUBSCRIBE orders.* 会接收频道名称与该模式匹配的消息。Pub/Sub 提供的是至多一次投递,而不保留消息历史:断开连接的订阅者会错过此期间发布的消息,Redis 也不为这些消息提供确认或重放机制。订阅重叠是另一个需要考虑的问题:如果客户端既直接订阅了某个频道,又通过匹配的模式订阅了该频道,就可能因一次消息发布而收到两条通知。因此,Pub/Sub 与工作队列不同:它向感兴趣的订阅者广播消息,而不是将每条消息分配给一个工作进程。(redis.io)
Redis 5.0 引入的流(Stream)是一种类似于仅追加事件日志的数据结构。XADD orders * order_id 42 status created 会追加字段—值对,并返回一个条目标识符,通常由毫秒时间戳和序列号组成。对于仍然保留的条目,可以使用 XRANGE 按标识符范围检索,也可以使用 XREAD 读取先前记录的标识符之后的条目;与 Pub/Sub 不同,读取流中的事件并不要求客户端在事件到达时保持连接。消费者组通过 XREADGROUP 将新条目分配给各个工作进程,不同的组则各自维护独立的消费状态。除非禁用了确认跟踪,否则已投递的条目会进入该组的待处理条目列表。XACK 用于确认处理完成并移除待处理记录,但本身不会删除流中的条目。XPENDING 可查看尚未完成的投递,认领操作则可转移条目的归属,让另一个工作进程重试处理。这种机制支持至少一次处理,但如果工作进程执行了外部操作,却在确认之前发生故障,就可能出现重复投递。XTRIM 或 XADD 的裁剪选项可按长度或最小标识符限制保留范围;重放仅限于仍然保留的条目。(redis.io)
在缓存场景中,Redis 保存的是可替换的记录副本,而这些记录的权威版本存放在另一个数据库中。因此,淘汰数据会造成缓存未命中,而不一定会丢失底层记录:应用程序可以重新获取并缓存该记录。会话存储的后果则有所不同。会话标识符可以指向一个 Redis 哈希,其中包含登录上下文或各用户的状态,可由多台应用服务器共享,并通过过期机制实现不活动超时。如果该哈希是唯一的副本,内存压力触发的淘汰就会丢弃应用程序可见的状态,并可能在预定超时之前终止会话。因此,过期与淘汰体现的是不同的策略:前者规定数据的存活时间,后者则用于应对内存压力。noeviction 策略可避免因内存压力而移除数据,但也可能导致会增加内存用量的写入操作失败。(redis.io)
计数器和速率限制是基于字符串数值操作构建的应用模式,而不是独立的 Redis 数据类型。INCR page:42:views 以原子方式递增整数,避免在应用端执行读取—修改—写回的操作序列。固定窗口限流器可以为每个客户端每秒使用一个键:递增该键,设置过期时间以便清理,并在返回的计数超过阈值(例如十次)时拒绝请求。递增和设置过期时间必须协调执行;否则,客户端若在 INCR 与 EXPIRE 之间发生故障,就可能留下一个未设置预期存活时间的计数器。文档中的实现使用事务组合这些命令,或使用 Lua 脚本按条件设置过期时间。(redis.io)
排行榜同样是将应用语义映射到有序集合上:成员代表玩家,分数代表积分;ZRANGE leaderboard 0 9 REV WITHSCORES 可获取积分最高的十个成员。基于列表的工作队列从两端分别执行插入和移除,以实现先进先出,例如从左端插入、从右端移除。简单的破坏性弹出操作在移除任务与完成处理之间留下了一个可能发生故障的时间窗口。Redis 6.2 引入的 LMOVE 则可将任务原子地移入处理中列表;工作进程在处理成功后将其移除,应用逻辑则可将超时任务放回等待队列。与流的消费者组不同,这种安排本身并不维护待确认投递的元数据,也不提供确认机制:这些约定由队列实现负责。(redis.io)
部署、可观测性与安全
Redis Open Source 采用自行管理的模式:运维人员负责安装服务器,并管理其配置、容量、持久化、升级和可用性方案。Redis Software 同样由用户自行管理,可部署在本地或云基础设施上,但它提供了商业化的集群管理平台,配备管理界面及产品专属功能。相比之下,Redis Cloud 提供托管式云计算数据库服务,由服务商负责基础设施和服务运维,客户则负责配置数据库及应用程序访问。这些部署模式并不意味着功能完全相同。例如,Redis Software 的 Active-Active 数据库通过可解决冲突的复制机制,支持跨地理区域的写入;普通 Redis Open Source 安装及其主从复制并不具备这一能力。(redis.io)
在可观测性方面,需要区分命令执行时间与应用程序的端到端响应时间。SLOWLOG 记录执行时间超过 slowlog-log-slower-than 的命令,但不计入与客户端通信的 I/O 时间;因此,慢日志为空并不能证明客户端感受到的延迟很低。INFO commandstats 报告各命令的调用次数、累计 CPU 时间以及每次执行的平均 CPU 时间,其他 INFO 部分则提供内存、客户端、持久化和复制方面的指标。延迟监视器会记录耗时超过可配置毫秒阈值的内部事件:例如,CONFIG SET latency-monitor-threshold 100 会启用记录,并将阈值设为 100 毫秒。LATENCY HISTORY 提供事件样本,LATENCY DOCTOR 则生成诊断报告。这些工具衡量的是服务器行为的不同方面,其测量结果不能作为同一种延迟指标相互替代。(redis.io)
不同机制可能引发相似的响应时间突增。遍历大型集合的命令可能拖延其他命令的执行;持久化会带来进程派生和存储同步的开销;而内存换页、操作系统调度或虚拟机监控程序调度,即使命令复杂度不高,也可能中断服务。redis-cli --intrinsic-latency 100 在服务器主机上测量 100 秒内的调度中断情况,不会发送 Redis 请求;客户端侧的延迟测量则包含与服务器通信的时间。将这些测量结果与 CPU 利用率、磁盘活动及持久化事件进行关联分析,有助于区分耗时的数据库操作与环境因素造成的延迟。(redis.io)
因此,性能测量结果取决于工作负载和测试安排。redis-benchmark 提供了用于控制并发连接数(-c)、请求数(-n)、值大小(-d)、随机键范围(-r)和流水线深度(-P)的选项。同步客户端每次等待回复,都要再次承担网络往返开销;流水线则在读取回复前先发送多条命令,从而分摊网络往返和套接字系统调用的开销。流水线深度为 16,描述的是一种不同于每个连接仅有一个未完成请求的工作负载,并不代表服务器速度获得了无条件提升。有意义的比较应明确 Redis 版本、硬件、网络部署位置、命令组合、数据集大小、值大小、并发度、流水线深度及持久化配置。针对小值的 GET/SET 基准测试,不能用于确定集合操作或更大数据集的普遍吞吐量或延迟。(redis.io)
Redis 的安全模型以可信客户端访问为前提,这些客户端通常是应用服务器,而非终端用户的浏览器。网络地址绑定和防火墙限制为服务建立访问边界。3.2.0 版本引入的保护模式,会在文档所述的不安全默认配置条件下,拒绝来自非回环地址的客户端;它是防止服务意外暴露的保护措施,不能替代访问策略。传统的 requirepass 身份验证使用通过 AUTH 提交的共享密码。Redis 6 引入了命名用户和访问控制列表,可限制命令和键模式:例如,新建用户若被授予 ~cached:* +get 权限,就可以读取匹配的键,但不能执行 SET。身份验证和授权与传输层安全协议(TLS)是不同的机制;自 Redis 6 起,客户端连接、复制连接和集群连接可选择启用 TLS。未启用 TLS 时,AUTH 凭据以未加密形式传输。TLS 提供传输过程中的加密和完整性保护,但既不决定命令权限,也不对静态存储的应用数据进行加密;应用层的访问校验仍是一项独立职责。(redis.io)
许可、分支与更广泛的生态系统
Redis 7.2 及更早的版本系列仍以 BSD 三条款许可证提供。2024 年 3 月 20 日,Redis 宣布,从 Redis 7.4 开始,后续版本将改为提供两种许可证供用户选择:Redis 源码可用许可证第 2 版(RSALv2)和服务器端公共许可证第 1 版(SSPLv1)。这一变化涉及新版本的许可方式,并非撤销早期版本的 BSD 许可。因此,Redis 的许可历史需要按版本区分:采用 BSD 许可证的 Redis 7.2 仍然可用,并不意味着 Redis 7.4 中引入的代码也可按 BSD 许可证使用。(redis.io)
2024 年提供的这两种许可证允许获取源代码,但均未获开放源代码促进会(OSI)认可为开源软件许可证。两者的限制有所不同:RSALv2 限制某些特定形式的商业化使用,而 SSPLv1 则要求将该软件的功能作为服务提供的供应商,依照其条款公开与服务有关的源代码,包括管理层代码。因此,仅仅能够查看源代码,并不代表拥有与 OSI 认可的许可证相同的使用权限。服务器许可方式的变化也不会自动延伸至采用独立许可证的生态系统组件:Redis 明确表示,各语言的客户端库和基础设施提供程序不在此次变更范围内。redis-py、node-redis 和 Jedis 等库列明采用 MIT 许可证,hiredis 则采用 BSD 三条款许可证。(redis.io)
2025 年 5 月 1 日,Redis 宣布从 Redis 8 开始,将 GNU Affero 通用公共许可证第 3 版(AGPLv3)加入许可选项,作为第三种选择。这在 RSALv2 和 SSPLv1 之外,增加了一条采用 OSI 认可许可证的途径;接收者只需选择其中一种,而不必同时遵守三种许可证。AGPLv3 是一种著佐权许可证,并不意味着回归早期宽松的 BSD 许可模式。集成的 RediSearch、RedisJSON、RedisTimeSeries 和 RedisBloom 组件也适用 Redis 8 的三选一许可安排。这些条款并未消除不同版本系列或产品之间的区别:Redis 7.2 及更早版本保留 BSD 许可,Redis 7.4 保留其双重许可安排,商业产品 Redis Software 和 Redis Cloud 则仍受各自独立的产品协议约束。(redis.io)
2024 年 3 月 28 日,Linux 基金会宣布推出 Valkey,这是基于 Redis 7.2.4 建立的独立分支,保留了 BSD 三条款许可。其最初的支持者包括亚马逊云科技、谷歌云、甲骨文、爱立信和 Snap。参与者包括前 Redis 维护者 Madelyn Olson,以及贡献者 Ping Xie 和 Viktor Söderqvist;公告介绍了一种开放治理模式,其技术领导委员会中包含前 Redis 贡献者。因此,Valkey 是在独立管理下,基于共同的 BSD 许可代码库继续开发,而不是换了名称的 Redis 版本,也不是 Redis 的商业版本。其公布的路线图包括对集群、哈希槽迁移、多线程性能和命令的改进。(linuxfoundation.org)
这一生态系统中的兼容性有几个不同层面。Redis 序列化协议(RESP)定义了请求和响应的编码方式;实现 RESP2 或 RESP3,本身并不意味着具有相同的命令覆盖范围、语义、持久化格式或商业功能。Valkey 的迁移文档说明了这种区别:文档称其兼容 Redis OSS 7.2 及更早版本,包括相应的 RDB 快照和 AOF 持久化文件,但同时指出,Redis 7.4 及更高版本生成的 RDB 文件与 Valkey 不兼容。因此,客户端能否连接并交换 RESP 消息,与服务器能否执行某条命令或加载某份备份,是不同的问题。描述兼容性时,必须同时指明具体实现及其版本,不能将“兼容 Redis”视为毫无限定的等同关系。(redis.io)
参考来源
- Redis then & now: Adapting with developers through every eraredis.io
- LLOOGG: ora completamente su Rediszzimma.antirez.com
- Redis Creator, Salvatore Sanfilippo (Antirez), Joins Redis Labsredis.io
- New Governance for Redisredis.io
- Thank You, Salvatore Sanfilipporedis.io
- Hello, Redis Stackredis.io
- Redis 8 GA: Fast, scalable, and feature-richredis.io
- Keys and valuesredis.io
- Keys and valuesredis.io
- Redis data typesredis.io
- Redis Stringsredis.io
- Redis hashesredis.io
- Compare data typesredis.io
- Redis listsredis.io
- Redis setsredis.io
- Redis sorted setsredis.io
- SISMEMBERredis.io
- HGETALLredis.io
- Redis bitmapsredis.io
- BITFIELDredis.io
- Redis geospatialredis.io
- HyperLogLogredis.io
- Bloom filterredis.io
- Cuckoo filterredis.io
- Count-min sketchredis.io
- Probabilisticredis.io
- t-digestredis.io
- JSONredis.io
- Time seriesredis.io
- TS.RANGEredis.io
- What is Redis? In-memory database, cache, and message brokerredis.io
- Diagnosing latency issuesredis.io
- Memory optimizationredis.io
- Key evictionredis.io
- EXPIREredis.io
- Redis serialization protocol specificationredis.io
- Connection pools and multiplexingredis.io
- Client toolsredis.io
- Redis pipeliningredis.io
- Transactionsredis.io
- Redis programmabilityredis.io
- Scripting with Luaredis.io
- Redis functionsredis.io
- Redis modules APIredis.io
- Modules API for native typesredis.io
- Redis persistenceredis.io
- Redis persistenceredis.io
- Redis FAQredis.io
- Diagnosing latency issuesredis.io
- SAVEredis.io
- Durable Redis technology for fast data storageredis.io
- redis-doc/docs/management/persistence.md at mastergithub.com
- BGREWRITEAOFredis.io
- Redis replicationredis.io
- High availability with Redis Sentinelredis.io
- Sentinel client specredis.io
- Scale with Redis Clusterredis.io
- Redis cluster specificationredis.io
- WAITredis.io
- Redis Pub/subredis.io
- Redis Streamsredis.io
- Redis Streamsredis.io
- Redis streaming with node-redisredis.io
- Redis streaming with redis-pyredis.io
- Redis session store with node-redisredis.io
- Redis session storeredis.io
- INCRredis.io
- ZRANGEredis.io
- LMOVEredis.io
- Install, set up, and upgrade Redis Softwareredis.io
- Redis productsredis.io
- Active-Active geo-distributed Redisredis.io
- SLOWLOGredis.io
- INFOredis.io
- Redis latency monitoringredis.io
- Redis benchmarkredis.io
- Redis securityredis.io
- ACLredis.io
- TLSredis.io
- Redis Adopts Dual Source-Available Licensingredis.io
- Licenses | Redisredis.io
- Redis is now available under the AGPLv3 open source licenseredis.io
- Linux Foundation Launches Open Source Valkey Communitylinuxfoundation.org
- valkey-doc/topics/migration.md at main · valkey-io/valkey-docgithub.com