字节二面:Redis 的大 Key 对持久化有什么影响?

news2024/10/5 16:22:59

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 持久化的过程不会影响主线程。

对 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/136149.html

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

相关文章

Git(四) - Git 分支操作

​​​​​​​ 一、什么是分支 在版本控制过程中,同时推进多个任务,为每个任务,我们就可以创建每个任务的单独分支。使用分支意味着程序员可以把自己的工作从开发主线上分离开来,开发自己分支的时候,不会影响主线分支…

前端面试常考 | js原型与原型链

文章目录一. 什么是原型?二. 什么是原型链?一. 什么是原型? 在js中所有的引用类型都有一个__proto__(隐式原型)属性,属性值是一个普通的对象。 而在js中的引用类型包括:Object,Array,Date,Function 而所有函数都有…

基于K8s的DevOps平台实践(二)

文章目录1. 流水线入门🍑 流水线基础语法🍑 脚本示例🍑 脚本解释🍑 Blue Ocean2. Jenkinsfile实践🍑 演示一🍑 演示二🍑 演示三🍑 演示四🍑 总结3. 多分支流水线实践&…

BEV视觉3D感知算法梳理

1. 基于BEV空间的自动驾驶感知任务 最近,基于BEV空间下的感知任务已经涌现出了众多优秀算法,并在多个自动驾驶公开数据集(KITTI,Waymo,nuScenes)上取得了非常不错的成绩。根据自动驾驶汽车上安装的传感器类…

【从零开始学习深度学习】37. 深度循环神经网络与双向循环神经网络简介

目录1. 深度循环神经网络2. 双向循环神经网络总结1. 深度循环神经网络 之前介绍的循环神经网络只有一个单向的隐藏层,在深度学习应用里,我们通常会用到含有多个隐藏层的循环神经网络,也称作深度循环神经网络。下图演示了一个有LLL个隐藏层的…

数字化时代,全方位解读商业智能BI

商业智能BI是一种通用的数据类技术解决方案,不会因为行业BI没有进行针对性开发而出现不适配、无法使用的情况。同时,也正因为商业智能BI核心是数据,只要企业有数据沉淀,不管是哪些行业BI商业智能都能发挥出作用。 不过考虑到不同…

文件IO操作开发笔记(一):使用Qt的QFile对磁盘文件存储进行性能测试以及测试工具

文为原创文章,转载请注明原文出处 本文章博客地址:https://hpzwl.blog.csdn.net/article/details/128438303 红胖子(红模仿)的博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结…

portraiture2023智能磨皮修饰滤镜插件中文版

在人像后期修图的时候免不了需要进行磨皮处理,很多人在挑选磨皮软件的时候都不知道该如何选择,今天的文章就来带大家看看磨皮软件哪个好,能磨皮的修图软件和插件!借助磨皮软件即使是新手也能做出高级的人像图片,下面挑选了几款好用…

Java 并发编程知识总结【五】

6. 线程中断与 LockSupport 6.1 线程中断机制 大厂(蚂蚁金服)面试题: 什么是中断? 首先,一个线程不应该由其他线程来强制中断或停止,而是应该由线程自己自行停止。所以,Thread.stop, Thread.…

Exynos_4412——中断控制器

目录 一、中断控制器 中断控制器的作用: 二、Exynos_4412下的中断控制器 它支持三种类型的中断 可以编程设置: 三、中断控制器详解 四、中断控制器编程 一、中断控制器 外设产生的中断信号,先要经过中断控制器,中断是异常…

如何解决软件项目管理中的冲突?

1、项目干系人间的良好沟通 项目干系人之间保持良好的沟通交流,是减少项目管理中冲突的重要手段。甲乙双方签订合同后,为保障项目的成功,在项目发生矛盾和困难时,需要双方相互理解和沟通,共同协商解决问题。 为了及时解…

Git(八) - IDEA 集成 GitHub

一、设置 GitHub 账号 二、分享工程到 GitHub 来到GitHub中发现已经帮我们创建好了git-test的远程仓库。 三、push 推送本地库到远程库 注意:push是将本地库代码推送到远程库,如果本地库代码跟远程库代码版本不一致, push的操作是会被拒绝的…

go 性能分析pprof和trace

runtime/pprof:采集程序(非 Server)的运行数据进行分析,用于可结束的代码块,如一次编解码操作等net/http/pprof:采集 HTTP Server 的运行时数据进行分析。用于不可结束的代码块,如 web 应用等 使…

​工程师如何对待开源

工程师如何对待开源 本文是笔者作为一个在知名科技企业内从事开源相关工作超过 20 年的工程师,亲身经历或者亲眼目睹很多工程师对待开源软件的优秀实践,也看到了很多 Bad Cases,所以想把自己的一些心得体会写在这里,供工程师进行…

linux的shell的概述

Shell 教程 Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁。Shell 既是一种命令语言,又是一种程序设计语言。 Shell 是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务。 Ke…

各种颜色的代码

颜色代码对照表如下: 关于16进制颜色代码: 这有必要了解一颜色系统的概念: RGB:RGB色彩模式是工业界的一种颜色标准,是通过对红(R)、绿(G)、蓝(B)三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色的&a…

MySQL的一些有意思的指令和函数

这个里面我准备记录一些比较有意思的MySQL的指令和函数,当然使用函数的时候我们要注意,会不会因为函数导致不走索引,走全表扫描的情况。 因为对索引字段做函数操作,可能会破坏索引值的有序性,因此优化器就决定放弃走树…

文本分类(LSTM+PyTorch)

本文的配套代码已上传至github,链接在文末,同时附带中文数据集。 一、传统方法的基本步骤 预处理:首先进行分词,然后是除去停用词;将文本表示成向量,常用的就是文本表示向量空间模型;进行特征…

回调函数、qsort函数、sort函数与lambda表达式

目录 目录 1、回调函数 2、sort函数 3、lambda表达式 4、qsort与sort函数使用lambda表达式 1、回调函数 回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一 个函数,当这个指针被用来调用其所指向的函…

凝心聚力 开源共建 | 统信软件参与成立OpenKunlun开源固件社区

11月22日,统信软件携手国内固件厂商、处理器厂商、外设板卡厂商等数十家产业同仁参与共建的OpenKunlun开源固件社区正式成立! OpenKunlun社区是在自愿、开源、平等和协作的基础上,由基础软硬件企业、高等院校、个人开发者共同参与的非营利性开…