当前位置: 首页>数据库>正文

用redis做qps限制 redis qps

redis简介

Redis性能极高,由于其超高的读写速度,被广泛用于缓存系统,解决超高并发的应用读写需求,新浪就有国内最大的redis缓存。

bgsave功能

Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。其中RDB的持久化,就是通过bgsave完成的,为什么占用内存大的redis实例,在调用bgsave时,会导致redis的qps性能波动呢?

bgsave运行过程

当redis调用bgsave进行RDB持久化时,会先fork子进程,在子进程未被创建成功之前,redis的主线程会被堵塞,这个堵塞的时间长短,就直接决定bgsave导致QPS性能波动有多大,当然这个fork时间,可以通过使用Redis的INFO 命令查看 latest_fork_usec 指标值(表示最近一次 fork 的耗时)。

用redis做qps限制 redis qps,用redis做qps限制 redis qps_用redis做qps限制,第1张

要彻底弄清楚bgsave的影响范围,所以必须要清楚的知道fork操作的实现原理。

fork

在讲解fork功能之前,先看一段代码示例

[root@mysql ~]# cat fork_test.cpp
#include 
#include 
int main()
{
int pid = fork();
if (pid == -1)
return -1;
if (pid>0)
{
printf("I am father, my pid is %d\n", getpid());
return 0;
}
else
{
printf("I am child, my pid is %d\n", getpid());
return 0;
}
}
[root@mysql ~]# g++ fork_test.cpp -o fork_test
[root@mysql ~]# ./fork_test
I am father, my pid is 27364
I am child, my pid is 27365

是不是非常惊讶,为什么执行一次程序,怎么打印了2个结果,这是怎么做到的呢。

fork实现过程1.分配新的内存块和内核数据结构给子进程2.将父进程代码数据段,页目录和页表,虚拟内存池(vm_area_struct )拷贝至子进程3.添加子进程到系统进程列表当中4.fork返回,开始调度器调度

了解fork实现过程之后,再来看代码,当代码执行到语句int pid = fork(),由于在复制时复制了父进程的堆栈段,所以两个进程都停留在fork函数中,等待返回。因此fork函数会返回两次,一次是在父进程中返回,另一次是在子进程中返回,并且这两次的返回值是不一样的。

用redis做qps限制 redis qps,用redis做qps限制 redis qps_mysql redis qps_02,第2张

在父进程中,fork返回新创建子进程的进程ID;在子进程中,fork返回0;如果出现错误,fork返回一个负值;

fork耗时分析

fork实现过程的4个步骤中,基本所有的耗时在步骤2中,将父进程代码数据段,页目录和页表,虚拟内存池(vm_area_struct )拷贝至子进程,而这里拷贝页目录和页表占据了绝大部分时间,所以要一定要弄清楚redis的实例的页目录和页表占用了多大内存空间,就变的至关重要了。

页目录和页表在内存中的存放形式,其实是一颗树,其结构如下图所示。

用redis做qps限制 redis qps,用redis做qps限制 redis qps_用redis做qps限制_03,第3张

那这棵树会占用多大内存空间呢,我们知道标准的linux内存页大小是4kb,一个页表条目占用8个字节,一个24GB的Redis实例,页表占用空间大约为:24 GB 4 kB * 8 = 48 MB,实际上会比这个值要大一点。

当执行bgsave命令,调用fork时,就会将这48M的页表数据拷贝到新的子进程中,这个拷贝性能,官方给了一组数据,在实体机和虚拟机的耗时不一样,实体机的耗时要比虚拟机中小。

Linux running on physical machine (Unknown HW) 6.1GB RSS forked in 80 milliseconds (13.1 milliseconds per GB)
Linux running on physical machine (Xeon @ 2.27Ghz) 6.9GB RSS forked into 62 milliseconds (9 milliseconds per GB)
Linux VM on 6sync (KVM) 360 MB RSS forked in 8.2 milliseconds (23.3 milliseconds per GB).
Linux VM on EC2, old instance types (Xen) 6.1GB RSS forked in 1460 milliseconds (239.3 milliseconds per GB)
Linux VM on EC2, new instance types (Xen) 1GB RSS forked in 10 milliseconds (10 milliseconds per GB).
Linux VM on Linode (Xen) 0.9GBRSS forked into 382 milliseconds (424 milliseconds per GB).

从上面的数据库,可以得出:10G内存的Redis,在物理机上fork操作耗时在90毫秒至131毫秒。10G内存的Redis,在虚拟机上fork操作耗时在100毫秒至4240毫秒。

由于生产环境的差异,实际的耗时会有不同,不过差异不会特别大。

所以在这里有2个建议建议1:生产的redis实例不要超过10G。建议2:生产的redis实例最好部署在物理机上。

在这里有的朋友可能会问,那我用大内存页,不就可以减少页表的数量,提升fork速度吗。这个想法很好,但是超级有聪明的linux开发人员,在优化fork时,使用cow(写时复制),当父进程或者子进程要修改共享内存区域数据时,linux内核就会将要修改的内存数据页拷贝一份到子进程,这样就不影响数据的写操作。

当在做bgsave时,redis突然有大量写操作,fork操作就会占用更多的内存空间,严重的话,到导致fork失败,甚至触发OOM。


https://www.xamrdz.com/database/6me1963404.html

相关文章: