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/闪存方法。您可以查看他们的产品了解更多信息,但此功能不是 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
系统调用的写时复制 (copy-on-write) 语义:Redis 通过 fork(创建一个子进程)来精确复制父进程。子进程将数据库 dump 到磁盘,最后退出。理论上,子进程作为副本应该占用与父进程相同的内存量,但实际上,得益于大多数现代操作系统实现的写时复制语义,父子进程将共享公共内存页面。只有当页面在子进程或父进程中发生变化时,才会被复制。由于理论上子进程保存期间所有页面都可能发生变化,Linux 无法提前知道子进程将占用多少内存,因此如果 overcommit_memory
设置为零,除非有足够的空闲 RAM 来真正复制所有父进程内存页面,否则 fork 将失败。如果您有一个 3GB 的 Redis 数据集但只有 2GB 空闲内存,它将失败。
将 overcommit_memory
设置为 1 会告诉 Linux 放宽限制,并以更乐观的分配方式执行 fork,这正是 Redis 所需要的。
您可以参考 proc(5) 手册页,了解可用值的解释。
Redis 磁盘快照是原子性的吗?
是的,Redis 后台保存过程总是在服务器不在执行命令时被 fork 出来,因此在 RAM 中报告为原子性的每个命令,从磁盘快照的角度看也是原子性的。
Redis 如何使用多个 CPU 或核心?
CPU 很少成为 Redis 的瓶颈,因为通常 Redis 受限于内存或网络。例如,在使用 pipelining 时,运行在普通 Linux 系统上的 Redis 实例每秒可以处理 100 万次请求,所以如果您的应用主要使用 O(N) 或 O(log(N)) 命令,它几乎不会占用过多的 CPU。
然而,为了最大化 CPU 使用率,您可以在同一台机器上启动多个 Redis 实例,并将它们视为不同的服务器。在某些时候,单台机器可能不够用,所以如果您想使用多个 CPU,可以提前开始考虑某种分片方式。
您可以在分区页面中找到关于使用多个 Redis 实例的更多信息。
从 4.0 版本开始,Redis 已经开始实现线程化操作。目前,这仅限于后台删除对象和通过 Redis modules 实现的阻塞命令。未来的版本计划让 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" 的发音。