每日一学—由面试题“Redis 是否为单线程”引发的思考

文章目录

  • 📋 前言
  • 🌰 举个例子
  • 🎯 什么是 Redis(知识点补充)
  • 🎯 Redis 中的多线程
  • 🎯 I/O 多线程
  • 🎯 Redis 中的多进程
  • 📝 结论
  • 🎯书籍推荐
  • 🔥参与方式


在这里插入图片描述

📋 前言

很多后端或运维程序员再面试中都遇到过这么一道面试题: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

竟然有 6 个线程!不是说 Redis 是单线程吗?怎么会有这么多线程呢?这 6 个线程的含义你可能不太了解,但是通过这个示例至少说明 Redis 并不是单线程。

🎯 什么是 Redis(知识点补充)

基于很多前端开发的博友不一定了解 Redis,这里做一个简单介绍和了解。

Redis 是一种开源的内存数据库(In-Memory Database),也被称为数据结构服务器,它支持多种数据结构,如字符串(Strings)、哈希表(Hashes)、列表(Lists)、集合(Sets)和有序集合(Sorted Sets)等。Redis 的出色之处在于其高性能、简单且灵活的键值对存储方式,以及丰富的功能和数据结构支持。以下是 Redis 的一些主要特点和用途:

  • 高性能:Redis 将数据存储在内存中,并通过异步写入磁盘或定期写入磁盘来保证数据持久化,因此具有非常快的读写速度。
  • 数据结构丰富:除了支持基本的键值对存储外,Redis还支持多种数据结构,如字符串、哈希表、列表、集合和有序集合,使其适用于各种不同的应用场景。
  • 持久化支持:Redis支持RDB持久化和AOF持久化两种方式,可以根据需求选择合适的持久化方式来保证数据的安全性。
  • 发布与订阅:Redis支持发布与订阅功能,可以实现消息的广播和订阅模式,非常适合构建实时消息系统。
  • 缓存:Redis常被用作缓存数据库,可以帮助加速访问速度,降低后端服务器的压力。
  • 计数器:由于Redis的原子操作支持,可以方便地实现计数器功能,如网站点击量统计等。
  • 分布式锁:Redis的分布式锁功能可以帮助解决分布式系统中的并发控制问题。

🎯 Redis 中的多线程

回到主题,接下来我们逐个介绍上述 6 个线程的作用:

  • redis-server:主线程,用于接收并处理客户端请求。

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

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

需要说明的是,主线程是通过队列将任务分发给异步线程的,并且这一操作是需要加锁的。主线程与异步线程的关系如下图所示:
在这里插入图片描述
这里我们以懒删除为例,讲解为什么要使用异步线程。Redis 是一款内存数据库,支持多种数据类型,包括字符串、列表、哈希表、集合等。思考一下,删除(DEL)列表类型数据的流程是怎样的呢?第一步从数据库字典中删除该键值对,第二步遍历并删除列表中的所有元素(释放内存)。想想如果列表中的元素数目非常多呢?这一步将非常耗时。这种删除方式称为同步删除,流程如下图所示:
在这里插入图片描述
针对上述问题,Redis 提出了懒删除(异步删除),主线程在收到删除命令(UNLINK)时,首先从数据库字典中删除该键值对,随后再将删除任务分发给异步线程 bio_lazy_free,由异步线程执行第二步耗时逻辑。这时候的流程如下图所示:
在这里插入图片描述


🎯 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 多线程之后,命令的执行流程如下图所示:
在这里插入图片描述


🎯 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 内存中存储了 10 GB的数据,在创建子进程执行持久化操作之后,此时子进程也需要 10 GB的内存吗?复制 10 GB的内存数据,也会比较耗时吧?另外如果系统只有 15 GB的内存,还能执行 BGSAVE 命令吗?

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

命令 BGSAVE 的执行流程如下图所示:
在这里插入图片描述


📝 结论

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

🎯书籍推荐

Redis 是一款非常受欢迎的开源内存数据存储系统,具有高性能、可扩展、灵活等优点,在互联网和大数据领域得到了广泛应用。为了帮助读者更好地理解和应用 Redis,需要一本既有理论又有实践、通俗易懂的 Redis 书籍。于是,本书诞生了。

本书将介绍 Redis 的基础知识,包括 Redis 的数据结构、数据存储方式、命令和使用场景等,同时深入探讨 Redis 的高级应用,如 Redis 集群、持久化、性能优化等。

本书将通过丰富的案例帮助读者更好地理解和掌握 Redis,使读者能够快速上手并在实际项目中应用 Redis。

在这里插入图片描述

🔥参与方式

《高效使用Redis:一书学透数据存储与高可用集群》免费包邮送出 3 本!

抽奖方式:评论区随机抽取 3 位小伙伴免费送出!
参与方式:关注博主、点赞、收藏、评论区评论 (随机有效留言即可)(切记要点赞+收藏,否则抽奖无效,每个人最多评论三次!)
活动截止时间:2024-2-28 22:00:00
当当网购买链接*:https://product.dangdang.com/29667600.html
在这里插入图片描述

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

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

相关文章

K线实战分析系列之五:刺透形态——多方反攻信号

K线实战分析系列之五:刺透形态——多方反攻信号 一、刺透形态二、类似刺透形态三、刺透形态的总结 一、刺透形态 阴线在前,阳线在后显示市场曾经跌到了低位,但是在盘中又将价格收回,并且多方收复了前一天大部分的失地 二、类似刺…

[SUCTF 2019]EasySQL1 题目分析与详解

一、题目介绍 1、题目来源: BUUCTF网站,网址:https://buuoj.cn/challenges 2、题目描述: 通过以上信息,拿到flag。 二、解题思路 首先打开靶机,尝试输入1查看回显,回显如图所示:…

利用psutil库检查脚本是否在运行

摘要 如果要判断某一脚本是否在运行,可以通过psutil库获取所有进程的cmdline,并判断指定的文件名是否在cmdline中。 目录 1.psutil库简介 2.检查代码及说明 2.1检查思路 2.2异常捕获 2.3执行方法 1.psutil库简介 psutil 是一个跨平台(…

记一次生产jvm oom问题

前言 jvm添加以下参数,发生OOM时自动导出内存溢出文件 -XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPath/opt 内存分析工具: MAT, 下载地址:Eclipse Memory Analyzer Open Source Project | The Eclipse Foundation, 注意工具地址…

让AI玩一千万次贪吃蛇

如果让人工智能来玩贪吃蛇游戏,会发生什么? 图源:DALLE 目录 贪吃蛇实现 游戏规则 游戏实现 Q学习算法实现 Q学习简介 Q表和Q值 Q学习更新规则 Q学习在贪吃蛇游戏中的应用 整体项目完整代码 运行过程截图 代码分析 环境设置 …

2024.2.23 C++QT 作业

思维导图 练习题 1>简单实现闹钟播报,设置时间,当系统时间与设置时间相同时播报语音5次,然后停止。如果设置时间小于当前系统时间,则弹出消息提示框,并清空输入框。 #include "widget.h" #include &quo…

JSON(javaScript Object Notation,Js对象标记)—我耀学IT

Json是一种轻量级的数据交换格式,目前使用非常广泛,是一种轻量级的数据交换格式。易于人阅读和编写,可以在多种语言之间进行数据交换 。同时也易于机器解析和生成 1.1json的值: 值可以是对象、数组、数字、字符串或者三个字面值(false、nul…

什么是nginx 、安装nginx、nginx调优

一、 什么是nginx 1.1 nginx的概念 一款高新能、轻量级Web服务软件系统资源消耗低对HTTP并发连接的处理能力高单台物理服务器可支持30 000~50 000个并发请求。 1.2 nginx模块与作用 核心模块:是 Nginx 服务器正常运行必不可少的模块,提供错…

深入理解JS的执行上下文、词法作用域和闭包(下)

🤍 前端开发工程师、技术日更博主、已过CET6 🍨 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 🕠 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 🍚 蓝桥云课签约作者、上架课程《Vue.js 和 E…

【Java程序设计】【C00262】基于Springboot的会员制医疗预约服务管理系统(有论文)

基于Springboot的会员制医疗预约服务管理系统(有论文) 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于Springboot的会员制医疗预约服务管理信息系统,本系统分为三种角色:管理员、医生和会员; 在系统…

JavaSec 基础之 XXE

文章目录 XMLReaderSAXReaderSAXBuilderDocumentBuilderUnmarshaller**SAXParserFactory**XMLReaderFactoryDigester总结 XMLReader public String XMLReader(RequestBody String content) {try {XMLReader xmlReader XMLReaderFactory.createXMLReader();// 修复&#xff1a…

如何让电脑待机而wifi不关的操作方法!!

1、一台电脑如果一天不关机,大约消耗0.3度电。 一般一台电脑的功耗约为250-400W(台式机)。 一台电脑每月的耗电量:如果是每小时300W每天10小时每月30天90KW,即90千瓦时的电。 这只是保守估计。 2、使用完毕后正常关闭…

企业微信应用开发:使用Cpolar域名配置进行本地接口回调的调试指南

文章目录 1. Windows安装Cpolar2. 创建Cpolar域名3. 创建企业微信应用4. 定义回调本地接口5. 回调和可信域名接口校验6. 设置固定Cpolar域名7. 使用固定域名校验 企业微信开发者在应用的开发测试阶段,应用服务通常是部署在开发环境,在有数据回调的开发场…

【MATLAB】 RLMD信号分解+FFT傅里叶频谱变换组合算法

有意向获取代码,请转文末观看代码获取方式~ 展示出图效果 1 RLMD分解算法 RLMD(Robust Local Mode Decomposition)是一种鲁棒的局部模态分解方法。它是通过在局部区间内对信号进行多项式拟合,提取局部特征,进而分解信…

【Java程序设计】【C00294】基于Springboot的车辆充电桩管理系统(有论文)

基于Springboot的车辆充电桩管理系统(有论文) 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于Springboot的车辆充电桩管理系统 本系统前台功能模块分为:首页功能和用户后台管理 后台功能模块分为:管理员功能和…

达梦数据库搭建和连接(详解一文看懂)

达梦数据库搭建和连接 一、数据库搭建1.安装前准备2.下载 Docker3.导入安装包4.启动docker版docker-compose版 5.启动/停止数据库 二、数据库连接1、下载DBeaver2、下载驱动3、DBeaver新建驱动数据库-驱动管理器新建驱动创建新驱动设置创建驱动-库驱动类确定和关闭 4、连接 博主…

回归预测 | Matlab实现SSA-BiLSTM-Attention麻雀算法优化双向长短期记忆神经网络融合注意力机制多变量回归预测

回归预测 | Matlab实现SSA-BiLSTM-Attention麻雀算法优化双向长短期记忆神经网络融合注意力机制多变量回归预测 目录 回归预测 | Matlab实现SSA-BiLSTM-Attention麻雀算法优化双向长短期记忆神经网络融合注意力机制多变量回归预测预测效果基本描述程序设计参考资料 预测效果 基…

[VulnHub靶机渗透] FourandSix2.01

🍬 博主介绍👨‍🎓 博主介绍:大家好,我是 hacker-routing ,很高兴认识大家~ ✨主攻领域:【渗透领域】【应急响应】 【Java】 【VulnHub靶场复现】【面试分析】 🎉点赞➕评论➕收藏 …

stm32单片机的智能手环-心率-步数-距离-体温-蓝牙监控

一.硬件方案 随着社会的发展,人们的物质生活水平日渐提高,人们也越来越关注自己的健康。智能手环作为一种测量仪器,可以计算行走的步数和消耗的能量,所以人们可以定量的制定运动方案来健身,并根据运行情况来分析人体的…

7.(数据结构)堆

7.1 相关概念 堆(Heap)在计算机科学中是一种特殊的数据结构,它通常被实现为一个可以看作完全二叉树的数组对象。以下是一些关于堆的基本概念: 数据结构: 堆是一个优先队列的抽象数据类型实现,通过完全二叉树…