那什么时候会发生物理内存的复制呢?当父进程或者子进程在向共享内存发起写操作时,CPU 就会触发缺页中断,这个缺页中断是由于违反权限导致的,然后操作系统会在「缺页异常处理函数」里进行物理内存的复制,并重新设置其内存映射关系,将父子进程的内存读写权限设置为可读写,最后才会对内存进行写操作,这个过程被称为「**写时复制(Copy On Write)**」 。

文章插图
【Redis 的大 Key 对持久化有什么影响?】
写时复制顾名思义,在发生写操作的时候,操作系统才会去复制物理内存,这样是为了防止 fork 创建子进程时,由于物理内存数据的复制时间过长而导致父进程长时间阻塞的问题 。
如果创建完子进程后,父进程对共享内存中的大 Key 进行了修改,那么内核就会发生写时复制,会把物理内存复制一份,由于大 Key 占用的物理内存是比较大的,那么在复制物理内存这一过程中,也是比较耗时的,于是父进程(主线程)就会发生阻塞 。
所以,有两个阶段会导致阻塞父进程:
- 创建子进程的途中,由于要复制父进程的页表等数据结构,阻塞的时间跟页表的大小有关,页表越大,阻塞的时间也越长;
- 创建完子进程后,如果子进程或者父进程修改了共享数据,就会发生写时复制,这期间会拷贝物理内存,如果内存越大,自然阻塞的时间也越长;
Linux 内核从 2.6.38 开始支持内存大页机制,该机制支持 2MB 大小的内存页分配,而常规的内存页分配是按 4KB 的粒度来执行的 。
如果采用了内存大页,那么即使客户端请求只修改 100B 的数据,在发生写时复制后,Redis 也需要拷贝 2MB 的大页 。相反,如果是常规内存页机制,只用拷贝 4KB 。
两者相比,你可以看到,每次写命令引起的复制内存页单位放大了 512 倍,会拖慢写操作的执行时间,最终导致 Redis 性能变慢 。
那该怎么办呢?很简单,关闭内存大页(默认是关闭的) 。
禁用方法如下:
echo never >/sys/kernel/mm/transparent_hugepage/enabled总结当 AOF 写回策略配置了 Always 策略,如果写入是一个大 Key,主线程在执行 fsync() 函数的时候,阻塞的时间会比较久,因为当写入的数据量很大的时候,数据同步到硬盘这个过程是很耗时的 。AOF 重写机制和 RDB 快照(bgsave 命令)的过程,都会分别通过 fork() 函数创建一个子进程来处理任务 。会有两个阶段会导致阻塞父进程(主线程):
- 创建子进程的途中,由于要复制父进程的页表等数据结构,阻塞的时间跟页表的大小有关,页表越大,阻塞的时间也越长;
- 创建完子进程后,如果父进程修改了共享数据中的大 Key,就会发生写时复制,这期间会拷贝物理内存,由于大 Key 占用的物理内存会很大,那么在复制物理内存这一过程,就会比较耗时,所以有可能会阻塞父进程 。
- 客户端超时阻塞 。由于 Redis 执行命令是单线程处理,然后在操作大 key 时会比较耗时,那么就会阻塞 Redis,从客户端这一视角看,就是很久很久都没有响应 。
- 引发网络阻塞 。每次获取大 key 产生的网络流量较大,如果一个 key 的大小是 1 MB,每秒访问量为 1000,那么每秒会产生 1000MB 的流量,这对于普通千兆网卡的服务器来说是灾难性的 。
- 阻塞工作线程 。如果使用 del 删除大 key 时,会阻塞工作线程,这样就没办法处理后续的命令 。
- 内存分布不均 。集群模型在 slot 分片均匀情况下,会出现数据和查询倾斜情况,部分有大 key 的 Redis 节点占用内存多,QPS 也会比较大 。
最好在设计阶段,就把大 key 拆分成一个一个小 key 。或者,定时检查 Redis 是否存在大 key ,如果该大 key 是可以删除的,不要使用 DEL 命令删除,因为该命令删除过程会阻塞主线程,而是用 unlink 命令(Redis 4.0+)删除大 key,因为该命令的删除过程是异步的,不会阻塞主线程 。
推荐阅读
- 地球中的大气层总共分为多少层? 大气层分为几层?
- 家养的大鹅?鹅的生活是怎样的
- 苍兰诀|《苍兰诀》首支番外重磅来袭 大强和小兰花的大婚名场面要来啦
- 艾美奖|不愧是HBO,艾美奖今年的大赢家
- 黎姿|本以为蔡少芬颜值身材够好了,当她和黎姿早年合影被翻出,差距不是一般的大
- 娱乐圈|这几天随着官宣李某某pc坐实的消息,还有两个娱乐圈的大咖跟着上了热搜
- 三四十岁的大龄程序员,到底该怎么做才能避免失业?
- 古代皇后身边的大宫女?古代皇后身边的大宫女
- 李易峰|原来,李易峰的这些“梗”,都是说的大实话!
- 康熙王朝的忠臣有几位?康熙时期的大臣
