Redis 基础知识
Redis 基础知识
前置基础知识
1. 说下什么是 Redis?
下面的介绍比较详细,读者简要理解就好。
Redis 是一个开源的、使用 C 语言编写的、支持网络、可基于内存亦可持久化的键值对存储数据库。
主要特点
高性能
数据存储在内存中,读写速度非常快,能够满足高并发、低延迟的应用场景需求。它可以支持每秒数万次的读写操作。
丰富的数据类型
Redis 支持多种数据类型,包括字符串(strings)、哈希(hashes)、列表(lists)、集合(sets)、有序集合(sorted sets)等。可以根据不同的应用场景选择合适的数据类型来存储和操作数据。
持久化
Redis 提供了两种持久化方式:RDB(Redis Database)和 AOF(Append Only File)。
RDB 是通过保存数据库在某一时刻的快照来实现持久化,而 AOF 则是通过记录服务器执行的所有写操作命令来实现持久化。
这两种方式可以确保在服务器重启或故障时,数据不会丢失。
支持主从复制
Redis 可以配置主从模式,实现数据的复制。
主节点负责写操作,从节点负责读操作,并从主节点同步数据。
这种架构可以提高系统的可用性和扩展性,当主节点出现故障时,可以快速切换到从节点,保证服务的连续性。
支持事务
Redis 支持事务操作,可以将多个命令打包成一个事务,保证这些命令要么全部执行成功,要么全部不执行。这在需要保证数据一致性的场景中非常有用。
发布/订阅模式
Redis 提供了发布/订阅功能,允许客户端订阅特定的频道,当有消息发布到这些频道时,订阅的客户端会收到通知。这种模式可以用于实现实时消息系统、事件通知等应用。
应用场景
缓存
由于 Redis 的高性能和内存存储特性,它非常适合作为缓存服务器,存储经常被访问的数据,以减少对后端数据库的访问压力,提高系统的响应速度。
计数器和限速器
可以使用 Redis 的原子操作来实现计数器,如统计网站的访问次数、用户的点赞数等。Redis 也可以用于实现限速器,限制某个用户或 IP 在一定时间内的访问频率。
分布式锁
在分布式系统中,Redis 可以用来实现分布式锁,保证多个节点对共享资源的互斥访问。通过使用 Redis 的 SETNX(Set if Not eXists)命令,可以在多个节点之间实现原子性的锁操作。
排行榜
利用 Redis 的有序集合数据类型,可以很方便地实现排行榜功能,如游戏中的得分排行榜、电商网站的销售排行榜等。
会话存储
在 Web 应用中,Redis 可以用来存储用户会话信息,代替传统的基于数据库或文件的会话存储方式。这样可以提高会话的读取和写入速度,同时也方便会话的管理和扩展。
2. Redis 和 Memcached 相比有什么优势?
Redis 和 Memcached 都是常用的内存存储系统,但是为什么我们平常多使用 redis 呢?
Redis 相比 Memcached 具有以下优势
Redis 支持多种数据类型,如字符串、哈希、列表、集合、有序集合等。这使得在不同的应用场景下可以更加灵活地存储和操作数据。例如,可以使用哈希类型来存储对象,使用列表类型实现队列,使用有序集合进行排行榜等操作。而 Memcached 只支持简单的 key-value 类型。
Redis 提供了两种持久化方式:RDB(快照)和 AOF(只追加文件)。RDB 是在指定的时间间隔内将内存中的数据集快照写入磁盘,AOF 则是将每次写操作命令追加到日志文件中。持久化机制可以保证在服务器重启或故障时,数据不会丢失。而 Memcached 没有提供持久化功能,数据全部存储在内存中,一旦服务器故障,数据将会丢失。
Redis 支持主从复制模式,可以配置一个主节点和多个从节点。主节点负责写操作,从节点复制主节点的数据,并可以提供读操作服务。这种架构可以提高系统的可用性和扩展性,当主节点出现故障时,可以快速切换到从节点,保证服务的连续性。Memcached 不支持主从复制。
Redis 支持事务操作,可以将多个命令打包成一个事务,保证这些命令要么全部执行成功,要么全部不执行。在需要保证数据一致性的场景中非常有用。Memcached 不支持事务。
Redis 采用了更加智能的内存管理策略,可以自动回收不再使用的内存空间,避免内存泄漏。Redis 还可以根据数据的访问频率和重要性,将部分数据从内存中交换到磁盘上,以释放内存空间。Memcached 的内存管理相对简单,可能会出现内存碎片和浪费的情况。
Redis 提供了发布/订阅功能,可以实现消息的实时推送和接收。这在实时通知、事件驱动架构等场景中非常有用。Memcached 没有发布/订阅功能。
3. Redis 官方为什么不开发 Windows 版本?
现在linux 版本已经足够稳定,且用户量很大,无需添加windows版本,反而会带来兼容性问题。
Windows中如果希望使用Redis,可以使用WSL安装部署,或者使用微软开发的Redis(仅供开发环境使用)。
4. 一个字符串类型能存储的最大容量是多少?
默认情况下,Redis 单个 String Value 最大可以存储 512 MB,即 512 × 1024 × 1024 字节。
Redis 的 Key 本身也是二进制安全的字符串,Key 名称的最大长度同样是 512 MB。
需要注意:
- Key 的 512 MB 指的是 Key 名称本身的长度,不是整个键值对的大小;
- Hash、List、Set 等集合类型的总容量可以超过 512 MB,但其中单个字符串元素仍会受到相应限制;
- 新版 Redis 的协议数据长度上限与
proto-max-bulk-len配置有关,因此更严谨的说法是“默认上限为 512 MB”; - 生产环境中不应接近这个上限,否则会形成 Big Key,增加网络传输、内存分配、持久化、复制和删除操作的耗时,并可能长时间阻塞主线程。
因此,面试时可以回答:
Redis 默认允许单个 String Value 最大为 512 MB,Key 名称的最大长度也是 512 MB。不过实际开发中应该避免保存如此大的 Key 或 Value,通常需要对大对象进行拆分、压缩,或者改用对象存储。
5. Redis 为什么将所有数据都放到内存中?
性能考量
内存读写速度优势,内存的读写速度比磁盘快几个数量级。数据在内存中能大大提高系统的性能和响应速度,满足实时性要求高的应用场景,如实时数据处理、高频交易等。
避免磁盘 I/O 瓶颈:传统的基于磁盘存储的数据库系统,在处理大量数据的读写操作时,磁盘 I/O 往往成为性能瓶颈。而 Redis 将数据存储在内存中,完全避免了磁盘 I/O 操作,从而能够在高并发场景下快速地响应大量的请求,实现高性能的数据处理。
功能需求
支持复杂数据结构:Redis 支持多种复杂的数据结构,如字符串、哈希表、列表、集合、有序集合等。这些数据结构需要在内存中进行快速的创建、修改和查询操作,这样才能更好的发挥其优势
数据时效性要求:对于一些对数据时效性要求较高的应用,如缓存系统,数据需要在短时间内快速更新和查询
设计目标
简单高效的架构:Redis 的设计目标之一是提供一个简单、高效、易于使用的内存数据库。
适应内存成本下降趋势
数据一致性保证
内存数据管理优势:由于数据都在内存中,Redis 可以更方便地对数据进行管理和监控,及时发现和处理数据的异常情况,如内存泄漏、数据冲突等,进一步保证了数据的一致性和系统的稳定性。
6. 请说一下使用 Redis 的优点
Redis 将数据存储在内存中,内存的读写速度远远快于磁盘,这使得 Redis 能够以极快的速度处理数据的读写请求。
其读操作通常可以在微秒级别内完成,写操作也非常迅速,能够轻松应对高并发场景下的大量请求,大大提高了系统的性能和响应速度。
Redis 支持多种高效的数据结构,如字符串、哈希表、列表、集合、有序集合等,能够在内存中快速地进行创建、查询、修改和删除操作,进一步提升了数据处理的效率
单线程与多路复用:Redis 采用单线程模型,避免了多线程之间的上下文切换和资源竞争问题,使得其在处理并发请求时更加高效
原子操作与事务:Redis 的原子操作和事务机制保证了在高并发环境下数据的一致性和完整性。原子操作确保了对数据的操作要么全部成功,要么全部失败,不会出现部分执行的情况。
RDB 与 AOF 持久化方式:Redis 提供了两种数据持久化方式,即 RDB 和 AOF。RDB 是在指定的时间间隔内将内存中的数据快照写入磁盘,生成一个二进制的压缩文件,适合用于数据的定期备份和恢复。AOF 则是将所有的写命令追加到文件中,通过重放 AOF 文件中的命令来恢复数据,能够提供更细粒度的持久化,保证数据的完整性和一致性。
数据可靠性提升:数据持久化功能使得 Redis 在服务器重启或意外故障时,能够快速地恢复数据,避免数据丢失,提高了数据的可靠性和可用性,满足了一些对数据持久性要求较高的应用场景的需求。
分布式架构实现:Redis 支持分布式架构,可以通过搭建 Redis 集群来实现数据的分布式存储和高可用性。Redis 集群将数据分散存储在多个节点上,通过数据分片和主从复制等机制,实现数据的负载均衡和故障转移,提高了系统的扩展性和容错能力,能够应对大规模的数据存储和高并发访问的需求。
横向扩展能力:随着业务的增长和数据量的增加,可以方便地向 Redis 集群中添加新的节点,实现系统的横向扩展,无需对现有系统进行大规模的修改和调整,降低了系统的维护成本和升级难度。
Redis 提供了简洁直观的命令行界面,通过简单的命令即可完成各种数据操作,如 SET、GET、HSET、HGET 等。
Redis 拥有多种编程语言的客户端库,如 Java、Python、C# 等,这些客户端库提供了与 Redis 交互的便捷接口
可能追问的问题
- 缓存命中率低时 Redis 还可能带来什么成本?
- 引入 Redis 后系统新增了哪些一致性问题?
- 如何证明缓存带来了实际收益?
7. 为什么多使用 Redis 作为 MySQL 的缓存?
因为其数据存在内存中,所以读写性能较强,并且 Redis 的QPS 远高于 MySQL ,所以使用 Redis 来做 MySQL的缓存,能够大大提升整个架构的性能
Redis 常用于 MySQL 前面的缓存,它能以较低延迟提供热点查询结果,减少数据库连接、SQL 执行和重复计算压力。两者通常分工合作:MySQL 保存权威数据,Redis 保存可重建副本。


8. Redis 中的字符串和 C 语言中的字符串有什么不同?
Redis 使用 SDS(Simple Dynamic String,简单动态字符串)表示字符串。与传统 C 字符串相比,主要有以下区别:
- 获取长度更快
- C 字符串通过
\0判断结束,使用strlen()获取长度需要遍历字符串,时间复杂度为 O(n)。 - SDS 保存了字符串的字节长度,可以直接读取,时间复杂度为 O(1)。
- C 字符串通过
- 支持二进制数据
- C 字符串遇到
\0就认为字符串结束,无法通过常规字符串函数完整处理包含\0的数据。 - SDS 根据记录的长度处理数据,中间出现
\0也不会被截断,因此是二进制安全的,既能保存文本,也能保存图片、序列化对象等二进制数据。
- C 字符串遇到
- 减少内存重新分配
- SDS 扩容时可以预留额外空间,减少连续追加内容时的内存分配次数。
- 缩短字符串时,可以保留已有容量,让空闲空间供后续使用;需要时也可以主动释放。
- 降低缓冲区溢出的风险
- 使用 C 字符串追加内容时,调用者需要自行保证缓冲区足够大,否则可能发生越界写入。
- SDS 的追加等操作会先检查可用空间,空间不足时自动扩容。
需要注意:SDS 末尾也保留了\0,以兼容部分 C 字符串函数,但它依靠记录的长度确定数据范围,而不是依靠\0

9. 说一下 Redis 的线程模型
Redis 使用单线程模型来处理客户端请求(下图黄框的内容)常见键值命令主要在主线程顺序执行;整个 Redis 进程并不是只有一个线程。
这意味着 Redis 主要通过线程来处理所有的客户端请求,而不是像传统的多线程服务器那样为每个客户端连接分配一个线程。这种单线程模型有以下几个特点:
简单高效:单线程模型使得 Redis 的实现相对简单,减少了线程切换的开销,提高了性能。
事件驱动:Redis 使用事件驱动模型,通过监听文件描述符的变化来处理客户端请求。这种模型在处理大量并发连接时表现出色。
非阻塞IO:Redis 使用非阻塞IO来实现异步处理多个客户端请求,提高了系统的并发性能。

10. Redis 如何处理客户端连接超时?
需要区分服务端空闲连接超时和客户端请求超时:
- 服务端:通过
timeout关闭空闲连接timeout的单位是秒,默认值为0,表示不主动关闭空闲连接。- 设置为非零值后,Redis 会定期检查客户端的空闲时间,关闭超过阈值的普通连接。由于是定期检查,关闭时间可能略晚于配置值。
- 该配置不适用于 Pub/Sub 订阅连接,因为长时间等待消息属于正常行为。
- timeout 限制的是连接空闲时间,不是命令执行时间。
- 客户端:配置连接超时和读取超时
- 连接超时:限制建立连接的最长等待时间。
- 读取超时:限制等待服务端响应的时间,避免因网络异常或响应过慢而长时间阻塞。
- 超时后,客户端应按连接库的机制处理异常、清理连接并按需重连。重试写操作时需要考虑幂等性,因为客户端超时不代表服务端没有执行成功。
服务端 timeout 用于回收空闲连接,客户端超时配置用于控制请求的等待时间,两者需要分别配置。
11. 简述 Redis 中的对象共享机制及其应用场景
Redis 的对象共享机制是指:让多个引用指向同一个只读对象,减少重复创建对象带来的内存占用和分配开销。
主要包括:
- 共享小整数对象
- 以 Redis 7.2 为例,Redis 启动时会预先创建
0~9999的整数对象。 - 在满足共享条件时,多个键的整数值可以引用同一个对象。例如,多个键的值都是
100,就可以复用表示100的共享对象。 - 共享对象不会被直接修改;修改某个键的值时,会使用独立对象或替换引用,不会影响其他键。
- 以 Redis 7.2 为例,Redis 启动时会预先创建
- 共享内部常用对象
- Redis 会复用常见的协议回复对象,例如
OK、常见错误信息等,减少处理请求时重复创建对象的开销。
- Redis 会复用常见的协议回复对象,例如
应用场景:大量键保存重复的小整数值,例如状态码、标志位等;以及服务端频繁返回相同的协议回复。
需要注意:
- Redis 不会自动共享任意相同的业务短字符串,
embstr是紧凑存储编码,不是对象共享。 - 小整数共享受版本和配置影响。例如 Redis 7.2 配置了
maxmemory并使用 LRU/LFU 淘汰策略时,键值需要独立记录访问时间或频率,因此不会使用共享整数对象。
12. Redis 内存碎片率如何计算?过高时如何优化?
什么是内存碎片?
内存碎片是指内存分配和释放过程中,产生了无法被充分利用的空间。主要分为两类:
内部碎片:分配的内存块大于实际需要的大小。例如,申请 100 字节,分配器分配了 112 字节,多出的 12 字节就是内部碎片。
为什么申请 100 会分配 112 呢?因为内存分配器通常按固定的“大小档位”分配内存,而不是申请多少就精确分配多少。
例如,在 jemalloc 官方文档列出的配置下,小内存的档位包含:
80 → 96 → 112 → 128 字节申请 100 字节时,96 字节装不下,分配器就选择能容纳它的下一档 112 字节。多出的 12 字节没有被这次请求利用,就是内部碎片。采用这种方式,是为了方便分配器管理和复用同样大小的内存块,提高分配、释放效率。
外部碎片:反复分配和释放后,空闲空间分散在不同位置,难以满足新的分配需求。例如,一些内存页中只剩少量存活对象,其余空间虽然空闲,但整页仍无法归还操作系统。
Redis 中,键值频繁更新、删除、过期,以及对象大小变化,都可能产生碎片,使实际占用的物理内存高于 Redis 分配的内存。
内存碎片率如何计算?
通过 INFO memory 查看,计算公式为:
mem_fragmentation_ratio = used_memory_rss / used_memory
used_memory_rss:操作系统统计的 Redis 进程实际驻留在物理内存中的大小。used_memory:Redis 通过内存分配器分配的内存,包括业务数据和内部管理开销。
例如,used_memory_rss 为 1.5 GB,used_memory 为 1 GB,则碎片率为 1.5。
这个比值不是纯粹的内存碎片指标。它还受进程其他内存开销、分配器保留的空闲内存等因素影响。数据量很小时,即使额外内存不多,比值也可能很高。
碎片率过高时如何优化?
- 先确认原因
- 结合
mem_fragmentation_bytes,判断额外内存的绝对大小。 - 查看
allocator_frag_ratio和allocator_frag_bytes,进一步判断分配器内部的碎片程度。 - 大量删除数据后,RSS 不一定立即下降,分配器保留的空闲内存仍可能被后续写入复用。
- 结合
- 开启主动碎片整理
- Redis 4.0 起提供主动碎片整理功能,在构建和分配器支持的情况下,可配置
activedefrag yes。 - Redis 会逐步搬迁分散的内存分配,帮助释放内存页。
- 需要合理设置触发阈值和 CPU 使用上限,并观察请求延迟。
- Redis 4.0 起提供主动碎片整理功能,在构建和分配器支持的情况下,可配置
- 释放分配器保留的空闲内存
- 使用 jemalloc 时,可以尝试
MEMORY PURGE,请求分配器将可释放的空闲页归还操作系统。 - 它不等同于碎片整理,不能直接解决仍有存活对象占用的内存页碎片,执行时也可能带来延迟。
- 使用 jemalloc 时,可以尝试
- 优化数据写入模式
- 减少大小差异较大的对象反复创建、删除,以及对象频繁扩缩容,降低碎片产生的机会。
- 持续监控 RSS、碎片率、碎片字节数和请求延迟,评估优化效果。
- 必要时滚动重启
- 如果碎片严重且在线整理效果有限,可以在确保数据可恢复的前提下,通过主从切换、滚动重启并重新加载数据缓解碎片。
- 重启是重新构建内存布局,并不是让操作系统分配连续的物理内存,也不能保证以后不再产生碎片。

13. 如何查看 Redis 所有配置项?动态修改配置有哪些方式?
使用 CONFIG GET 命令
可以在 Redis 客户端使用 CONFIG GET 命令来查看配置项。如果要查看所有配置项,使用通配符 *,示例如下:
redis-cli CONFIG GET *
执行该命令后,Redis 会返回所有配置项及其当前的值。这个命令适用于我们想了解 Redis 详细配置信息的场景,不过返回的信息较多,需要仔细筛选和查看。
查看配置文件
Redis 默认的配置文件是 redis.conf,可以直接查看这个文件来获取配置信息。使用以下命令可以查看配置文件内容:
cat /path/to/redis.conf
需要注意的是,配置文件中的配置项可能和实际运行时的配置有差异,因为有些配置可以通过命令动态修改。查看配置文件有助于我们了解 Redis 启动时的初始配置情况。
动态修改配置的方式
1. 使用 CONFIG SET 命令
可以在 Redis 客户端使用 CONFIG SET 命令动态修改配置项。例如,要修改 Redis 的最大内存限制,可以使用以下命令:
redis-cli CONFIG SET maxmemory 200mb
这个命令会立即修改 Redis 的运行时配置,但是这种修改是临时的,Redis 重启后配置会恢复到配置文件中的设置。此方式适用于需要临时调整配置来进行测试或者应对突发情况的场景。
2. 修改配置文件并重启 Redis
先编辑 redis.conf 配置文件,修改相应的配置项。例如,将 port 配置项从默认的 6379 修改为 6380:
vim /path/to/redis.conf
# 将 port 6379 修改为 port 6380
修改完成后,重启 Redis 服务,使新的配置生效:
systemctl restart redis
使用 Redis 管理工具
一些 Redis 管理工具,如 RedisInsight,提供了图形化的界面来查看和修改配置。在 RedisInsight 中,可以连接到 Redis 实例,然后在配置管理界面中直接修改配置项。这种方式对于不熟悉命令行操作或者需要直观查看和修改配置的用户比较友好。
14. 从网络连接角度看,Redis 单线程模型有哪些潜在瓶颈?如何应对?
Redis 使用 I/O 多路复用和非阻塞 Socket 管理大量客户端连接,由主线程按事件循环处理就绪的网络事件和命令。
因此,连接数量多并不一定会造成性能问题,真正的瓶颈通常来自以下几个方面。
潜在瓶颈
网络 I/O 处理能力受限
当请求量很大、请求或响应数据较大时,主线程需要执行大量读取、协议解析和响应写入操作,可能消耗较多 CPU,无法充分利用多核处理器,进而导致请求排队和延迟升高。
频繁建立和关闭连接
短连接会反复产生 TCP 握手、连接初始化和资源回收开销。连接突增时,还可能受到
maxclients、文件描述符数量和tcp-backlog等限制,导致新连接被拒绝。大请求和大响应占用事件循环
大 Key 的读写、一次返回大量数据,以及规模过大的 Pipeline,都会增加网络传输、协议解析和响应组装的时间,使其他连接的请求等待更久。
慢客户端造成输出缓冲区积压
Redis 使用非阻塞 I/O,因此某个客户端网络延迟较高时,通常不会直接阻塞其他连接。但如果客户端消费响应的速度低于 Redis 产生响应的速度,数据会积压在客户端输出缓冲区中:
- 占用大量内存;
- 增加事件循环处理负担;
- 达到输出缓冲区限制后,连接会被 Redis 关闭。
这种问题在 Pub/Sub、复制连接以及大结果集场景中更加常见。
单线程命令执行造成排队
即使网络层使用了非阻塞 I/O,命令仍主要由主线程串行执行。如果出现时间复杂度较高的命令、Lua 脚本或大 Key 操作,主线程会被长时间占用,所有客户端都会受到影响。
应对方式
复用连接
使用长连接和连接池,减少频繁建立、关闭连接的开销。连接池大小应根据业务并发量合理设置,连接过多反而会增加 Redis 和操作系统的资源消耗。
使用 Pipeline 或批量命令
将多个请求批量发送,减少网络往返次数,提高吞吐量。但要控制每批命令的数量和数据大小,避免一次处理过多请求而长时间占用主线程。

Pipeline减少网络往返 启用 I/O 多线程
Redis 6.0 开始支持 I/O 多线程,可以将部分网络读写工作交给多个线程处理,缓解大流量场景下的网络 I/O 压力。
需要注意,命令执行仍主要由主线程完成。如果瓶颈来自慢命令或 CPU 计算,增加 I/O 线程的效果有限。
控制大 Key 和大结果集
避免一次读写过大的数据,使用
SCAN代替KEYS,对集合数据进行分页或分批处理,同时控制 Pipeline 的批次大小。限制慢客户端的缓冲区
合理设置
client-output-buffer-limit和maxmemory-clients,防止慢客户端持续占用内存;结合CLIENT LIST中的omem等指标识别输出缓冲区异常的连接。调整连接和系统参数
根据实际并发量合理配置:
maxclients;- 操作系统文件描述符上限;
tcp-backlog;- TCP Keepalive;
- 客户端连接、读取和写入超时。
横向扩展
如果单个 Redis 实例已经达到 CPU 或网络上限,可以使用 Redis Cluster 或业务分片,将请求和网络流量分散到多个实例。
总结:Redis 的非阻塞 I/O 可以高效管理大量连接,单个慢连接通常不会直接阻塞整个服务。
主要风险是网络读写量过大、频繁创建连接、大请求或大响应、慢客户端缓冲区积压,以及慢命令占用主线程。可以通过连接复用、Pipeline、I/O 多线程、缓冲区限制和集群分片等方式优化。
15. Redis 对网络 I/O 模型做了哪些优化?
Redis 主要通过以下方式优化网络 I/O
非阻塞 I/O
Redis 会将客户端 Socket 设置为非阻塞模式。执行网络读写时,如果数据暂时不可读或不可写,线程不会一直等待,而是继续处理其他客户端的请求。
I/O 多路复用
Redis 使用 I/O 多路复用机制,同时监听大量客户端连接。Linux 下通常使用 epoll,其他系统还可能使用 kqueue、select 等机制。
只有当某个 Socket 出现连接、可读或可写事件时,Redis 才会处理对应连接,避免为每个连接创建一个线程,从而降低线程创建、上下文切换和内存开销。
基于事件循环的 Reactor 模型
Redis 将不同的网络事件注册到事件循环中,并绑定相应的处理函数:
- 监听 Socket 产生连接事件时,接受客户端连接;
- 客户端 Socket 产生可读事件时,读取并解析命令;
- 命令执行完成后生成响应;
- Socket 可写时,将响应发送给客户端。
Redis 还会控制单次读取行为,避免少数高流量客户端长期占用事件循环。
单线程执行命令
传统 Redis 由主线程处理网络事件和执行命令。命令串行执行具有以下优势:
- 避免多线程操作共享数据产生的锁竞争;
- 减少线程上下文切换;
- 单条命令天然具有原子性;
- 实现相对简单,适合 Redis 以内存操作为主的场景。
需要注意,Redis 并非所有工作都由一个线程完成,持久化、异步释放内存等任务可能由后台线程或子进程完成。
I/O 多线程
Redis 6.0 开始支持 I/O 多线程,可以让多个线程分担客户端网络数据的读取和响应发送,缓解主线程在大流量、大请求和大响应场景下的网络 I/O 压力。
命令执行仍主要由主线程串行完成,因此 I/O 多线程不会破坏命令的原子性。如果瓶颈来自慢命令或复杂计算,启用 I/O 多线程的提升有限。
Pipeline 减少网络往返
客户端可以通过 Pipeline 一次发送多条命令,Redis 批量读取并依次处理,再将结果批量返回,从而减少网络往返次数和系统调用开销,提高吞吐量。
Pipeline 的批次不能过大,否则可能长时间占用主线程,并增加客户端输入、输出缓冲区的内存占用。
TCP 和缓冲区优化
Redis 会为连接设置 TCP_NODELAY,减少小数据包因 Nagle 算法产生的发送延迟。同时,Redis为每个客户端维护输入和输出缓冲区,在暂时无法完成读写时保存数据,并通过事件循环继续处理。
对于消费速度较慢的客户端,可以使用 client-output-buffer-limit 限制输出缓冲区,避免其持续增长并占用大量内存。
关于连接池
连接池属于客户端侧优化,不是 Redis 服务端 I/O 模型的一部分。它可以复用已经建立的连接,减少 TCP 握手和连接初始化开销。
连接池大小需要合理设置。连接太少可能导致业务线程等待,连接过多则会增加 Redis 的连接管理、文件描述符和内存开销。
总结:Redis 通过非阻塞 I/O、I/O 多路复用、事件驱动、单线程命令执行以及 I/O 多线程,实现了较高的网络并发能力;客户端还可以通过连接池和 Pipeline 进一步减少连接及网络往返开销。
阅读导航
下一章:Redis 数据结构




