众所周知,Redis在内存库数据库领域非常地火热,它极高的性能和丰富的数据结构为我们的开发提供了极大的便利。

但我们也听说了,Redis是单线程的,为什么采用单线程的Redis也会如此之快呢?这篇文章我们来分析一下其中的缘由。

其实,严格来说,Redis Server是多线程的,只是它的请求处理整个流程是单线程处理的。 这一点我们一定要清楚了解到,不要单纯地认为Redis Server是单线程的!

我们平时说的Redis单线程快是指它的请求处理过程非常地快!

下面我们就来分析一下为什么请求处理使用单线程,依旧可以达到这么高的性能。

Redis的性能非常之高,每秒可以承受10W+的QPS,它如此优秀的性能主要取决于以下几个方面:

  • 纯内存操作
  • 使用IO多路复用技术
  • 非CPU密集型任务
  • 单线程的优势

纯内存操作

Redis是一个内存数据库,它的数据都存储在内存中,这意味着我们读写数据都是在内存中完成,这个速度是非常快的。

Redis是一个KV内存数据库,它内部构建了一个哈希表,根据指定的KEY访问时,只需要O(1)的时间复杂度就可以找到对应的数据。同时,Redis提供了丰富的数据类型,并使用高效的操作方式进行操作,这些操作都在内存中进行,并不会大量消耗CPU资源,所以速度极快。

使用IO多路复用技术

Redis采用单线程,那么它是如何处理多个客户端连接请求呢?

Redis采用了IO多路复用技术和非阻塞IO,这个技术由操作系统实现提供,Redis可以方便地操作系统的API即可。Redis可以在单线程中监听多个Socket的请求,在任意一个Socket可读/可写时,Redis去读取客户端请求,在内存中操作对应的数据,然后再写回到Socket中。

整个过程非常高效,Redis利用了IO多路复用技术的事件驱动模型,保证在监听多个Socket连接的情况下,只针对有活动的Socket采取反应。

非CPU密集型任务

采用单线程的缺点很明显,无法使用多核CPU。Redis作者提到,由于Redis的大部分操作并不是CPU密集型任务,而Redis的瓶颈在于内存和网络带宽。

在高并发请求下,Redis需要更多的内存和更高的网络带宽,否则瓶颈很容易出现在内存不够用和网络延迟等待的情况。

当然,如果你觉得单个Redis实例的性能不足以支撑业务,Redis作者推荐部署多个Redis节点,组成集群的方式来利用多核CPU的能力,而不是在单个实例上使用多线程来处理。

单线程的优势

基于以上特性,Redis采用单线程已足够达到非常高的性能,所以Redis没有采用多线程模型。

另外,单线程模型还带了以下好处:

  • 没有了多线程上下文切换的性能损耗
  • 没有了访问共享资源加锁的性能损耗
  • 开发和调试非常友好,可维护性高

所以Redis正是基于以上这些方面,所以采用了单线程模型来完成请求处理的工作。

多线程优化

在文章开头已经特别说明,Redis Server本身是多线程的,除了请求处理流程是单线程处理之外,Redis内部还有其他工作线程在后台执行,它负责异步执行某些比较耗时的任务,例如AOF每秒刷盘、AOF文件重写都是在另一个线程中完成的。

而在Redis 4.0之后,Redis引入了lazyfree的机制,提供了unlink、flushall aysc、flushdb async等命令和lazyfree-lazy-eviction、lazyfree-lazy-expire等机制来异步释放内存,它主要是为了解决在释放大内存数据导致整个redis阻塞的性能问题。

在删除大key时,释放内存往往都比较耗时,所以Redis提供异步释放内存的方式,让这些耗时的操作放到另一个线程中异步去处理,从而不影响主线程的执行,提高性能。

到了Redis 6.0,Redis又引入了多线程来完成请求数据的协议解析,进一步提升性能。它主要是解决高并发场景下,单线程解析请求数据协议带来的压力。请求数据的协议解析由多线程完成之后,后面的请求处理阶段依旧还是单线程排队处理。

可见,Redis并不是保守地认为单线程有多好,也不是为了使用多线程而引入多线程。Redis作者很清楚单线程和多线程的使用场景,针对性地优化,这是非常值得我们学习的。

缺点

上面介绍了单线程可以达到如此高的性能,并不是说它就没有缺点了。

单线程处理最大的缺点就是,如果前一个请求发生耗时比较久的操作,那么整个Redis就会阻塞住,其他请求也无法进来,直到这个耗时久的操作处理完成并返回,其他请求才能被处理到。

我们平时遇到Redis变慢或长时间阻塞的问题,90%也都是因为Redis处理请求是单线程这个原因导致的。

所以,我们在使用Redis时,一定要避免非常耗时的操作,例如使用时间复杂度过高的方式获取数据、一次性获取过多的数据、大量key集中过期导致Redis淘汰key压力变大等等,这些场景都会阻塞住整个处理线程,直到它们处理完成,势必会影响业务的访问。

我会在后期的文章中专门介绍具体有哪些场景会引发Redis阻塞的问题,并提供规避问题的方法和优化方案。

总结

Redis使用单线程,配合IO多路复用技术,可以完成多个连接的请求处理。而且正是由于它的使用定位是内存数据库,这样几乎所有的操作都在内存中完成,它的性能可以达到非常之高。

同时,单线程没有了线程上下文切换和访问共享资源加锁的性能损耗,而且单线程模型对程序的开发和调试非常友好,因此Redis使用单线程模型也就在情理之中了。

Redis在最近的版本也对多线程进行了优化,用于解决释放大内存数据和请求数据协议解析对Redis产生的性能影响,进一步提升了Redis的性能。

单线程结合上述场景可以达到非常高的性能,同时也存在耗时操作阻塞整个线程的问题,我们在使用Redis时要避免耗时过长的操作,才能更好地发挥Redis的性能。

redis线程阻塞原因排插_每次面试都要被问:为什么采用单线程的Redis也会如此之快?...相关推荐

  1. 计算机每次网络重插才能启动,为何电脑开机后再插网线才能用_每次开机都要重插网线的解决方法...

    为何电脑开机后再插网线才能用_每次开机都要重插网线的解决方法 最近有朋友向dengb.com小编咨询为何电脑开机后再插网线才能用,否则就无法联网,电脑每次开机都要重新插网线是非常麻烦的,很多人都遇到过 ...

  2. redisserver是什么问题_面试官老是问:为什么采用单线程的Redis也会如此之快?...

    Java面试笔试面经.Java技术每天学习一点 公众号Java面试 关注我不迷路 作者:kaito 来源:http://kaito-kidd.com/2020/06/28/why-redis-so-f ...

  3. 如何查找历史线程阻塞原因_java并发编程-线程状态,线程阻塞方式,阻塞中的线程如何终止?...

    前面的例子通过volatile boolean来让任务终止,那么如果任务阻塞了? 如何终止它?本文来看下,参考think in java . 线程的状态 1.new:线程创建后的短暂状态,其分配系统资 ...

  4. 计算机每次开机都要配置文件,电脑开机自检的原因是什么 电脑每次开机都自检该如何解决?...

    经常用电脑的人估计会遇到这样的问题,每次 电脑开机 时都会自行进行检测,如何才能解决这一问题,在电脑每次开机都自检.要怎样才能关闭电脑开机自检呢?下面和小编了解一下电脑开机自检的原因是什么?分享几种开 ...

  5. 如何查找历史线程阻塞原因_吊打面试官!Java多线程并发 108 道题,你能答对多少?...

    多线程并发108题 1.Java中实现多线程有几种方法 2.继承Thread 类 3.实现Runnable 接口. 4.ExecutorService. Callable. Future 有返回值线程 ...

  6. 如何查找历史线程阻塞原因_学习 Web Worker(js中的“多线程”)

    昨天部门例会,讨论开发的系统遇到的问题,遇到一个医保上传比较耗时的问题,解决方案提到了Web Worker,学习一波. 首先简单介绍一下什么是web worker.我们都知道在浏览器中javascri ...

  7. java响应很慢排插_服务响应时间慢:Java SecureRandom和/ dev / random - java

    我正在尝试调试Tomcat上部署的应用程序提供的一些慢速响应. 现在,我主要关注SecureRandom和/dev/random(其他一些可能的原因已被调查并排除). 模式如下: 第一个调用恰好在To ...

  8. 前端vue适配不同的分辨率_前端面试时,被问到项目中的难点有哪些?

    在每个前端求职者的面试过程中,一定都有过被面试官问到项目中的难点,而答不上来的情况.为了让大家在前端面试前准备得更加充足,小编准备了一些易被忽视且难度较高的前端面试题,希望可以帮助大家更加顺利完成求职 ...

  9. java基础巩固-宇宙第一AiYWM:为了维持生计,Redis基础Part6(Redis的应用场景、Redis是单线程的速度还快、Redis线程模型:Reactor模式、事件、发布订阅、管道)~整起

    PART1-1:为什么Redis是单线程的 Redis单线程是指: Redis的网络IO和键值对读写是由一个线程来完成的.这也是 Redis 对外提供键值存储服务的主要流程.Redis的其他功能,比如 ...

最新文章

  1. FragmentPagerAdapter 与 FragmentStatePagerAdapter 的区别
  2. java session验证码_利用session实现一次性验证码
  3. css3制作一个漂亮的按钮
  4. 【Groovy】map 集合 ( map 集合操作符重载 | - 操作符重载 | 代码示例 )
  5. 数据分析从零开始,新手小白如何入门?
  6. boost::math::barycentric_rational用法的测试程序
  7. 百度网盘自动备份php,服务器自动备份脚本上传至百度云存储
  8. 深入浅出设计模式 ------ Abstract Factory(抽象工厂)
  9. 【分布式事务----LCN】LCN原理及使用方式
  10. 【AWVS】python调AWVS接口 新建扫描并导出扫描报告 [自定义扫描报告](三)
  11. 关于O、Θ、Ω、o、ω等数学符号
  12. 初学者学python用什么软件,python编程入门软件
  13. c语言http上传图片,基于RTOS的c语言实现http文件上传
  14. 用于采集数据的工具——StatsD
  15. Texstudio的下载
  16. 劫持Linux idle进程做点自己的计算任务
  17. html+css+js制作简单网站首页
  18. MySQL批量删除所有重复数据只保留一条
  19. seastar与go的http性能差异
  20. Python GUI编程入门(31)-Treeview控件

热门文章

  1. 日期选择控件-laydate
  2. Fibonacci(求前四位数)
  3. js中函数的使用方式及回调函数
  4. Openstack的RPC通信代码调用架构
  5. 300+Jquery, CSS, MooTools 和 JS的导航菜单资源
  6. 为脚本语言平反-JavaScript篇(3)
  7. 漂亮图片演示ajax制作教程-lightbox
  8. 【剑指offer】面试题47:礼物的最大价值(Java)
  9. MVC原理及案例分析
  10. python中使用什么导入模块-python—模块导入和类