虚拟内存
Redis 虚拟内存这一特性将首次出现在Redis 2.0的一个稳定发布版中。目前Git上Redis 不稳定分支的虚拟内存(从现在起称之为VM)已经可以使用,并且经试验证明足够稳定。
简介
实际应用中,如果内存中有一个10万条记录的key值数据集,而只有10%被经常使用,那么开启虚拟内存的Redis将把与较少使用的key相对应的value转移至磁盘上。当客户端请求获取这些value时,他们被将从swap 文件中读回,并载入到内存中。
何时使用虚拟内存
在确定使用VM之前,请首先确认是否真的需要使用这一特性。Redis是一个磁盘备份,内存型数据库。使用Redis 的正确方法通常是使用足够大的RAM去装载所有数据。然而,有些场景下是无法做到这样的:
- 数据访问不均匀。只有很少部分的key被大量访问,而每一个key又有大量的数据要放入内存。
- 在不考虑数据读取方式以及value存储空间大小的前提下,仅由于没有足够的空间将所有数据放入内存。这种场景下,Redis可以被配置为在内存中存储key,在磁盘中存储value。此时key的查询操作较快,而value的读取则相对较慢。
谨记Redis的key是不做swap操作的,所以如果你的内存有大量的key和少量的value时,那么VM并不能解决你的问题。
然而当由于value占用空间较大(如占用空间较多的strings以及含有大量元素的lists, sets 或者 hashes)导致内存不足时,那么VM无疑是一个很好的选择。
有时你可以通过哈希表将相关的数据归组到一个key的相应字段,从而将“大量key与小存储的value”的问题转化为“少量key与大存储的value”的问题。例如你可以为每一个对象设置一个单独的key,并用哈希表的多个字段代表对象的不同属性,而非为对象的每一个属性设置一个单独的key。
VM 配置
VM的配置相对简单,可以根据需求设置最佳参数。通过编辑redis.conf来开启并配置VM。首先开启VM:
vm-enabled yes
很多配置项可以改变VM的行为。事实上,为了获取最佳性能,你常常需要对配置做微调,而非使用默认参数。
vm-max-memory设置
vm-max-memory 指 Redis 在将value交换至磁盘(进行swap操作)之前有多大内存可使用。
通常,如果未达到内存上限,则不需要进行磁盘交换,Redis将所有对象放在内存中操作。然而一旦到达上限,将会有大量的对象被交换出内存至磁盘,以释放内存空间,直到低于限制。
交换过程(swap操作)中,首先被交换的对象是那些有着较大“年龄”(指未被访问的时长)的对象,同时一个对象的“交换能力”(”swappability”)与它在内存中大小的对数成正(swappability = age*log(size_in_memory))。当两个对象有着相同的“年龄”时,占用空间较大的对象将会首先被交换出去。
提醒:由于key不能被交换出内存,所以当仅由于key占用空间较多而达到内存上限时, Redis是不能通过改变 vm-max-memory 来解决问题的。
最好将该值设置到可以用RAM装载整个数据的工作集(working set)。实际中,当Redis有足够的内存时,交换操作(swap)也将进行的更加顺利。
配置swap文件
Redis利用swap文件将数据从内存转移到磁盘。swap文件并不对数据的存储时间做处理,当一个Redis应用结束时,swap文件可以被清除。然而,当Redis运行时不能以任何形式移动,删除或改变 swap文件。由于Redis的swap文件通常以随机读取的方式被使用,所以用ssd(固态硬盘)存储swap文件性能将更好。
swap文件按“页”(page)切分。一个值可以被交换(swap)到一个或多个页中,但是一个页最多只能存储一个值。
没有直接的方式可以获取Redis的swap文件将有多少字节被使用。然而,可以利用两个不同的配置参数,将其做乘积计算出使用的字节总数。这两个参数分别表示交换文件的页数和页大小,它们可以在redis.conf文件中配置:
- vm-pages 用于配置swap文件中页的总数
- vm-page-size 用于配置页的字节数
例:如果页大小被设置为32 byte,同时 页的总数被设置为1千万, 则交换文件总共可以装载320MB的数据。
由于单页最多只能装载一个value(但是一个value可以被存储在多个页上),因此应特别注意这些参数的设置。通常通过改变页大小来完成设置,这样可以用少量的页来完成大部分value的交换。
线程式虚拟内存 vs 阻塞式虚拟内存
另一个重要的配置参数是 vm-max-threads:
# The default vm-max-threads configuration
vm-max-threads 4该参数用于设置Redis与swap文件进行I/O的最大线程数。通常令其等于系统的cpu核数。
当该参数设置为“0”时,将开启阻塞式虚拟内存。此时,它将以同步阻塞的方式进行I/0. 阻塞式虚拟内存有如下特性:
- 当客户端从磁盘上读取被交换出去的key时,将阻塞其他客户端,所以该方式经历时延较长,尤其当磁盘较慢或者有大存储的数据发生交换时。
- 总体说来,由于在进行同步、增加线程和恢复由于等待value而阻塞的客户端时没有时间损耗,阻塞式虚拟内存表现更好一些。所以如果你能接受偶尔较高的时延,阻塞式虚拟内存将是一个好的选择。尤其在发生交换较少,同时大部分你经常访问的数据恰好可以放入内存时。
相反,如果有大量的换入换出操作时,同时系统有多核可以使用,而你又不希望进行swap操作的客户端阻塞其他客户端时(通常几毫秒,当待交换数据占用空间较大时,时间会更长),用线程式虚拟内存效果将更好。我们鼓励你尝试用不同的配置做测试。
要知道的一些事情
swap文件的存放
在很多配置中swap文件可以很大,达到40GB或者更大。然而并不是所有的文件系统都可以很好的处理大文件,尤其是Mac OS X的文件系统,常常会显得异常蹩脚。
建议使用 linux ext3文件系统,或者其他可以很好支持稀疏文件的文件系统。
什么是稀疏文件?
稀疏文件指有大量内容为空的文件。高级一些的文件系统例如ext2, ext3, ext4,ReiserFS, Reiser4和其他一些文件 可以用一种更高效的方式编码这些文件,同时当文件有更多的块(block)需要被使用时,则为该文件分配更多的空间。
swap文件很稀疏的。当一次创建一个很大的文件时,不支持稀疏文件的文件系统可能会阻塞Redis进程。
参考wikepedia page 了解支持稀疏文件的文件系统。
虚拟内存的监控
一旦使用了开启虚拟内存的Redis,你可能很感兴趣它是怎样工作的:总共多少个对象被交换,每秒交换与载入的对象量等等。
下面是一个用于检查VM是如何工作的工具(见此处)。作为Redis 工具的一部分,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
上面是一个redis-server 在虚拟内存开启,内部含有大约 1千万条key,同时利用redis-load 工具做大量仿真负载时的输出结果。从输出中你可以看到每秒有大量的“载入”和“交换”操作。请注意第一行显示从服务器开启时到现在的实际数值,下面几行不同于之前读取的数值。
如果你分配了足够的内存,可能会看到很多不太明显的交换,而redis-stat 是一个非常有用的工具,可以帮你判断是否需要更换RAM了。
开启虚拟内存的Redis :.rdb文件还是 AOF(Append Only File)文件更合适 ?
当虚拟内存开启时,保存和读取数据库操作都将变慢。当服务器被配置为用最少的内存时(即vm-max-memory 被设置为0),一个通常2s载入一次的DB操作,在开启虚拟内存时耗时将长达13s,所以你可能希望切换使用AOF的配置来实现持久化,以便可以进行BGREWRITEAOF操作。
请注意 当进程进行 BGSAVE 或者 BGREWRITEAOF 操作时,Redis不会在磁盘上交换新的value.
当有子进程访问虚拟内存时,虚拟内存将是只读的。所以,当有一个子进程有大量的写操作时,内存使用将增加。
减少使用内存
将 vm-max-memory设置为0,可以使Redis转为仅有key在内存中的磁盘数据库。当你希望使用尽可能少的内存存储大容量数值, 同时不介意时延或者相对糟糕的性能时,这是不错的选择。
该设置中你应该首先尝试着将虚拟内存设置为阻塞式(vm-max-threads 0)的。大量的交换入和交换出操作将带来巨大的开销,与单线程阻塞式虚拟内存相比,线程式虚拟内存将消耗大量的资源 。
虚拟内存的稳定性
虚拟内存仍处于试验阶段,但在过去的数周里,它已经以各种方式被应用到开发环境中,甚至一些产品中。目前,在测试阶段并没有发现bug,然而更多不确定的bug可能会在日后某些不可控的环境中发生,而这些环境常常由于一些原因而无法复现。
当前阶段,我们鼓励你在开发过程中尝试使用虚拟内存,甚至在产品中,如果你的db不是至关重要的话。
请报告任何你注意到的问题到the Redis Google Group,或者通过IRC加入 #redis IRC。
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/tech/pnotes/117721.html