Redis 常见问题解答
在开始使用 Redis 时经常遇到的问题
Redis 与其他键值存储有何不同?
- Redis 在键值数据库中具有不同的演化路径,其中值可以包含更复杂的数据类型,并针对这些数据类型定义了原子操作。Redis 数据类型与基本数据结构密切相关,并以这种方式向程序员公开,而无需额外的抽象层。
- Redis 是一个内存中但持久化到磁盘的数据库,因此它代表了一种不同的权衡,其中通过限制不能大于内存的数据集来实现非常高的写入和读取速度。内存中数据库的另一个优势是,与磁盘上的相同数据结构相比,内存中复杂数据结构的表示更容易操作,因此 Redis 可以用很少的内部复杂性完成很多工作。同时,两种磁盘存储格式(RDB 和 AOF)不需要适用于随机访问,因此它们是紧凑的,并且始终以追加方式生成(即使 AOF 日志轮换也是追加操作,因为新版本是根据内存中数据的副本生成的)。但是,与传统的磁盘存储相比,此设计也涉及不同的挑战。由于内存是主要的数据表示,因此必须仔细处理 Redis 操作,以确保磁盘上始终有数据集的更新版本。
Redis 的内存占用是多少?
以下列出几个示例(均使用 64 位实例获取)
- 空实例使用约 3MB 内存。
- 100 万个小键 -> 字符串值对使用约 85MB 内存。
- 100 万个键 -> 哈希值,表示具有 5 个字段的对象,使用约 160 MB 内存。
测试用例非常简单。使用 redis-benchmark
实用程序生成随机数据集,然后使用 INFO memory
命令检查所使用的空间。
64 位系统将比 32 位系统使用更多内存来存储相同的键,尤其是当键和值较小时。这是因为在 64 位系统中,指针占用 8 个字节。但当然,64 位系统中的优势在于可以拥有大量内存,因此要运行大型 Redis 服务器,或多或少需要一个 64 位系统。另一种选择是分片。
为什么 Redis 将其整个数据集保存在内存中?
过去,Redis 开发人员尝试使用虚拟内存和其他系统来允许大于 RAM 的数据集,但最终,如果我们能做好一件事,我们就会非常高兴:从内存中提供数据,使用磁盘进行存储。因此,目前还没有为 Redis 创建磁盘后端的计划。毕竟,Redis 的大部分内容都是其当前设计的结果。
如果你的实际问题不是所需的总 RAM,而是需要将数据集拆分为多个 Redis 实例,请阅读此文档中的 分区页面 以获取更多信息。
赞助 Redis 开发的 Redis Ltd. 公司开发了一种“Redis on Flash”解决方案,该解决方案使用混合 RAM/flash 方法来处理具有偏差访问模式的大型数据集。你可以查看他们的产品以获取更多信息,但此功能不属于开源 Redis 代码库的一部分。
你能将 Redis 与基于磁盘的数据库一起使用吗?
是的,一种常见的设计模式涉及在 Redis 中获取非常繁重的写入小数据(以及你需要 Redis 数据结构以有效方式对问题建模的数据),并将大数据块放入 SQL 或最终一致的磁盘数据库中。类似地,有时使用 Redis 来获取磁盘数据库中存储的同一数据子集的另一个副本。这可能看起来类似于缓存,但实际上是一个更高级的模型,因为通常 Redis 数据集与磁盘数据库数据集一起更新,而不是在缓存未命中时刷新。
如何减少 Redis 的总体内存使用量?
在将逻辑数据模型映射到 Redis 中的物理数据模型时,考虑内存消耗是一种好做法。这些考虑因素包括使用特定数据类型、键模式和规范化。
除了数据建模之外,内存优化页面 中还有更多信息。
如果 Redis 内存耗尽会发生什么?
Redis 具有内置保护,允许用户使用配置文件中的 maxmemory
选项设置内存使用量的最大限制,以限制 Redis 可以使用的内存。如果达到此限制,Redis 将开始对写入命令回复错误(但将继续接受只读命令)。
您还可以配置 Redis,以便在达到最大内存限制时逐出键。有关此信息的更多内容,请参阅逐出策略文档。
后台保存失败,Linux 上出现 fork() 错误?
简短回答:echo 1 > /proc/sys/vm/overcommit_memory
:)
现在是长答案
Redis 后台保存架构依赖于现代操作系统中 fork
系统调用的写时复制语义:Redis 分叉(创建一个子进程),该子进程是父进程的精确副本。子进程将数据库转储到磁盘,最后退出。理论上,子进程应该使用与父进程一样多的内存,因为它是一个副本,但实际上,由于大多数现代操作系统实现的写时复制语义,父进程和子进程将共享公共内存页。只有当子进程或父进程中的页面发生更改时,才会复制页面。由于理论上所有页面都可能在子进程保存时发生更改,因此 Linux 无法预先判断子进程将占用多少内存,因此如果 overcommit_memory
设置为零,则 fork 将失败,除非有足够的可用 RAM 来真正复制所有父内存页。如果您有 3 GB 的 Redis 数据集和仅 2 GB 的可用内存,它将失败。
将 overcommit_memory
设置为 1 告诉 Linux 放松并以更乐观的分配方式执行 fork,而这正是 Redis 所需的。
你可以参考 proc(5) 手册页以了解可用值的说明。
Redis 磁盘快照是否原子化的?
是的,当服务器不在命令执行之外时,Redis 后台保存进程总是 fork,因此在 RAM 中报告为原子化的每个命令从磁盘快照的角度来看也是原子化的。
Redis 如何使用多个 CPU 或内核?
CPU 成为 Redis 瓶颈的情况并不常见,因为通常 Redis 受限于内存或网络。例如,在使用管道时,在普通 Linux 系统上运行的 Redis 实例每秒可以传递 100 万个请求,因此如果你的应用程序主要使用 O(N) 或 O(log(N)) 命令,那么它不太可能使用过多的 CPU。
但是,为了最大化 CPU 使用率,你可以在同一台机器上启动多个 Redis 实例,并将它们视为不同的服务器。在某个时刻,一台机器可能不够用,因此如果你想使用多个 CPU,你可以开始考虑更早进行分片。
你可以在 分区页面 中找到有关使用多个 Redis 实例的更多信息。
从 4.0 版本开始,Redis 已开始实现线程化操作。目前,这仅限于在后台删除对象和通过 Redis 模块实现的阻塞命令。对于后续版本,计划是让 Redis 变得越来越线程化。
单个 Redis 实例可以容纳的最大键数是多少?哈希、列表、集合和有序集合中的最大元素数是多少?
Redis 最多可以处理 2^32 个键,并且在实践中经过测试,每个实例至少可以处理 2.5 亿个键。
每个哈希、列表、集合和有序集合可以容纳 2^32 个元素。
换句话说,你的限制可能是系统中可用的内存。
为什么我的副本的键数与它的主实例不同?
如果你使用具有有限生存时间的键(Redis 过期),这是正常行为。以下是发生的情况
- 主服务器在与副本的第一次同步时生成一个 RDB 文件。
- RDB 文件将不包括主服务器中已过期但在内存中仍然存在的键。
- 即使在逻辑上已过期,这些键仍然保留在 Redis 主节点的内存中。它们将被视为不存在,并且它们的内存将稍后被回收,无论是在增量上还是在访问时显式回收。虽然这些键在逻辑上不属于数据集,但它们会被计入
INFO
输出和DBSIZE
命令中。 - 当副本读取主节点生成的 RDB 文件时,这组键将不会被加载。
因此,对于具有许多已过期键的用户来说,在副本中看到更少的键是很常见的。然而,在逻辑上,主节点和副本将具有相同的内容。
“Redis”这个名字从何而来?
Redis 是一个缩写,代表REmote DIctionary Server(远程字典服务器)。
Salvatore Sanfilippo 为什么启动 Redis 项目?
Salvatore 最初创建 Redis 是为了扩展 LLOOGG,一个实时日志分析工具。但在让基本的 Redis 服务器工作之后,他决定与其他人分享这项工作并将 Redis 变成一个开源项目。
Redis 如何发音?
“Redis” (/ˈrɛd-ɪs/) 的发音类似于单词“red”加上单词“kiss”,但没有“k”。