Redis 大 Key 对持久化有什么影响?

news2024/9/20 16:29:34

资料来源 : 小林coding

小林官方网站 : 小林coding (xiaolincoding.com)

Redis 的持久化方式有两种:AOF 日志和 RDB 快照。

所以接下来,针对这两种持久化方式具体分析分析

大 Key 对 AOF 日志的影响

先说说 AOF 日志三种写回磁盘的策略

Redis 提供了 3 种 AOF 日志写回硬盘的策略,分别是:

  • Always,这个单词的意思是「总是」,所以它的意思是每次写操作命令执行完后,同步将 AOF 日志数据写回硬盘;
  • Everysec,这个单词的意思是「每秒」,所以它的意思是每次写操作命令执行完后,先将命令写入到 AOF 文件的内核缓冲区,然后每隔一秒将缓冲区里的内容写回到硬盘;
  • No,意味着不由 Redis 控制写回硬盘的时机,转交给操作系统控制写回的时机,也就是每次写操作命令执行完后,先将命令写入到 AOF 文件的内核缓冲区,再由操作系统决定何时将缓冲区内容写回硬盘。

这三种策略只是在控制 fsync() 函数的调用时机。

当应用程序向文件写入数据时,内核通常先将数据复制到内核缓冲区中,然后排入队列,然后由内核决定何时写入硬盘。

如果想要应用程序向文件写入数据后,能立马将数据同步到硬盘,就可以调用 fsync() 函数,这样内核就会将内核缓冲区的数据直接写入到硬盘,等到硬盘写操作完成后,该函数才会返回。

  • Always 策略就是每次写入 AOF 文件数据后,就执行 fsync() 函数;
  • Everysec 策略就会创建一个异步任务来执行 fsync() 函数;
  • No 策略就是永不执行 fsync() 函数;

分别说说这三种策略,在持久化大 Key 的时候,会影响什么?

在使用 Always 策略的时候,主线程在执行完命令后,会把数据写入到 AOF 日志文件,然后会调用 fsync() 函数,将内核缓冲区的数据直接写入到硬盘,等到硬盘写操作完成后,该函数才会返回。

当使用 Always 策略的时候,如果写入是一个大 Key,主线程在执行 fsync() 函数的时候,阻塞的时间会比较久,因为当写入的数据量很大的时候,数据同步到硬盘这个过程是很耗时的

当使用 Everysec 策略的时候,由于是异步执行 fsync() 函数,所以大 Key 持久化的过程(数据同步磁盘)不会影响主线程。

当使用 No 策略的时候,由于永不执行 fsync() 函数,所以大 Key 持久化的过程不会影响主线程。

大 Key 对 AOF 重写和 RDB 的影响

当 AOF 日志写入了很多的大 Key,AOF 日志文件的大小会很大,那么很快就会触发 AOF 重写机制

AOF 重写机制和 RDB 快照(bgsave 命令)的过程,都会分别通过 fork() 函数创建一个子进程来处理任务。

在创建子进程的过程中,操作系统会把父进程的「页表」复制一份给子进程,这个页表记录着虚拟地址和物理地址映射关系,而不会复制物理内存,也就是说,两者的虚拟空间不同,但其对应的物理空间是同一个。

这样一来,子进程就共享了父进程的物理内存数据了,这样能够节约物理内存资源,页表对应的页表项的属性会标记该物理内存的权限为只读

随着 Redis 存在越来越多的大 Key,那么 Redis 就会占用很多内存,对应的页表就会越大。

在通过 fork() 函数创建子进程的时候,虽然不会复制父进程的物理内存,但是内核会把父进程的页表复制一份给子进程,如果页表很大,那么这个复制过程是会很耗时的,那么在执行 fork 函数的时候就会发生阻塞现象

而且,fork 函数是由 Redis 主线程调用的,如果 fork 函数发生阻塞,那么意味着就会阻塞 Redis 主线程。由于 Redis 执行命令是在主线程处理的,所以当 Redis 主线程发生阻塞,就无法处理后续客户端发来的命令。

我们可以执行 info 命令获取到 latest_fork_usec 指标,表示 Redis 最近一次 fork 操作耗时。

# 最近一次 fork 操作耗时
latest_fork_usec:315

如果 fork 耗时很大,比如超过1秒,则需要做出优化调整:

  • 单个实例的内存占用控制在 10 GB 以下,这样 fork 函数就能很快返回。
  • 如果 Redis 只是当作纯缓存使用,不关心 Redis 数据安全性问题,可以考虑关闭 AOF 和 AOF 重写,这样就不会调用 fork 函数了。
  • 在主从架构中,要适当调大 repl-backlog-size,避免因为 repl_backlog_buffer 不够大,导致主节点频繁地使用全量同步的方式,全量同步的时候,是会创建 RDB 文件的,也就是会调用 fork 函数。

那什么时候会发生物理内存的复制呢?

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

写时复制顾名思义,在发生写操作的时候,操作系统才会去复制物理内存,这样是为了防止 fork 创建子进程时,由于物理内存数据的复制时间过长而导致父进程长时间阻塞的问题。

如果创建完子进程后,父进程对共享内存中的大 Key 进行了修改,那么内核就会发生写时复制,会把物理内存复制一份,由于大 Key 占用的物理内存是比较大的,那么在复制物理内存这一过程中,也是比较耗时的,于是父进程(主线程)就会发生阻塞

所以,有两个阶段会导致阻塞父进程:

  • 创建子进程的途中,由于要复制父进程的页表等数据结构,阻塞的时间跟页表的大小有关,页表越大,阻塞的时间也越长;
  • 创建完子进程后,如果子进程或者父进程修改了共享数据,就会发生写时复制,这期间会拷贝物理内存,如果内存越大,自然阻塞的时间也越长;

这里额外提一下, 如果 Linux 开启了内存大页,会影响 Redis 的性能的

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 占用的物理内存会很大,那么在复制物理内存这一过程,就会比较耗时,所以有可能会阻塞父进程。

大 key 除了会影响持久化之外,还会有以下的影响。

  • 客户端超时阻塞。由于 Redis 执行命令是单线程处理,然后在操作大 key 时会比较耗时,那么就会阻塞 Redis,从客户端这一视角看,就是很久很久都没有响应。

  • 引发网络阻塞。每次获取大 key 产生的网络流量较大,如果一个 key 的大小是 1 MB,每秒访问量为 1000,那么每秒会产生 1000MB 的流量,这对于普通千兆网卡的服务器来说是灾难性的。

  • 阻塞工作线程。如果使用 del 删除大 key 时,会阻塞工作线程,这样就没办法处理后续的命令。

  • 内存分布不均。集群模型在 slot 分片均匀情况下,会出现数据和查询倾斜情况,部分有大 key 的 Redis 节点占用内存多,QPS 也会比较大。

如何避免大 Key 呢?

最好在设计阶段,就把大 key 拆分成一个一个小 key。或者,定时检查 Redis 是否存在大 key ,如果该大 key 是可以删除的,不要使用 DEL 命令删除,因为该命令删除过程会阻塞主线程,而是用 unlink 命令(Redis 4.0+)删除大 key,因为该命令的删除过程是异步的,不会阻塞主线程。

完!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1544069.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot3+JPA+MySQL实现多数据源的读写分离(基于EntityManagerFactory)

1、简介 在Spring Boot中配置多个数据源并实现自动切换EntityManager,这里我编写了一个RoutingEntityManagerFactory和AOP(面向切面编程)的方式来实现。 这里我配置了两个数据源:primary和secondary,其中primary主数…

Unity连接MySQL踩坑,问题处理记录

用的unity2021版本,MySQL是官方下载的最新版8.0.36. 安装MySQL时,过去如果安装过,一定要删干净,单纯的卸载不行,网上有很多教程。 MySQL安装完成后,将安装目录的MySql.Data.dll文件放入unity项目的Plugin…

【Leetcode每日一题】模拟 - 替换所有的问号(难度⭐)

1. 题目解析 题目链接:1576. 替换所有的问号 这个问题的理解其实相当简单,只需看一下示例,基本就能明白其含义了。 2.算法原理 遍历字符串:从左到右逐个处理字符。 处理问号字符:对于每个问号字符,我们需…

[蓝桥杯 2020 省 AB1] 网络分析

一开始写的暴力合并 卡n^2过的不是正解 看正解是类似 虚拟点树形DP的思路 很巧妙 记录一下 #include<bits/stdc.h> using namespace std; using ll long long; using pii pair<int,int>; #define int long long const int N 3e510; const int inf 0x3f3f3f3f; …

MyBatis3源码深度解析(二十一)动态SQL实现原理(二)动态SQL解析过程、#{}和${}的区别

文章目录 前言8.5 动态SQL解析过程8.5.1 SQL配置转换为SqlSource对象8.5.2 SqlSource转换为静态SQL语句 8.6 #{}和${}的区别8.7 小结 前言 在【MyBatis3源码深度解析(二十)动态SQL实现原理(一)动态SQL的核心组件】中研究了MyBatis动态SQL相关的组件&#xff0c;如SqlSource用于…

git在单分支(自己分支)上的操作流程

文章目录 一、git命令整体操作流程&#xff08;了解&#xff09;二、idea中git操作流程&#xff08;常用-图文&#xff09;1、add2、commit&#xff0c;提交代码3、pull 拉取最新代码4、push 推送代码到远程仓库5、最后就可以在远程仓库中看你提交的代码了。 平时在idea中&…

MySQL数据库的下载及安装教程

MySQL是一个数据库管理系统&#xff0c;允许您管理关系数据库。它是Oracle支持的开源软件&#xff0c;本文介绍如何下载、安装和启动&#xff0c;便于新手快速启动学习之旅&#xff0c;具体如下&#xff1a; 一、下载MySQL 1.打开MySQL官网&#xff08;https://www.mysql.com…

第九届蓝桥杯大赛个人赛省赛(软件类)真题C 语言 A 组-第几个幸运数字

幸运数字是可以被3,5,7任一整除的数字&#xff0c;列举小明号码内的所有可能组合并计数。注意别忘了把1占的一位减去。 #include<stdio.h> typedef long long ll; int main(){long long ans 0, n 59084709587505LL;for(ll i 1; i < n; i * 3){//计算小于等于n的数…

面试笔记——Redis(分布式锁的使用场景及实现原理)

分布式锁的使用场景 资源竞争控制&#xff1a;多个客户端同时访问共享资源时&#xff0c;可以使用分布式锁来控制资源的并发访问&#xff0c;防止多个客户端同时对同一资源进行修改造成数据不一致的问题。 避免重复操作&#xff1a;在分布式环境中&#xff0c;可能会出现多个客…

rmvb是什么文件格式?rmvb格式怎么改成mp4?

RMVB&#xff0c;全称RealMedia Variable Bitrate&#xff0c;是由RealNetworks公司开发的一种视频文件格式。其产生背景可追溯至上世纪90年代&#xff0c;为了解决传输和存储上的挑战&#xff0c;RealNetworks公司致力于推出一种更为高效的解决方案。于是&#xff0c;RMVB问世…

还在用传统知识库?AI知识库才是企业的最优选择

在数字化和信息化日趋严重的时代&#xff0c;企业不仅要处理海量的数据&#xff0c;同时还要有效地管理和利用它们。这就使得知识库&#xff0c;作为一种集中存储、管理和共享知识资源的工具&#xff0c;被越来越多的企业所重视。然而&#xff0c;随着技术的快速迭代&#xff0…

Django之Celery篇(一)

一、介绍 Celery是由Python开发、简单、灵活、可靠的分布式任务队列,是一个处理异步任务的框架,其本质是生产者消费者模型,生产者发送任务到消息队列,消费者负责处理任务。 Celery侧重于实时操作,但对调度支持也很好,其每天可以处理数以百万计的任务。特点: 简单:熟悉…

ElasticSearch8 - 基本操作

前言 本文记录 ES 的一些基本操作&#xff0c;就是对官方文档的一些整理&#xff0c;按自己的习惯重新排版&#xff0c;凑合着看。官方的更详细&#xff0c;建议看官方的。 下文以 books 为索引名举例。 新增 添加单个文档 (没有索引会自动创建) POST books/_doc {"n…

消息队列八股

RabbitMQ 确保消息不丢失 重复消费问题 延迟队列 消息堆积 高可用 很少使用 Kafka 如何保证消息不丢失 回调接口保证生产者发送到brocker消息不丢失 保证消息顺序性 高可用机制 数据清理机制 实现高性能的设计

mysql - 缓存

缓存 InnoDB存储引擎在处理客户端的请求时&#xff0c;当需要访问某个页的数据时&#xff0c;就会把完整的页的数据全部加载到内存中&#xff0c;也就是说即使我们只需要访问一个页的一条记录&#xff0c;那也需要先把整个页的数据加载到内存中。将整个页加载到内存中后就可以…

学习vue3第十一节(依赖注入:provide/inject)

本机介绍&#xff1a;provide/inject 注意&#xff1a;大家在看此小节时候&#xff0c;默认大家已经了解一些组件的使用方法 1、依赖注入的用途&#xff1a; 当嵌套层级多的时候&#xff0c;某个子组件需要较远层级的父组件数据时候&#xff0c;如果我们依然使用props 传递数…

virtualbox导入vdi

新建虚拟机 点击新建 输入新建属性 配置cpu和内存 虚拟硬盘 这里选择已有的vdi文件 摘要 这里点击完成 虚拟机添加成功 点击启动&#xff0c;启动虚拟机 注意 这个时候的ip&#xff0c;还是以前镜像的ip&#xff0c;如果两个镜像一起启动&#xff0c;则需要修 改ip地…

802.1X网络访问控制协议

802.1X是一种由IEEE&#xff08;电气和电子工程师协会&#xff09;制定的网络访问控制协议&#xff0c;主要用于以太网和无线局域网&#xff08;WLAN&#xff09;中基于端口的网络接入控制。802.1X协议通过认证和授权机制&#xff0c;确保只有合法的用户和设备才能够接入网络&a…

Java中static、final关键字【详解】

文章目录 一、static关键字1.1 成员变量1.1.1 静态变量及其访问1.1.2 实例变量及其访问 1.2 成员方法1.2.1 静态方法及其访问1.2.2 实例方法及其访问 1.3 小结1.4 static应用知识 二、final关键字2.1 修饰类&#xff1a;不能被继承2.2 修饰方法2.3 修饰变量-局部变量2.3.1 局部…

背景减除(1)--bgslibrary Windows编译和使用

入侵监控领域中&#xff0c;在固定场景下&#xff0c;需要检测和监控的入侵物体种类繁多&#xff0c;无法具体穷尽。传统的CV算法提取的特征应用场景有限&#xff0c;无法完成大量物体的监控&#xff1b;深度学习目标检测方法没法收集到无穷无尽的物体种类&#xff0c;因此监督…