跳转到主要内容

常见问题

KeyDB 与 Redis 有何不同?#

KeyDB 最初是 Redis 的一个分支,因为我们的基本理念不同。我们认为 Redis 应该是多线程的,而 Redis 的核心开发团队并没有支持它的计划。这个分支的初衷是为了加速我们和其他用户感兴趣的领域的发展。使用 Redis 的某些方面会带来很多不必要的复杂性,例如分片和高可用性设置。因此,KeyDB 引入了主动复制和多主配置等功能。其他功能,如闪存支持、AWS S3 备份,也是我们认为应该成为基础代码和每个实例标配的重要功能。

KeyDB 主要用 C++ 编写,这使得该项目能够加速到另一个层次。我们计划继续添加其他备受追捧的功能,并将在发布时宣布这些功能。在性能方面,KeyDB 处于领先地位,您可以期待 KeyDB 产品将随着最新的硬件和软件的出现而不断适应和利用。

与其他大多数键值存储相比,KeyDB 有何不同?#

主要有两个原因。

  • KeyDB 是键值数据库中一条不同的演进路径,其值可以包含更复杂的数据类型,并对这些数据类型定义了原子操作。KeyDB 的数据类型与基本数据结构密切相关,并以这种方式直接暴露给程序员,没有额外的抽象层。
  • KeyDB 是一个内存数据库,但数据会持久化到磁盘,因此它代表了一种不同的权衡。通过将数据集限制在不超过内存大小的范围内,实现了非常高的读写速度。内存数据库的另一个优点是,复杂数据结构的内存表示形式比磁盘上的相同数据结构更容易操作,因此 KeyDB 能够用很小的内部复杂性完成很多工作。同时,两种磁盘存储格式(RDB 和 AOF)不需要支持随机访问,所以它们非常紧凑,并且总是以仅追加的方式生成(即使 AOF 日志轮换也是一个仅追加操作,因为新版本是从内存中的数据副本生成的)。然而,与传统的磁盘存储相比,这种设计也带来了不同的挑战。由于主要数据表示在内存中,KeyDB 的操作必须小心处理,以确保磁盘上始终有数据集的更新版本。

KeyDB 的内存占用情况如何?#

举几个例子(所有例子均在 64 位实例上获得):

  • 一个空实例大约使用 3MB 内存。
  • 100 万个小的键 -> 字符串值对大约使用 85MB 内存。
  • 100 万个键 -> 哈希值(代表一个有 5 个字段的对象)大约使用 160MB 内存。

测试您的用例非常简单。使用 `KeyDB-benchmark` 工具生成随机数据集,然后使用 `INFO memory` 命令检查所用空间。

64 位系统存储相同的键值对会比 32 位系统使用更多的内存,特别是当键和值很小的时候。这是因为在 64 位系统中指针占用 8 个字节。但其优点当然是您可以在 64 位系统中使用大量内存,因此要运行大型 KeyDB 服务器,64 位系统基本上是必需的。另一种选择是分片。

我喜欢 KeyDB 的高级操作和功能,但我不喜欢它将所有东西都保存在内存中,导致数据集不能大于内存。有改变这个的计划吗?#

“KeyDB on flash”是一个能够为较大数据集使用混合 RAM/闪存方法的解决方案。DRAM 每 GB 的成本明显高于像闪存这样的非易失性存储器。启用此功能后,KeyDB 可以将不常访问的数据存储在非易失性存储中,而不是 RAM 中。KeyDB 会根据需要主动将数据在非易失性存储中换入换出。当然,您也可以使用普通的机械硬盘,但不推荐,因为性能会很差。KeyDB 期望底层设备具有良好的随机 I/O 性能。

闪存存储仅用于临时数据,其行为类似于 RAM。持久性仍然通过常规机制实现。当使用 'free' 命令查看内存时,KeyDB 使用的内存会显示为 "buff/cache"。KeyDB 依赖内核的分页策略来决定将什么内容放到磁盘上。

将 KeyDB 与磁盘数据库一起使用是个好主意吗?#

是的,一个常见的设计模式是将写入密集型的小数据存储在 KeyDB 中(以及那些需要用 KeyDB 数据结构来高效建模问题的数据),而将大的*二进制大对象*(blobs)存储在 SQL 或最终一致性的磁盘数据库中。类似地,有时 KeyDB 也被用来在内存中保存一份存储在磁盘数据库中部分数据的副本。这可能看起来像缓存,但实际上是一个更高级的模型,因为通常 KeyDB 数据集与磁盘数据库数据集是同步更新的,而不是在缓存未命中时刷新。

有什么办法可以降低 KeyDB 的内存使用量吗?#

如果可以,请使用 KeyDB 32 位实例。另外,善用小的哈希、列表、有序集合和整数集合,因为 KeyDB 能够以更紧凑的方式表示这些包含少量元素的数据类型。更多信息请参见内存优化页面

如果 KeyDB 内存耗尽会发生什么?#

KeyDB 要么会被 Linux 内核的 OOM killer 杀死,要么会因错误而崩溃,要么会开始变慢。在现代操作系统中,malloc() 返回 NULL 的情况并不常见,通常服务器会开始交换(如果配置了交换空间),KeyDB 的性能会开始下降,所以你可能会注意到出了问题。

KeyDB 有内置保护机制,允许用户通过配置文件中的 `maxmemory` 选项设置内存使用上限,以限制 KeyDB 可以使用的内存。如果达到此限制,KeyDB 将开始对写入命令返回错误(但仍会接受只读命令),或者您可以配置它在达到最大内存限制时驱逐键,这适用于您将 KeyDB 用作缓存的情况。

如果您计划将 KeyDB 用作 LRU 缓存,我们有详细的文档。

`INFO` 命令报告 KeyDB 正在使用的内存量,因此您可以编写脚本来监控您的 KeyDB 服务器,在达到临界条件之前进行检查。

即使我有很多空闲内存,在 Linux 下后台保存也会因 fork() 错误而失败!#

简短回答:`echo 1 > /proc/sys/vm/overcommit_memory` :)

下面是详细解释

KeyDB 的后台保存机制依赖于现代操作系统中 fork 的写时复制(copy-on-write)语义:KeyDB fork(创建一个子进程),该子进程是父进程的精确副本。子进程将数据库转储到磁盘,然后退出。理论上,子进程作为副本应该使用与父进程一样多的内存,但实际上,由于大多数现代操作系统实现的写时复制语义,父子进程将*共享*公共内存页。只有当子进程或父进程中的页面发生更改时,该页面才会被复制。由于理论上在子进程保存期间所有页面都可能发生变化,Linux 无法预先知道子进程将占用多少内存,因此如果 `overcommit_memory` 设置为零,除非有足够的空闲内存来真正复制所有父进程的内存页,否则 fork 将会失败。结果是,如果你有一个 3 GB 的 KeyDB 数据集,而只有 2 GB 的空闲内存,它就会失败。

将 `overcommit_memory` 设置为 1 会告诉 Linux 放宽限制,以更乐观的分配方式执行 fork,这正是 KeyDB 所需要的。

要了解 Linux 虚拟内存的工作原理以及 `overcommit_memory` 和 `overcommit_ratio` 的其他替代方案,一个很好的资源是红帽杂志的这篇经典文章:“理解虚拟内存”。您也可以参考 proc(5) 手册页来了解可用值的解释。

KeyDB 的磁盘快照是原子的吗?#

是的,KeyDB 后台保存进程总是在服务器不在执行命令时进行 fork,因此每个在 RAM 中被报告为原子的命令,从磁盘快照的角度来看也是原子的。

单个 KeyDB 实例最多能容纳多少个键?哈希、列表、集合、有序集合中的元素最大数量是多少?#

KeyDB 最多可以处理 2^32 个键,并且在实践中测试过每个实例至少可以处理 2.5 亿个键。

每个哈希、列表、集合和有序集合都可以容纳 2^32 个元素。

换句话说,你的限制很可能是你系统中的可用内存。

为什么我的副本声称其键数与主节点不同?#

如果你使用了有生存时间限制的键(KeyDB 过期),这是正常行为。情况如下:

  • 主节点在与副本首次同步时会生成一个 RDB 文件。
  • RDB 文件不会包含在主节点中已经过期但仍在内存中的键。
  • 然而,这些键仍然存在于 KeyDB 主节点的内存中,尽管它们在逻辑上已经过期。它们不会被视作存在,但内存会在稍后以增量和显式访问的方式被回收。然而,尽管这些键在逻辑上不属于数据集的一部分,它们仍然会在 `INFO` 输出和 `DBSIZE` 命令中被通告。
  • 当副本读取由主节点生成的 RDB 文件时,这组键将不会被加载。

因此,有许多设置了过期时间的键的用户经常会看到副本中的键更少,这是由于这个现象造成的,但在实例内容上没有实际的逻辑差异。