由面试题“Redis是否为单线程”引发的思考

这篇具有很好参考价值的文章主要介绍了由面试题“Redis是否为单线程”引发的思考。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

由面试题“Redis是否为单线程”引发的思考,赠书活动,redis,数据库,缓存

博主 默语带您 Go to New World.
个人主页—— 默语 的博客👦🏻
《java 面试题大全》
《java 专栏》
🍩惟余辈才疏学浅,临摹之作或有不妥之处,还请读者海涵指正。☕🍭
《MYSQL从入门到精通》数据库是开发者必会基础之一~
🪁 吾期望此文有资助于尔,即使粗浅难及深广,亦备添少许微薄之助。苟未尽善尽美,敬请批评指正,以资改进。!💻⌨


  • 每日一句正能量
  • 前言
  • 01 Redis中的多线程
  • 02 I/O多线程
  • 03 Redis中的多进程
  • 04 结论
  • 作者介绍
  • 延伸阅读
    • 推荐语
  • 赠书活动

每日一句正能量

友情之所以美,就是建立以后,不需要像情侣一样,保持紧密的联系,它可以相当松散,不少人有这样的体验:好朋友多年没见,见面话题依然不会冷,聊完以后,下次见面又是三五年后。

前言

很多人都遇到过这么一道面试题: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 385806
   PID    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是否为单线程”引发的思考,赠书活动,redis,数据库,缓存

主线程与异步线程

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

同步删除流程图

针对上述问题,Redis提出了懒删除(异步删除),主线程在收到删除命令(UNLINK)时,首先从数据库字典中删除该键值对,随后再将删除任务分发给异步线程bio_lazy_free,由异步线程执行第二步耗时逻辑。这时候的流程如下图所示:
由面试题“Redis是否为单线程”引发的思考,赠书活动,redis,数据库,缓存

懒删除流程图

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 104648
   PID    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,数据库,缓存

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的执行流程如下图所示:
    由面试题“Redis是否为单线程”引发的思考,赠书活动,redis,数据库,缓存

BGSAVE执行流程

04 结论

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

作者介绍

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

延伸阅读

由面试题“Redis是否为单线程”引发的思考,赠书活动,redis,数据库,缓存

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

推荐语

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

赠书活动

  • 🎁本次送书1~4本,【取决于阅读量,阅读量越多,送的越多】👈
  • ⌛️活动时间:截止到2024年3月02日
  • ✳️参与方式:关注博主+三连(点赞、收藏、评论)

由面试题“Redis是否为单线程”引发的思考,赠书活动,redis,数据库,缓存


🪁🍁 希望本文能够给您带来一定的帮助🌸文章粗浅,敬请批评指正!🍁🐥

如对本文内容有任何疑问、建议或意见,请联系作者,作者将尽力回复并改进📓;(联系微信:Solitudemind )

点击下方名片,加入IT技术核心学习团队。一起探索科技的未来,共同成长。

由面试题“Redis是否为单线程”引发的思考,赠书活动,redis,数据库,缓存文章来源地址https://www.toymoban.com/news/detail-852965.html

到了这里,关于由面试题“Redis是否为单线程”引发的思考的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 由C# yield return引发的思考

        当我们编写 C# 代码时,经常需要处理大量的数据集合。在传统的方式中,我们往往需要先将整个数据集合加载到内存中,然后再进行操作。但是如果数据集合非常大,这种方式就会导致内存占用过高,甚至可能导致程序崩溃。     C# 中的 yield return 机制可以帮助我们

    2024年02月07日
    浏览(44)
  • 阿里云无影云电脑初体验及引发的思考

    有幸尝试阿里无影云电脑,记录下使用过程,并对云电脑进行思考。 ​ 阿里云无影云桌面( Elastic Desktop Service)的原产品名为弹性云桌面,融合了无影产品技术后更名升级。它可以为您提供易用、安全、高效的云上桌面服务,帮助您快速构建、高效管理桌面办公环境,提供安全

    2024年02月05日
    浏览(42)
  • ChatGPT引发的人机交互发展历程与升级思考

    ChatGPT自从去年12月火爆以来一直热度不减,最近正好研读了科技之巅,书中详细阐述了人机交互、人工智能、算力算法等技术的发展历史,本文主要围绕ChatGPT引发的人机交互方面的思考。 在讨论人机交互之前,首先需要说明的一点,目前计算机发展的结果已经从原来作为科

    2023年04月24日
    浏览(51)
  • 一个vuepress配置问题,引发的js递归算法思考

    这两天在尝试用语雀+ vuepress + github 搭建个人博客。 小破站地址 :王天的 web 进阶之路 语雀作为编辑器,发布文档推送 github,再自动打包部署,大概流程如下。 我使用的 elog 插件批量导出语雀文档。 elog 采用的配置是所有文章平铺导出,没有按照语雀知识库目录生成 m

    2024年02月08日
    浏览(39)
  • 周星驰进军web3引发怎样的思考?

    谁能想到,向来低调的 周星驰 首次注册社交账号,竟是为了发布一条招人信息,挑选的还不是新片男女主角,而是Web3人才,一脚跨界到了互联网科技领域。 从年初就开始爆火的概念——Web3,到底是什么?能给我们带来什么?周星驰可能会往哪个方向创业? 要理解Web3,可能

    2023年04月08日
    浏览(42)
  • 由黑塞(Hessian)矩阵引发的关于正定矩阵的思考

    最近看论文,发现论文中有通过黑塞(Hessian)矩阵提高电驱系统稳定性的应用。所以本篇主要从Hessian矩阵的性质出发,对其中正定矩阵的判定所引发的想法进行记录。 (其实看论文出现黑塞很惊奇,因为前不久刚读了作家黑塞的《德米安:彷徨少年时》,所以在这一领域的黑塞

    2024年02月06日
    浏览(48)
  • 由 Array.includes 函数引发对引用数据类型的思考

    `` 数组的includes方法在日常的编程中比较常用到,其作用就是判断某一数据是否在数组中,通常来说,数组中的数据如果是数字,布尔值,或者字符串的话,都是能够进行判断的 例如: 如果是对象的话,那么会有一个有趣的现象发生 arr.includes({ age: 11, name: \\\'bob\\\'}) 这行,inclu

    2024年02月09日
    浏览(62)
  • 这问题巧了,SpringMVC 不同参数处理机制引发的思考

    这个问题非常有趣,不是SpringMVC 的问题,是实际开发中混合使用了两种请求方式暴露出来的。 功能模块中,提供两个 Http 服务。一个是列表查询(application/json 请求),一个是列表导出(表单请求)。运行环境发现个问题:MVC model 新添加的属性,类似的 Http 请求,一个有值

    2024年02月11日
    浏览(42)
  • 编程示例:概率论的问题——囚犯生存概率引发的循环思考

    适用于无编程经验的初学者,目的是提供一个编程的思路。 有一个囚犯,国王打算处决他,但仁慈的国王给了他一个生还的机会。 现在摆在他面前有两个瓶子,一个里面装了50个白球,一个装了50个 黑球,这个囚犯有一个机会可以随便怎样重新分配这些球到两个瓶子 中(当

    2024年02月03日
    浏览(44)
  • Unity由“鼠标点不准物体”引发的Camera的相关思考

            前段一个同事在使用Unity开发时遇到一个奇怪的问题,使用左键点击发射射线的方式选择物体,总是选不准,尤其是小的物体,鼠标点击到物体上,有时能选上,有时选不上,偶尔点击到物体旁边反而能选上,于是他让我帮看看咋回事。我第一个想法是也许代码写

    2024年02月03日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包