Redis虚拟内存

Redis的虚拟内存和操作系统的虚拟内存不是一码事,但是思路和目的都是相同的:

就是暂时把不常访问的数据从内存交换到磁盘中,从而腾出宝贵的内存空间

Redis在用户态实现了自己的虚拟内存机制

主要的理由有以下两点:

  1. 操作系统的虚拟内存是以4k/页为最小单位进行交换的。而Redis 的大多数对象都远小于4k,所以一个操作系统页上可能有多个Redis对象。另外Redis的集合对象类型如list,set可能存在于多个操作系统页上,最终可能造成只有10%的key 被经常访问,但是所有操作系统页都会被操作系统认为是活跃的,这样只有内存真正耗尽时操作系统才会进行页的交换。

  2. 相比操作系统的交换方式,Redis 可以将被交换到磁盘的对象进行压缩,保存到磁盘的对象可以去除指针和对象元数据信息。一般压缩后的对象会比内存中的对象小10 倍。这样Redis 的虚拟内存会比操作系统的虚拟内存少做很多IO 操作。

虚拟内存配置

#开启虚拟内存的功能
vm-enabled yes

#交换出来value保存的文件路径/tmp/redis.swap
vm-swap-file /tmp/redis.swap

#redis使用的最大内存上限,超出上限后redis开始狡猾value到磁盘swap文件中,建议设置为系统空闲内存的60%~80%
vm-max-memory 268435456

#每个Redis页的大小 字节
vm-page-size 32

#最多在文件中使用多少个页,交换文件的大小 = (vm-page-size * vm-pages)
vm-pages 1334217728

#用于执行value对象换入换出的工作线程数量,0表示不使用工作线程
vm-max-threads 8

Redis的虚拟内存在设计上为了保证key 的查询速度,只会将value 交换到swap 文件中。如果是由于太多key很小的value造成的内存问题,那么redis 的虚拟内存并不能解决问题。和操作系统一样redis 也是按页来交换对象的。redis 规定同一个页只能保存一个对象。但是一个对象可以保存在多个页中。在redis 使用的内存没超过vm-max-memory 之前是不会交换任何value 的。当超过最大内存限制后,redis 会选择把较老的对象交换到swap文件中去。如果两个对象一样老会优先交换比较大的对象, 精确的交换计算公式swappability = age*log(size_in_memory)。对于vm-page-size 的设置应该根据自己应用将页的大小设置为可以容纳大多数对象的尺寸。太大了会浪费磁盘空间,太小了会造成交换文件出现过多碎片。对于交换文件中的每个页,redis 会在内存中用一个1bit 值来对应记录页的空闲状态。所以像上面配置中页数量(vm-pages 134217728 )会占用16MB 内存用来记录页的空闲状态。vm-max-threads 表示用做交换任务的工作线程数量。如果大于0 推荐设为服务器的cpu 的核心数。如果是0 则交换过程在主线程进行。

Redis虚拟内存工作方式简介

当vm-max-threads设置为0时(阻塞方式)
换出

主线程定期检查发现内存超出最大上限后,会直接以阻塞的方式,将选中的对象保存到swap文件中,并释放对象占用的内存空间,此过程会一直重复直到下面条件满足:

1.内存使用降到最大限制以下

2.swap文件满了

3.几乎全部的对象都被交换到磁盘

换入

当有客户端请求已经被换出的value时,主线程会以阻塞的方式从swap文件中加载对应的value对象,加载时会阻塞所有客户端,然后处理该客户端的请求

当vm-max-threads设置为大于0时(工作线程方式)
换出

当主线程检测到使用内存超过最大上限,会将选中要交换的对象信息放到一个队列中交给工作线程后台处理,主线程会继续处理客户端请求

换入

如果有客户端请求的key已经被换出了,主线程会先阻塞发出命令的客户端,然后将加载对象的信息放到一个队列中,让工作线程去加载。加载完毕后工作线程通知主线程,主线程再执行客户端的命令,这种方式值阻塞请求的value是已经被换出key的客户端

总的来说,阻塞方式的性能更好一点,因为不需要线程同步、创建线程和恢复被阻塞的客户端等开销,但是也相应的牺牲了响应性

工作线程方式主线程不会阻塞在磁盘IO上,所以响应性更好

如果应用不太经常发生换入换出,而且也不太在意有点延迟的话,推荐使用阻塞方式