4 Redis VM 技术
- 4.1 前言
- 4.2 简单解释 VM
- 4.3 何时使用 VM 是个好主意
- 4.4 VM 配置
- 4.5 设置 vm-max-memory
- 4.6 配置 swap 文件
- 4.7 线程式 VM vs 阻塞式 VM
- 4.8 需要知道的一些事
- 4.9 VM 稳定性
- 4.10 参考
- 4.11 其他内容
4.1 前言
- 重要提示:Redis VM(virtual memory) 现在已经弃用。2.4 是最新支持 VM 的版本(但警告不鼓励使用 VM)。我们发现使用 VM 有一些劣势和问题。在将来的 Redis,我们希望简单提供最好的内存数据库(但像往常一样持久化到磁盘),至少现在不用考虑支持大于 RAM 大小的数据库。我们将来致力于提供脚本化、集群和更好的持久化
- Redis VM 特性第一次出现在稳定的 Redis 2.0 发布版本。然而,VM 在 git 的不稳定分支上仍然可以获取,且稳定可测试
4.2 简单解释 VM
- Redis 遵循 key-value 模型。键和一些值关联。通常,Redis 将键和相关的值保存在内存。有时这不是最好的选项,所以在设计上必须把键放在内存(为了保证快速查找),但是可以把较少使用的值交换到磁盘
- 在实际中,这意味着如果你在内存有 100 000 个键的数据集,但是只有 10% 的键经常使用,支持 VM 的 Redis 会尝试将较少使用的键关联的值转移到磁盘。当客户端的命令请求这些值时,这些值从 swap 文件加载到主存
4.3 何时使用 VM 是个好主意
- 在使用 VM 之前,你应该问自己你是否真的需要它。Redis 是磁盘备份,内存型数据库。正确使用 Redis 几乎总是有足够的 RAM 保存所有数据到内存。仍然有一些场景是不可能实现的
- 数据访问非常不均匀。只有小部分的键(比如网站上相关的活跃用户)被大量访问。同时每个键有大量的数据在内存中
- 不管数据访问模式和大量的值,只是没有足够的内存存放所有的数据。这种配置下,Redis 可当作磁盘型数据库,而键保存在内存,因此键查找很快,但是访问实际的值需要访问磁盘(较慢)
- 需要记住一个重要的概念
Redis 不能交换键
,因此如果内存问题的事实是键太多而对应的值很小,VM 不是解决方案 - 然而,如果因为值很大(比如大量的字符串、列表、集合或者有太多元素的哈希)而占用大量内存,VM 是一个好主意
- 有时候,可以通过哈希将相关的数据组合在一个键中,从而将“键很多,值很小”的问题转换成“键很少,值很大”的问题。比如,不要为对象的每个属性设置一个键,而是每个对象一个键,用哈希表示不同的属性
4.4 VM 配置
- 配置 VM 不难,但是需要根据需求仔细设置最好的参数
- 通过编辑 redis.conf 开启和配置 VM:
vm-enabled yes
- 其他一些配置选项可以改变 VM 行为。规则就是不想使用默认配置运行 Redis,因为每个问题和数据集需要一些微调以达到最好的优势
4.5 设置 vm-max-memory
vm-max-memory
指定 Redis 在交换值到磁盘之前可以自由使用的内存大小- 基本上,如果没有达到这个内存限制,Redis 不会交换对象,所有对象在内存中。一旦达到这个限制,Redis 会交换足够的对象以使内存降到限制以下
- 交换的对象主要是“年纪最大”(即未被使用的实际最长),但是一个对象的“可交换性”与它在内存中大小的对数是成比例的。因此,虽然偏向更旧的对象,当“年纪”相同时首先交换更大的对象
- 警告:因为不能交换键,在键使用空间大于内存时,Redis 不会考虑
vm-max-memory
设置 vm-max-memory
最好的值是足够的 RAM 来保持数据工作集。在实际中,只要给 Redis 尽可能多的空间,交换过程更好
4.6 配置 swap 文件
- Redis 使用交换文件将数据从内存转移到磁盘。交换文件和数据持久性无关,而且当一个 Redis 示例终止时可以被删除。但是,Redis 运行时,不应移动、删除或改变交换文件
- 因为在随机访问方式中经常使用 Redis 交换文件,把交换文件放在 SSD(solid state disk)会达到更好的性能
- 交换文件被分成页。一个值可以被交换到一个或多个页,但是一个页不能保存超过一个值
- 没有直接方式告诉 Redis 应该使用多大的交换文件。而是配置两个不同的值,二者相乘得到使用的字节数。可通过 redis.conf 配置两个参数
vm-pages
:交换文件的页数vm-page-size
:页的大小,以字节为单位
- 比如页大小是 32 bytes,总页数是 10 000 000,交换文件可以保存 320 MB 的数据
- 因为一个页不能保存超过一个值(但是一个值可以保存在多个页),必须仔细设置这些参数。通常,最好的注意是设置页大小以便大部分值可以使用较少的页交换
4.7 线程式 VM vs 阻塞式 VM
- 另一个很重要的配置参数是
vm-max-threads
。默认值是 4 - 表示为了执行交换文件的 I/O 操作所用的线程数量最大值。一个好的值只要和系统的核数匹配即可
0
会开启阻塞式 VM。当配置成阻塞式 VM 时,Redis 会以同步阻塞方式执行 I/O- 客户端访问交换出去的值,从磁盘读时会阻塞其他客户端,因此客户端经历的延迟会变大,尤其当磁盘慢或者忙,或者磁盘有大的交换的值
- 阻塞式 VM 的性能总的来说较好,因为没有同步、线程创建、恢复等待值的阻塞客户端的时间损失。因此,如果愿意接受较高的延迟,阻塞式 VM 是个好的选择。尤其是交换很少发生,且大部分访问的数据都在内存时
- 相反,如果有大量的交换操作,且有许多核想要利用,而且通常不希望处理交换值的客户端阻塞其他客户端几毫秒(交换值很大的时候时间更长),最好使用线程式 VM
- 鼓励使用不同的配置对数据集做实验。。。
4.8 需要知道的一些事
4.8.1 swap 文件的好位置
- 在很多配置中,交换文件可以很大,达到 40GB 甚至更大。不是所有类型的文件系统可以较好的处理大文件,尤其是 Mac OS X 文件系统在处理大文件方面比较差
- 建议使用 Linux ext3 文件系统,或者其他较好支持稀疏文件(sparse files)的文件系统。什么是稀疏文件呢?
- 稀疏文件大部分内容是空白的。高级的文件系统如 ext2,ext3,ext4,ReiserFS,Reiser4 等可以更有效地编码这些文件,并且在需要的时候为文件分配更多的空间,即文件更多的实际块被使用
- 交换文件显然是非常稀疏的,尤其是当服务运行时间较短,或者相比交换出去的数据更大时。一个不支持稀疏文件的文件系统创建一个大文件时,有时会阻塞 Redis 流程
4.8.2 监视 VM
- 当有一个开启 VM 的 Redis 系统允许时,可能对它如何工作感兴趣:总共交换了多少对象,每秒交换和加载的对象数目等
有一个工具方便检查 VM 如何允许,是 Redis 工具的一部分。这个工具叫做
redis-stat
,使用方式很直接:./redis-stat vmstat
./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
上述输出的 redis 服务开启了 VM,大约有 1 百万键,且有大量的同步加载使用
redis-load
工具可以从输出中看到,每秒都发生一些 load-in 和 swap-out 操作。注意第一行表示服务启动后实际的值,后续的行和前面的不一样
如果分配足够的内存来保存数据工作集,可能应该看到更少的交换发送,因为 redis-stat 是一个很有价值的工具来理解是否需要去商店购买 RAM
4.8.3 开启 VM 的 Redis:.rdb 文件 和 只能追加的文件哪个好
- 当开启 VM 时,保存和加载数据库是相当慢的操作。如果服务配置成使用最小的内存(即
vm-max-memory
设置成 0),在开启 VM 后,在 2 秒内加载的数据库通常需要 13 秒时间加载 - 因此,你可能想要切换配置使用只能追加的文件(Append Only File)来持久化,以便于你可以一直执行
BGREWRITEAOF
- 需要注意当一个
BGSAVE
或BGREWRITEAOF
在处理时,Redis 不会在磁盘上交换新的值。当有一个子进程访问 VM 时 VM 是只读的。因此如果一个工作的子进程有大量的写操作时,内存使用会增长- 子进程在读 VM 时,主进程不能进行值交换操作。因为通常读完一次值,“年龄”变小,可能从 VM 取出,而交换新的值
4.8.4 尽可能少的使用内存
- 将 Redis 设置成磁盘型数据库,而只保存键在内存的一个有趣的设置是设置
vm-max-memory
为 0。如果不介意更多延迟和较差的性能,但是想要非常大的值使用更少的内存,这个是好的设置 - 这种设置情况,应该首先尝试设置 VM 是阻塞式的(
vm-max-threads
为 0),因为大业务量会导致很多交换操作,且和简单的阻塞式实现相比,线程会消耗大量的资源
4.9 VM 稳定性
- VM 仍然是实验性代码,但是在过去的几周,在开发环境下用各种方式测试了 VM,甚至在一些生产环境。在测试阶段没有注意到 bug。但是在一些未控制的环境,且出于某些原因无法复现这些设置,会出现更加模糊的 bug
- 在这个阶段,鼓励在开发环境尝试 VM,甚至是生产环境下,当数据库不是关键型任务,比如大量持久化数据可以小时而不会有任何问题
4.10 参考
4.11 其他内容
4.11.1 Redis 对象和 VM pointer
- 键值都是 Redis 的对象,但是当值被移到 VM 时,会变成 VM pointer。主要记录值在磁盘的信息,如记录对象在交换文件第几页、共使用几页等内容
- Redis 对象和 VM pointer 都有一个字段 storage,用于判断值的位置
- redis_vm_memory:在内存
- redis_vm_swapped:在磁盘
- redis_vm_loading:在磁盘,但目前正有进程将其加载到内存
- redis_vm_swapping:在内存,但目前正有进程将其写入磁盘
4.11.2 交换过程
- 将对象交换到交换文件
- 计算保存此对象需要占用多少页
- 在交换文件中寻找一段连续空间保存此对象
- 把对象写入交换文件
- 将对象从交换文件取出到内存
- VM pointer 记录了对象在文件的起始页和占用页数,直接加载到内存即可
- 阻塞式 VM 在内存使用超过设定的
vm-max-memory
时,会循环找到候选对象进行交换直到内存使用下降到设定值以下