重要说明: Redis VM 现已弃用。Redis 2.4 将是支持虚拟内存的最新 Redis 版本(但它也警告你虚拟内存的使用是不被鼓励的)。我们发现使用 VM 有几个缺点和问题。在 Redis 的未来,我们希望简单地提供有史以来最好的内存数据库(但像往常一样持久化到磁盘),至少目前不考虑支持大于 RAM 的数据库。我们未来的努力集中在提供脚本、集群和更好的持久化上。

*虚拟内存

Redis 虚拟内存是一个功能,将在 Redis 2.0 的稳定发行版中首次出现。然而虚拟内存(从现在开始称为 VM)已经可用并且足够稳定,可以在 Redis 的不稳定分支中进行测试,可在 GitHub 上获取。

*用简单的话解释虚拟内存

Redis 遵循键-值模型。你有与某些值关联的键。通常 Redis 将键和关联的值都保存在内存中。有时这不是最佳选择,虽然键 必须 按设计保存在内存中(并且为了确保快速查找),但当值很少使用时,可以将它们交换到磁盘。

在实际操作中,这意味着如果你有 100,000 个键的数据集在内存中,但只有 10% 的键经常被使用,启用虚拟内存的 Redis 会尝试将很少使用的键关联的值转移到磁盘上。

当这些值被请求时,作为客户端发出的命令的结果,这些值会从交换文件加载回主内存。

*何时使用虚拟内存是个好主意

在使用 VM 之前,你应该问问自己是否真的需要它。Redis 是一个磁盘支持的内存数据库。使用 Redis 的正确方式几乎总是拥有足够的 RAM 来容纳内存中的所有数据。不过有些场景下这是不可能的:

  • 数据访问非常偏向。只有一小部分键(例如与你网站上活跃用户相关的键)获得了绝大多数访问。同时每个键的数据量太大,无法全部放入内存。
  • 无论数据访问模式如何,根本没有足够的内存来容纳所有数据,而且值很大。在这种配置中,Redis 可以用作磁盘上的数据库,键在内存中,因此键查找很快,但实际值的访问需要访问(较慢的)磁盘。

需要记住的一个重要概念是 Redis 不能交换键,所以如果你的内存问题与你有太多具有非常小值的键有关,VM 不是解决方案。

然而,如果使用了大量内存是因为值相当大(例如大字符串、列表、集合或具有许多元素的哈希),那么 VM 可能是个好主意。

有时你可以通过使用哈希将相关数据分组到单个键的字段中,将你的"许多具有小值的键"问题转化为"少数具有非常大值的键"问题。例如,不是为对象的每个属性都有一个键,而是每个对象有一个键,其中哈希字段代表不同的属性。

*VM 配置

配置 VM 并不难,但需要根据要求小心设置最佳参数。

VM 通过编辑 redis.conf 来启用和配置,第一步是用以下方式打开它:

vm-enabled yes

还有许多其他配置选项能够改变 VM 的行为。规则是你不想使用默认配置运行,因为每个问题和数据集都需要一些微调才能获得最大优势。

*vm-max-memory 设置

vm-max-memory 设置指定 Redis 在开始在磁盘上交换值之前可以自由使用多少内存。

基本上,如果未达到此内存限制,不会交换任何对象,Redis 会像往常一样在内存中使用所有对象工作。然而一旦达到此限制,就会交换出足够的对象,使内存恢复到限制以下。

被交换的对象主要是具有最高"年龄"的对象(即自它们上次使用以来的秒数),但对象的"可交换性"也与其在内存中大小的对数成正比。因此虽然优先选择较旧的对象,但当它们年龄相当时,较大的对象会先被交换出去。

警告: 因为键不能被交换出去,如果键单独使用的空间超过了限制,Redis 将无法遵守 vm-max-memory 设置。

此设置的最佳值是足以容纳数据"工作集"的 RAM。实际上,只需给 Redis 尽可能多的内存,交换就会更好地工作。

*配置交换文件

为了将数据从内存传输到磁盘,Redis 使用交换文件。交换文件与数据的持久性无关,当 Redis 实例终止时可以删除。然而,在 Redis 运行时,不应移动、删除或以任何其他方式更改交换文件。

因为 Redis 交换文件主要以随机访问方式使用,将交换文件放入固态磁盘将获得更好的性能。

交换文件被划分为"页"。一个值可以交换到一个或多个页中,但单个页不能容纳超过一个值。

没有直接的方法告诉 Redis 应该使用多少字节的交换文件。而是配置了两个不同的值,当它们相乘时将产生使用的总字节数。这两个值是交换文件中的页数和页大小。可以在 redis.conf 中配置这两个参数。

  • vm-pages 配置指令用于设置交换文件中的总页数。
  • vm-page-size 配置指令用于设置页大小(以字节为单位)。

因此,例如,如果页大小设置为 32 字节,总页数设置为 10000000(1000 万),那么交换文件可以容纳总共 320 MB 的数据。

因为单个页不能用于容纳超过一个值(但一个值可以存储到多个页中),在设置这些参数时必须小心。通常最好的想法是设置页大小,使大多数值可以使用几个页进行交换。

*线程化 VM 与阻塞式 VM

另一个非常重要的配置参数是 vm-max-threads

# 默认的 vm-max-threads 配置
vm-max-threads 4

这是用于执行与交换文件的 I/O 的最大线程数。一个好的值是匹配你系统中的核心数。

然而特殊值 "0" 将启用阻塞式 VM。当 VM 配置为阻塞式时,它以同步阻塞方式执行 I/O。这是阻塞式 VM 的预期行为:

  • 访问已交换出去的键的客户端会在从磁盘读取时阻塞其他客户端,因此客户端体验的延迟可能更大,特别是如果磁盘慢或繁忙,和/或磁盘上有很大的值被交换。
  • 阻塞式 VM 的 总体 性能更好,因为没有在同步、生成线程和恢复等待值的阻塞客户端上浪费时间。所以如果你愿意时不时接受更高的延迟,阻塞式 VM 可能是一个不错的选择。特别是如果交换很少发生,而且你经常访问的大部分数据恰好适合你的内存。

相反,如果你有很多换入和换出操作,并且你想利用许多核心,而且一般来说你不希望处理已交换值的客户端阻塞其他客户端几毫秒(或更多,如果被交换的值非常大),那么最好使用线程化 VM。

warmly 鼓励你用你的数据集和不同的配置进行实验……

*需要了解的随机事项

*交换文件的好位置

在许多配置中,交换文件可能相当大,达到 40GB 或更多。并非所有类型的文件系统都能很好地处理大文件,尤其是 Mac OS X 文件系统,它在这方面往往真的很差。

建议使用 Linux ext3 文件系统,或任何其他对 稀疏文件 有良好支持的文件系统。什么是稀疏文件?

稀疏文件是其中大量内容恰好为空的文件。ext2、ext3、ext4、ReiserFS、Reiser4 等高级文件系统能够以更有效的方式编码这些文件,并仅在需要时(即当文件的更多实际块将被使用时)为文件分配更多空间。

交换文件显然非常稀疏,特别是如果服务器运行时间很短,或者它比交换出去的数据量大得多。不支持稀疏文件的文件系统可能在某个时刻一次性创建非常大的文件时阻塞 Redis 进程。

有关支持稀疏文件的文件系统列表,请查看这个比较不同文件系统的 Wikipedia 页面

*监控 VM

一旦你有一个启用了 VM 的 Redis 系统运行起来,你可能非常想知道它工作得如何:总共交换了多少对象,每秒交换和加载的对象数量,等等。

有一个非常方便的工具来检查 VM 的工作情况,它是 Redis Tools 的一部分。这个工具叫 redis-stat,使用它非常简单:

$ ./redis-stat vmstat
--------------- objects --------------- ------ pages ------ ----- memory -----
load-in  swap-out  swapped   delta      used     delta      used     delta
138837   1078936   800402    +800402    807620   +807620    209.50M  +209.50M
4277     38011     829802    +29400     837441   +29821     206.47M  -3.03M
3347     39508     862619    +32817     870340   +32899     202.96M  -3.51M
4445     36943     890646    +28027     897925   +27585     199.92M  -3.04M
10391    16902     886783    -3863      894104   -3821      200.22M  +309.56K
8888     19507     888371    +1588      895678   +1574      200.05M  -171.81K
8377     20082     891664    +3293      899850   +4172      200.10M  +53.55K
9671     20210     892586    +922       899917   +67        199.82M  -285.30K
10861    16723     887638    -4948      895003   -4914      200.13M  +312.35K
9541     21945     890618    +2980      898004   +3001      199.94M  -197.11K
9689     17257     888345    -2273      896405   -1599      200.27M  +337.77K
10087    18784     886771    -1574      894577   -1828      200.36M  +91.60K
9330     19350     887411    +640       894817   +240       200.17M  -189.72K

上面的输出是关于一个启用了 VM 的 redis-server,里面有大约 100 万个键,并且使用 redis-load 工具进行了大量的模拟负载。

如你所见,每秒都有大量的换入和换出操作发生。注意第一行报告的是自服务器启动以来的实际值,而接下来的行是与上一次读数相比的差异。

如果你分配了足够的内存来容纳数据的工作集,可能你应该看到不太剧烈的交换发生,所以 redis-stat 可以是一个非常有价值的工具,用来了解你是否需要购买 RAM ;)

*启用 VM 的 Redis:更好的 .rdb 文件还是仅追加文件?

当启用 VM 时,保存和加载数据库的操作要 慢得多。一个通常 2 秒就能加载的数据库,如果服务器配置为使用尽可能小的内存(即 vm-max-memory 设置为 0),启用 VM 后需要 13 秒才能加载。

所以你可能想切换到使用仅追加文件进行持久化的配置,这样你可以不时执行 BGREWRITEAOF。

重要的是要注意,当 BGSAVE 或 BGREWRITEAOF 正在进行时,Redis 不会 将新值交换到磁盘。当有另一个子进程访问它时,VM 将是只读的。所以如果你在子进程工作时有很多写入,内存使用量可能会增长。

*使用尽可能少的内存

将 Redis 转换为仅内存中有键的磁盘数据库的一个有趣设置是将 vm-max-memory 设置为 0。如果你不介意更多的延迟和更差的性能,但希望为非常大的值使用非常少的内存,这是一个好的设置。

在这种设置中,你应该首先尝试将 VM 设置为阻塞式(vm-max-threads 0),因为在这种配置和高流量下,换入和换出操作的数量将非常巨大,线程化将消耗大量资源,相比之下简单的阻塞式实现更好。

*VM 稳定性

VM 仍然是实验性代码,但在过去几周中,它在开发环境中以多种方式进行了测试,甚至在一些生产环境中也进行了测试。在此期间没有注意到任何错误。不过在非受控环境中仍可能出现更隐蔽的错误,在那里有一些我们无法因某种原因重现的设置。

在这个阶段,你被鼓励在开发环境中尝试 VM,甚至在生产环境中尝试,如果你的数据库不是任务关键型的,而例如只是一个大数据的持久缓存,丢失这些数据不会有太大的问题。

如果你注意到任何问题,请向 Redis Google Group 报告,或通过 IRC 加入 freenode 上的 #redis 频道。