金三银四面试必问:Redis真的是单线程吗?

文章目录

  • 01 Redis中的多线程
    • 1)redis-server:
    • 2)jemalloc_bg_thd
    • 3)bio_xxx:
  • 02 I/O多线程
  • 03 Redis中的多进程
  • 04 结论
  • ▼延伸阅读



图片

由面试题“Redis是否为单线程”引发的思考
作者:李乐
来源:IT阅读排行榜

很多人都遇到过这么一道面试题:Redis是单线程还是多线程?这个问题既简单又复杂。说他简单是因为大多数人都知道Redis是单线程,说复杂是因为这个答案其实并不准确。

难道Redis不是单线程?我们启动一个Redis实例,验证一下就知道了。Redis安装部署方式如下所示:

// 下载
wget https://download.redis.io/redis-stable.tar.gz
tar -xzvf redis-stable.tar.gz
// 编译安装
cd redis-stable
make
// 验证是否安装成功
./src/redis-server -v
Redis server v=7.2.4

接下来启动Redis实例,使用命令ps查看所有线程,如下所示:

// 启动Redis实例
./src/redis-server ./redis.conf// 查看实例进程ID
ps aux | grep redis
root     385806  0.0  0.0 245472 11200 pts/2    Sl+  17:32   0:00 ./src/redis-server 127.0.0.1:6379// 查看所有线程
ps -L -p 385806PID    LWP TTY          TIME CMD
385806 385806 pts/2    00:00:00 redis-server
385806 385809 pts/2    00:00:00 bio_close_file
385806 385810 pts/2    00:00:00 bio_aof
385806 385811 pts/2    00:00:00 bio_lazy_free
385806 385812 pts/2    00:00:00 jemalloc_bg_thd
385806 385813 pts/2    00:00:00 jemalloc_bg_thd

竟然有6个线程!不是说Redis是单线程吗?怎么会有这么多线程呢?

这6个线程的含义你可能不太了解,但是通过这个示例至少说明Redis并不是单线程。

01 Redis中的多线程

接下来我们逐个介绍上述6个线程的作用:

1)redis-server:

主线程,用于接收并处理客户端请求。

2)jemalloc_bg_thd

jemalloc 是新一代的内存分配器,Redis底层使用他管理内存。

3)bio_xxx:

以bio前缀开始的都是异步线程,用于异步执行一些耗时任务。其中,线程bio_close_file用于异步删除文件,线程bio_aof用于异步将AOF文件刷到磁盘,线程bio_lazy_free用于异步删除数据(懒删除)。

需要说明的是,主线程是通过队列将任务分发给异步线程的,并且这一操作是需要加锁的。主线程与异步线程的关系如下图所示:

图片

主线程与异步线程 主线程与异步线程 主线程与异步线程
这里我们以懒删除为例,讲解为什么要使用异步线程。Redis是一款内存数据库,支持多种数据类型,包括字符串、列表、哈希表、集合等。思考一下,删除(DEL)列表类型数据的流程是怎样的呢?第一步从数据库字典中删除该键值对,第二步遍历并删除列表中的所有元素(释放内存)。想想如果列表中的元素数目非常多呢?这一步将非常耗时。这种删除方式称为同步删除,流程如下图所示:

图片

同步删除流程图 同步删除流程图 同步删除流程图
针对上述问题,Redis提出了懒删除(异步删除),主线程在收到删除命令(UNLINK)时,首先从数据库字典中删除该键值对,随后再将删除任务分发给异步线程bio_lazy_free,由异步线程执行第二步耗时逻辑。这时候的流程如下图所示:

图片

懒删除流程图 懒删除流程图 懒删除流程图

02 I/O多线程

难道Redis是多线程?那为什么我们老说Redis是单线程呢?这是因为读取客户端命令请求,执行命令以及向客户端返回结果都是在主线程完成的。不然的话,多线程同时操作内存数据库,并发问题如何解决?如果每次操作之前都加锁,那和单线程又有什么区别呢?

当然这一流程在Redis6.0版本也发生了改变,Redis官方指出,Redis是基于内存的键值对数据库,执行命令的过程是非常快的,读取客户端命令请求和向客户端返回结果(即网络I/O)通常会成为Redis的性能瓶颈。

因此,在Redis 6.0版本,作者加入了多线程I/O的能力,即可以开启多个I/O线程,并行读取客户端命令请求,并行向客户端返回结果。I/O多线程能力使得Redis性能提升至少一倍。

为了开启多线程I/O能力,需要先修改配置文件redis.conf:

io-threads-do-reads yes
io-threads 4

这两个配置含义如下:

  • io-threads-do-reads:是否开启多线程I/O能力,默认为"no";

  • io-threads:I/O线程数目,默认为1,即只使用主线程执行网络I/O,线程数最大为128;该配置应该根据CPU核数设置,作者建议,4核CPU设置2~3个I/O线程,8核CPU设置6个I/O线程。

开启多线程I/O能力之后,重新启动Redis实例,查看所有线程,结果如下:

ps -L -p 104648PID    LWP TTY          TIME CMD
104648 104648 pts/1    00:00:00 redis-server
104648 104654 pts/1    00:00:00 io_thd_1
104648 104655 pts/1    00:00:00 io_thd_2
104648 104656 pts/1    00:00:00 io_thd_3
……

由于我们设置了io-threads等于4,所以会创建4个线程用于执行I/O操作(包括主线程),上述结果符合预期。

当然,只有I/O阶段才使用了多线程,处理命令请求还是单线程,毕竟多线程操作内存数据存在并发问题。

最后,开启了I/O多线程之后,命令的执行流程如下图所示:

图片

I / O 多线程流程图 I/O多线程流程图 I/O多线程流程图

03 Redis中的多进程

Redis还有多进程?是的。在某些场景下,Redis也会创建多个子进程来执行一些任务。以持久化为例,Redis支持两种类型的持久化:

  • AOF(Append Only File):可以看作是命令的日志文件,Redis会将每一个写命令都追加到AOF文件。

  • RDB(Redis Database):以快照的方式存储Redis内存中的数据。命令SAVE用于手动触发RDB持久化。想想如果Redis中的数据量非常大,持久化操作必然耗时比较长,而Redis是单线程处理命令请求,那么当命令SAVE的执行时间过长时,必然会影响其他命令的执行。

命令SAVE有可能会阻塞其他请求,为此,Redis又引入了命令BGSAVE,该命令会创建一个子进程来执行持久化操作,这样就不会影响主进程执行其他请求了。

我们可以手动执行命令BGSAVE验证。首先,使用GDB跟踪Redis进程,添加断点,让子进程阻塞在持久化逻辑。如下所示:

// 查询Redis进程ID
ps aux | grep redis
root     448144  0.1  0.0 270060 11520 pts/1    tl+  17:00   0:00 ./src/redis-server 127.0.0.1:6379// GDB跟踪进程
gdb -p 448144// 跟踪创建的子进程(默认GDB只跟踪主进程,需手动设置)
(gdb) set follow-fork-mode child
// 函数rdbSaveDb用于持久化数据快照
(gdb) b rdbSaveDb
Breakpoint 1 at 0x541a10: file rdb.c, line 1300.
(gdb) c

设置好断点之后,使用Redis客户端发送命令BGSAVE,结果如下:

// 请求立即返回
127.0.0.1:6379> bgsave
Background saving started// GDB输出以下信息
[New process 452541]
Breakpoint 1, rdbSaveDb (...) at rdb.c:1300

可以看到,GDB目前跟踪的是子进程,进程ID是452541。也可以通过Linux命令 ps 查看所有进程,结果如下:

ps aux | grep redis
root     448144  0.0  0.0 270060 11520 pts/1    Sl+  17:00   0:00 ./src/redis-server 127.0.0.1:6379
root     452541  0.0  0.0 270064 11412 pts/1    t+   17:19   0:00 redis-rdb-bgsave 127.0.0.1:6379

可以看到子进程的名称是redis-rdb-bgsave,也就是该进程将所有数据的快照持久化在RDB文件。

最后再思考两个问题。

  • 问题1:为什么采用子进程而不是子线程呢?

因为RDB是将数据快照持久化存储,如果采用子线程,主线程与子线程将会共享内存数据,主线程在持久化的同时还会修改内存数据,这有可能导致数据不一致。而主进程与子进程的内存数据是完全隔离的,不存在此问题。

  • 问题2:假设Redis内存中存储了10GB的数据,在创建子进程执行持久化操作之后,此时子进程也需要10GB的内存吗?复制10GB的内存数据,也会比较耗时吧?另外如果系统只有15GB的内存,还能执行BGSAVE命令吗?

这里有一个概念叫写时复制(copy on write),在使用系统调用fork创建子进程之后,主进程与子进程的内存数据暂时还是共享的,但是当主进程需要修改内存数据时,系统会自动将该内存块复制一份,以此实现内存数据的隔离。

命令BGSAVE的执行流程如下图所示:

图片

B G S A V E 执行流程 BGSAVE执行流程 BGSAVE执行流程

04 结论

Redis的进程模型/线程模型还是比较复杂的,这里也只是简单介绍了部分场景下的多线程以及多进程,其他场景下的多线程、多进程还有待读者自己研究。

作者介绍
李乐:好未来Golang开发专家、西安电子科技大学硕士,曾就职于滴滴,乐于钻研技术与源码,合著有《高效使用Redis:一书学透数据存储与高可用集群》《Redis5设计与源码分析》《Nginx底层设计与源码分析》。

▼延伸阅读

在这里插入图片描述

《高效使用 R e d i s :一书学透数据存储与高可用集群》 《高效使用Redis:一书学透数据存储与高可用集群》 《高效使用Redis:一书学透数据存储与高可用集群》

推荐语:深入Redis数据结构与底层实现,攻克Redis数据存储与集群管理难题。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

工业锅炉物联网:HiWoo Cloud为工业能源转型注入新动力

随着全球工业化的快速发展,工业锅炉作为工业生产的“心脏”,其能源效率和运行安全性越来越受到关注。然而,传统的工业锅炉管理往往依赖于人工监控和定期维护,这种方式不仅效率低下,而且难以确保设备的持续高效运行。在…

WebServer -- 日志系统(下)

目录 🌼整体思路 🎂基础API fputs 可变参数宏 __VA_ARGS__ fflush 🚩流程图与日志类定义 流程图 日志类定义 🌼功能实现 生成日志文件 && 判断写入方式 日志分级与分文件 🌼整体思路 日志系统分两部…

Figma 最新版下载:无需激活码,轻松安装!

从事设计工作,怎么能没有设计工具呢?我相信许多设计师也必须使用Figma这样的软件,真的可以让我们的设计工作更有效率,但我相信你也发现Figma属于外国软件,自然语言也是英语,直到现在没有中文版本&#xff0…

【Kubernetes】安装K3S

目录 前言一、原理单体架构高可用架构 二、初始化1.配置yum源2.关掉防火墙3.关掉selinux4. 修改内核参数5.关掉swap交换分区 三、安装master节点1. 安装container2.启动master服务 四、安装node节点五、卸载六、总结 前言 各位小伙伴们,大家好,小涛又来…

自定义神经网络四之编写自定义神经网络

文章目录 前言神经网络组件代码整体的项目结构Tensor张量Layers层NeuralNet神经网络Loss损失函数Optim优化器data数据处理train训练 神经网络解决实际问题实际问题训练和推理代码 总结 前言 自定义神经网络一之Tensor和神经网络 自定义神经网络二之模型训练推理 自定义神经网络…

关于电脑一天24小时多少度电电脑的一天用电量计算

随着这几年物价的上涨,一些地区的电价越来越高,而我们经常需要使用电脑,那么一台电脑一天24小时用多少度电呢? 如何计算电脑一天的用电量? 让我们跟随小编来了解更多吧。 1、功耗、主机箱功耗 现在的计算机中&#xf…

(Linux学习一):Mac安装vmWare11.5,centOS 7安装步骤教程

一。下载vmware 官网地址:下载地址 由于我的电脑系统是Mac 10.15.6版本系统,我下载的是VMware Fusion 11.5版本,13是最新版本不支持安装需要系统在11以上。 百度网盘下载地址: VMware Fusion 11 VMware Fusion 12 VMware Fusion 13 下载需要…

插值、逼近、拟合、光顺

插值 插值(Interpolation)是数学和计算科学中的一个重要概念,它指的是通过已知的一系列数据点,构造一个函数或曲线,并据此估计未知数据点的值。这个过程通常发生在已知数据点之间,用于预测或估算在这些已知…

Android Duplicate class 排除重复类

一、起因: 在迭代开发的时候,发现2个ijk很多类重复。但又2个库实现的功能是不一样,目前不能合并。但又想保留2个功能。需要排除其中一个库。 二、报错如何下图: 三、解决方法: 3.1 在terminal 也就是命令行处输入 …

Project_Euler-29 题解

Project_Euler-29 题解 题目 思路 如果暴力破解的话会有一个问题,那就是数值过大的问题,那我们就需要通过对数据结构来进行操作,这样的话会让代码变得很臃肿。 优化思路 其实,对于一个数值,我们并不需要要将其计算出…

Mysql数据库管理系统学习笔记1——sql语句,DBMS,数据库的分类

mysql是一种数据库管理系统(DBMS),data base manage system sql语句即为“structured query language”,结构化查询语言 数据库的分类:关系型数据库(RDBMS)与非关系型数据库 对于一些具有相同…

如何在aws服务器上部署mysql

在AWS服务器上部署 MySQL 数据库可以通过以下步骤完成: 启动 EC2 实例: 在 AWS 控制台中启动一个 EC2 实例,选择适合你需求的实例类型和配置。 安全组配置: 确保你的 EC2 实例的安全组配置允许来自你的 IP 地址的 MySQL 连接。默…

【全志D1-H 哪吒开发板】Debian系统安装调教和点灯指南

全志D1-H开发板【哪吒】使用Deabian系统入门 特别说明: 因为涉及到操作较多,博文可能会导致格式丢失 其中内容,会根据后续使用做优化调整 目录: 参考资料固件烧录启动调教点灯问题 〇、参考资料 官方资料 开发板-D1开发板【…

VR元宇宙的概念|VR体验店加盟|虚拟现实设备销售

VR元宇宙是一个结合了虚拟现实(Virtual Reality)和增强现实(Augmented Reality)等技术的概念,代表着一个虚拟的多维度世界。它是一个由数字化的空间构成的虚拟环境,可以通过虚拟现实设备进行交互和探索。 元…

Linux安装Nginx详细步骤

1、创建两台虚拟机,分别为主机和从机,区别两台虚拟机的IP地址 2、将Nginx素材内容上传到/usr/local目录(pcre,zlib,openssl,nginx) 附件 3、安装pcre库   3.1 cd到/usr/local目录 3.2 tar -zxvf pcre-8.36.tar.gz 解压 3.3 cd…

goland配置新增文件头

参考: goland函数注释生成插件 goland函数注释生成插件_goland自动加函数说明-CSDN博客 GoLand 快速添加方法注释 GoLand 快速添加方法注释_goland批量注释-CSDN博客 goland 如何设置头注释,自定义author和data goland 如何设置头注释,自定…

【北京迅为】《iTOP-3588开发板网络环境配置手册》第2章 电脑、开发板直连交换机或路由器

RK3588是一款低功耗、高性能的处理器,适用于基于arm的PC和Edge计算设备、个人移动互联网设备等数字多媒体应用,RK3588支持8K视频编解码,内置GPU可以完全兼容OpenGLES 1.1、2.0和3.2。RK3588引入了新一代完全基于硬件的最大4800万像素ISP&…

通过大语言模型理解运维故障:评估和总结

张圣林 南开大学软件学院副教授、博士生导师 第六届CCF国际AIOps挑战赛程序委员会主席 在ATC、WWW、VLDB、KDD、SIGMETRICS等国际会议和JSAC、TC、TSC等国际期刊发表高水平论文50余篇。主持国家自然科学基金项目2项,横向项目13项(与华为、字节跳动、腾讯…

resilience4j 2.0.0版本使用要求最低JDK17(使用踩坑记录)

文章目录 🔊博主介绍🥤本文内容📢文章总结📥博主目标 🔊博主介绍 🌟我是廖志伟,一名Java开发工程师、Java领域优质创作者、CSDN博客专家、51CTO专家博主、阿里云专家博主、清华大学出版社签约作…

非线性优化资料整理

做课题看了一些非线性优化的资料,整理一下,以方便查看: 优化的中文博客 数值优化|笔记整理(8)——带约束优化:引入,梯度投影法 (附代码)QP求解器对比对于MPC的QP求解器 数值优化| 二次规划的…