socket io 不使用redis_为什么Redis单线程能够达到数十万、百万级的QPS?
性能测试报告
查看了下阿里 Redis 的性能测试报告如下,能够达到数十万、百万级别的 QPS(暂时忽略阿里对 Redis 所做的优化),我们从 Redis 的设计和实现来分析一下 Redis 是怎么做的。
Redis的设计与实现
其实 Redis 主要是通过三个方面来满足这样高效吞吐量的性能需求
- 高效的数据结构
- 多路复用 IO 模型
- 事件机制
1、高效的数据结构
Redis 支持的几种高效的数据结构 string(字符串)、hash(哈希)、list(列表)、set(集合)、zset(有序集合)。
以上几种对外暴露的数据结构它们的底层编码方式都是做了不同的优化的,不细说了,不是本文重点。2、多路复用 IO 模型
假设某一时刻与 Redis 服务器建立了 1 万个长连接,对于阻塞式 IO 的做法就是,对每一条连接都建立一个线程来处理,那么就需要 1万个线程,同时根据我们的经验对于 IO 密集型的操作我们一般设置,线程数 = 2 * CPU 数量 + 1,对于 CPU 密集型的操作一般设置线程 = CPU 数量 + 1。
当然各种书籍或者网上也有一个详细的计算公式可以算出更加合适准确的线程数量,但是得到的结果往往是一个比较小的值,像阻塞式 IO 这也动则创建成千上万的线程,系统是无法承载这样的负荷的更加弹不上高效的吞吐量和服务了。
而多路复用 IO 模型的做法是,用一个线程将这一万个建立成功的链接陆续的放入 event_poll,event_poll 会为这一万个长连接注册回调函数,当某一个长连接准备就绪后(建立建立成功、数据读取完成等),就会通过回调函数写入到 event_poll 的就绪队列 rdlist 中,这样这个单线程就可以通过读取 rdlist 获取到需要的数据。
需要注意的是,除了异步 IO 外,其它的 I/O 模型其实都可以归类为阻塞式 I/O 模型,不同的是像阻塞式 I/O 模型在第一阶段读取数据的时候,如果此时数据未准备就绪需要阻塞,在第二阶段数据准备就绪后需要将数据从内核态复制到用户态这一步也是阻塞的。而多路复用 IO 模型在第一阶段是不阻塞的,只会在第二阶段阻塞。
通过这种方式,就可以用 1 个或者几个线程来处理大量的连接了,极大的提升了吐吞量
3、事件机制
Redis 客户端与 Redis 服务端建立连接,发送命令,Redis 服务器响应命令都是需要通过事件机制来做的,如下图
- 首先 redis 服务器运行,监听套接字的 AE_READABLE 事件处于监听的状态下,此时连接应答处理器工作
- 客户端与 Redis 服务器发起建立连接,监听套接字产生 AE_READABLE 事件,当 IO 多路复用程序监听到其准备就绪后,将该事件压入队列中,由文件事件分派器获取队列中的事件交于连接应答处理器工作处理,应答客户端建立连接成功,同时将客户端 socket 的 AE_READABLE 事件压入队列由文件事件分派器获取队列中的事件交命令请求处理器关联
- 客户端发送 set key value 请求,客户端 socket 的 AE_READABLE 事件,当 IO 多路复用程序监听到其准备就绪后,将该事件压入队列中,由文件事件分派器获取队列中的事件交于命令请求处理器关联处理
- 命令请求处理器关联处理完成后,需要响应客户端操作完成,此时将产生 socket 的 AE_WRITEABLE 事件压入队列,由文件事件分派器获取队列中的事件交于命令恢复处理器处理,返回操作结果,完成后将解除 AE_WRITEABLE 事件与命令恢复处理器的关联
reactor模式
大体上可以说 Redis 的工作模式是,reactor 模式配合一个队列,用一个 serverAccept 线程来处理建立请求的链接,并且通过 IO 多路复用模型,让内核来监听这些 socket,一旦某些 socket 的读写事件准备就绪后就对应的事件压入队列中,然后 worker 工作,由文件事件分派器从中获取事件交于对应的处理器去执行,当某个事件执行完成后文件事件分派器才会从队列中获取下一个事件进行处理。
可以类比在 netty 中,我们一般会设置 bossGroup 和 workerGroup 默认情况下 bossGroup 为 1,workerGroup = 2 * cpu 数量,这样可以由多个线程来处理读写就绪的事件,但是其中不能有比较耗时的操作如果有的话需要将其放入线程池中,不然会降低其吐吞量。在 Redis 中我们可以看做这二者的值都是 1。为什么说存储的值不宜过大
比如一个 string key = a,存储了 500MB,首先读取事件压入队列中,文件事件分派器从中获取到后,交于命令请求处理器处理,此处就涉及到从磁盘中加载 500MB。
比如是普通的 SSD 硬盘,读取速度 200MB/S,那么需要 2.5S 的读取时间,在内存中读取数据比较快比如 DDR4 中 50G/秒,读取 500MB 需要 100 毫秒左右。
线程的库一般默认 10 毫秒就算慢查询了,大部分的指令执行时间都是微秒级别,此时其它 socket 所有的请求都将处于等待过程中,就会导致阻塞了 100 毫秒,同时又会占用较大的带宽导致吞吐量进一步下降。以上内容希望帮助到大家,很多PHPer在进阶的时候总会遇到一些问题和瓶颈,业务代码写多了没有方向感,不知道该从那里入手去提升,对此我整理了一些资料,包括但不限于:分布式架构、高可扩展、高性能、高并发、服务器性能调优、TP6,laravel,Redis,Swoole、Swoft、Kafka、Mysql优化、shell脚本、Docker、微服务、Nginx等多个知识点高级进阶干货需要的可以免费分享给大家,需要戳这里PHP进阶架构师>>>实战视频、大厂面试文档免费获取
socket io 不使用redis_为什么Redis单线程能够达到数十万、百万级的QPS?相关推荐
- 使用redis缓存加索引处理数据库百万级并发
使用redis缓存加索引处理数据库百万级并发 前言:事先说明:在实际应用中这种做法设计需要各位读者自己设计,本文只提供一种思想.准备工作:安装后本地数redis服务器,使用mysql数据库,事先插入1 ...
- 总结:如何使用redis缓存加索引处理数据库百万级并发
前言:事先说明:在实际应用中这种做法设计需要各位读者自己设计,本文只提供一种思想.准备工作:安装后本地数redis服务器,使用mysql数据库,事先插入1000万条数据,可以参考我之前的文章插入数据, ...
- TWaver HTML5 + Node.js + express + socket.io + redis(六)
接上一篇TWaver HTML5 + Node.js + express + socket.io + redis(五), 这一篇将讲解如何用模版生成html页面, 如何验证用户登录, 您将了解到: 1 ...
- TWaver HTML5 + Node.js + express + socket.io + redis(五)
接上一回TWaver HTML5 + Node.js + express + socket.io + redis(四), 这一篇您将了解到 1. 如何保存更改后的拓扑数据 (包括新增的, 修改的, 删 ...
- redis为什么是单线程_面试官:Redis单线程为什么执行效率这么高?
点击上方☝Java编程技术乐园,轻松关注!及时获取有趣有料的技术文章 做一个积极的人 编码.改bug.提升自己 我有一个乐园,面向编程,春暖花开! 上一篇回顾: 面试官:Redis为什么设计成单线程的 ...
- redis 和 memcached 有什么区别?redis 的线程模型是什么?为什么 redis 单线程却能支撑高并发?...
这个是问 redis 的时候,最基本的问题吧,redis 最基本的一个内部原理和特点,就是 redis 实际上是个单线程工作模型, 你要是这个都不知道,那后面玩儿 redis 的时候,出了问题岂不是什 ...
- redis 的线程模型是什么?为什么 redis 单线程却能支撑高并发?
redis 的线程模型 redis 内部使用文件事件处理器 file event handler,这个文件事件处理器是单线程的,所以 redis 才叫做单线程的模型.它采用 IO 多路复用机制同时监听 ...
- redis单线程原理___Redis为何那么快-----底层原理浅析
redis单线程原理 redis单线程问题 单线程指的是网络请求模块使用了一个线程(所以不需考虑并发安全性),即一个线程处理所有网络请求,其他模块仍用了多个线程. 1. 为什么说redis能够快速执行 ...
- 【面试题】一文讲清,为啥redis单线程还有很高的性能?
面试的时候如果聊到缓存,肯定会聊到redis,因为它现在是缓存事实上的标准. 早些年一些互联网公司会用到memcached作为缓存,它是多线程的,用c语言开发,不过现在基本很少了,有兴趣的同学可以学习 ...
最新文章
- Angular http跨域
- Perl的Hash一个小细节
- 【DP、线段树优化】琪露诺
- isOnSyncQueue
- 特性和混入不是面向对象的
- git常用命令,项目删除原有github连接并重新连接,回滚,下拉分支代码,切换分支
- (60)UART外设驱动发送驱动(五)(第12天)
- fiddler重定向
- SQL内部连接3个表?
- 在Docker上运行Asp.Net Core示例网站
- Android获取安装应用Apk包大小 缓存大小 getPackageSizeInfo StorageManager
- matlab+dds正弦表,FPGA模拟DDS正弦波信号源1
- 【Yolov5】1.认真总结6000字Yolov5保姆级教程(2022.06.28全新版本v6.1)
- 开发核酸检测软件方案书
- 【定量分析、量化金融与统计学】R语言MANOVA多元方差分析
- linux ubuntu vim复制粘贴,关于vim的复制粘贴
- 有的放矢,Liferay进军数字体验市场
- empty() 是 (boolean) var 的反义词 is_null() 是 is_set() 的反义词!
- 2022海外流媒体十大技术趋势
- 手撕TreeMap红黑树
热门文章
- 【Elasticsearch】java 操作 Elasticsearch 7.8 索引 文档 等操作
- 【Elasticsearch】 6 种 能使 es 挂掉的方法
- 【lucene】Lucene Tika 操作各种文件
- 【Flink】flink报错:This type GenericType pojo cannot be used as key
- Dubbo(RPC原理、Dubbo架构负载均衡配置方式)(3)
- ConcurrentHashMap面试灵魂拷问,你能扛多久
- 修改定时任务不重启项目,SpringBoot如何实现?
- Flask蓝图使用的方法
- VCL组件之TPanel
- 2019.8.8 2048小游戏