Redis专题(十一) ——Redis虚拟内存

Redis专题(十一) ——Redis虚拟内存

(原创内容,转载请注明来源,谢谢)

一、概述

Redis的数据是保存在内存中,当物理内存不足,其会保存在虚拟内存(VM)中。Redis的vm类似操作系统的vm,其会把所有的键都存在内存中,而把部分很少被访问到的值放在硬盘中。

操作系统的vm是基于页的概念,linux每个页4KB,而redis很多对象远小于4KB。另外,redis将交换到磁盘的对象压缩,保存到磁盘的对象可以去除指针和对象元数据,这样可以减少很多的I/O操作。

redis的虚拟内存只能解决value太大的问题,因为其全部key都存在内存中,因此如果是key不足,则无法解决问题。

二、配置

修改相关配置文件,如下:

1、开启vm

         vm-enabledyes

2、value保存在硬盘时的路径

         vm-swap-file  /routetofile/redis.swap

3、最大内存上限,超出时开始使用vm

         vm-max-memory  xx

4、每个页面的大小(字节)

         vm-page-sizexx

5、用于执行value对象换入换出的工作线程数量,通常设置成cpu内核数量

         vm-max-threads 4

三、vm使用规则

redis根据以下算法将value存于vm中:

         swappability= age * log(size_in_memory)

其中age是距离上一次被访问的时间,size_in_memory是value占用内存的大小。由于占用内存较大的value,取出来时消耗的I/O和CPU资源更多,因此在判断该取哪个value时,其权重更低,因此加上log计算。

四、vm开启限制

vm会按规则将部分value保存在硬盘,而redis的数据库备份通常采用rdb,其备份方式是主进程正常提供客户端操作,子进程去进行rdb备份。这可能会出现redis主进程在写vm的时候,正好子进程在备份。

为了避免此情况,redis做了限制,子进程在读vm时(无论是rdb还是aof),主进程不能进行value交换操作(因为通常读完一次value,其age变小,可能会从vm取出,而换新的value进去)。即此时两个进程都采用只读访问swap文件。

五、redis object 与vm pointer

通常key和value都是redis的object,但是当value被移到vm时,其会变成vmpointer。其主要记录的是value在磁盘的信息,如记录对象在swap文件第几页开始、共使用几页等内容。

redisobject和vm pointer 都有一个字段storage,用于判断value此时的位置,共有四种情况:redis_vm_memory(在内存里)、redis_vm_swapped(在磁盘里)、redis_vm_loading(在磁盘里,但目前正在有进程将其加载到内存)、redis_vm_swapping(在内存里,但目前正在有进程将其写入磁盘)

六、交换过程

1、将对象交换到swap文件

1)计算保存这个对象需要占用swap文件中的多少页。在代码中是通过调用函数rdbSavedObjectPages进行计算。

2)在swap文件中寻找一段连续空间保存这个对象。在代码中是通过调用函数vmFindContiguousPages进行计算。

该函数内部是从全部磁盘页中查找n块连续的空闲页,成功时返回redis_ok,并把first参数设置为连续页的开始地址。失败会返回redis_error。

3)把对象写入swap文件。在代码中是通过调用函数vmWriteObjectOnSwap进行计算。

该函数在写入文件之前,会先将swap文件锁住,防止其他进程也在进行此操作。接着把文件指针移动到要写入页的开始地址,并调用rdbSaveObject函数把对象写入swap文件。对象写入文件后,会释放内存,并把对象从redis object转成vm pointer。并且会将storage字段设置成redis_vm_swapped。

2、将对象从swap文件取出到内存

由于vm pointer记录了对象在文件的起始页和所占页数,因此只需要调用vmLoadObject函数,其最终会调用vmReadObjectFromSwap将对象写入内存

在将内存取出的过程中,也会锁住swap文件。

七、阻塞式VM

将上述配置文件中的vm-max-threads设置为0,即不开启多线程,则进行vm时会阻塞。将对象从内存交换到swap文件,发生在cron任务,每100毫秒执行一次。当redis对内存的使用超过设定的vm-max-memory,则会循环调用vmSwapOneObject函数,实现对象交换。

该操作步骤如下:

1)找一个较优的用来交换至swap分区的候选对象。

2)调用vmSwapObjectBlocking将对象所关联的value值交换到硬盘,此函数返回一个vmPointer指针,用于存储value在磁盘中的信息。

3)使用vmSwapObjectBlocking返回的vmPointer替换value对应的redisObject,并把storage设置为redis_vm_swapped,表示value已经交换到磁盘。

4)是否对象关联的value内存。

这个操作每次会释放一个object,因此会循环执行此函数,直到内存的使用下降到配置的vm-max-memory之下。

八、非阻塞式VM

阻塞式VM会导致读取swap文件时,锁住整个redis,这样其他客户端访问不在swap文件的value时,也会延迟。为了避免此问题,redis支持非阻塞式VM,是通过I/O线程实现。

具体来说,redis使用任务队列的方式,每当主线程需要在后台使用I/O线程完成任务,便push一个I/O任务到server.io_newjobs队列。当系统不存在活动I/O线程,便新建一个,会执行这些I/O任务。任务执行完成后,会将结果push到server.io_processed队列。该线程使用unix管道给主进程发送1字节信号,通知主线程有一个新任务已经完成。

——written by linhxx 2017.08.14

原文发布于微信公众号 - 决胜机器学习(phpthinker)

原文发表时间:2017-08-14

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏磨磨谈

rbd的image对象数与能写入文件数的关系

对于这个问题,我原来的理解也是:对象默认设置的大小是4M一个,存储下去的数据,如果小于4M,就会占用一个小于4M的对象,如果超过4M,那么存储的数据就会进行拆分...

992
来自专栏kalifaの日々

BeautifulSoup使用find,find_all常见问题汇总

1.soup.find(class='abc')报错,原因是find和find_all里面都不能直接把class作为参数,改写成如下任意一种就对了: 第一种,给...

3465
来自专栏三木的博客

Linux shell 程序设计2——bash的内置命令

常用的内置命令忽略,来看看shell编程中其他一些重要的内置命令: 1、help:显示所有内置命令列表,或显示一个具体命令的用法。 -s: 表示列出命令的语法...

1916
来自专栏自动化测试实战

flask第34篇——测试器、模板继承

http://jinja.pocoo.org/docs/2.10/templates/#list-of-builtin-tests

622
来自专栏极客猴

Python 三种遍历目录的方法,轻松帮你找出隐藏文件

无论在 Windows 系统中还是 Linux 系统中,都存在着隐藏文件以及隐藏文件夹。隐藏文件夹一般是系统关键性目录,例如 Windows 系统中的 C 盘中...

1202
来自专栏idba

Python模块之fileinput

一 简介 fileinput 是python 提供的一个可以快速遍历,修改一个或者多个文件的模块。我们可以使用该模块进行文本替换 并做文件备份。

662
来自专栏行者常至

(Servlet)常见错误及解决方式

·404数字 指的是:服务器执行完客户端的请求后,返回给客户端一个执行结果的状态编码。

694
来自专栏上善若水

L14c程序执行的环境变量

execve(执行文件)在父进程中fork一个子进程,在子进程中调用exec函数启动新的程序。exec函数一共有六个,其中execve为内核级系统调用,其他(e...

853
来自专栏社区的朋友们

django 初始化默认数据的一个方法

经常可能会有的一个需求就是,一个表中会有一些默认数据,这些数据未来是可以通过管理端来配置的。所以数据库在被建立之后,会有一些默认数据被插入表中。

1.6K2
来自专栏Python中文社区

Python判断文件是否存在的三种方法

專 欄 ❈Jerry,Python中文社区专栏作者。 blog:https://my.oschina.net/jhao104/blog github:ht...

21410

扫码关注云+社区