做者:在江湖中coding
https://juejin.im/post/5e6097...react
性能测试报告redis
查看了下阿里 Redis 的性能测试报告以下,可以达到数十万、百万级别的 QPS(暂时忽略阿里对 Redis 所作的优化),咱们从 Redis 的设计和实现来分析一下 Redis 是怎么作的。后端
Redis的设计与实现服务器
其实 Redis 主要是经过三个方面来知足这样高效吞吐量的性能需求微信
Redis 支持的几种高效的数据结构 string(字符串)、hash(哈希)、list(列表)、set(集合)、zset(有序集合)数据结构
以上几种对外暴露的数据结构它们的底层编码方式都是作了不一样的优化的,不细说了,不是本文重点。架构
假设某一时刻与 Redis 服务器创建了 1 万个长链接,对于阻塞式 IO 的作法就是,对每一条链接都创建一个线程来处理,那么就须要 1万个线程,同时根据咱们的经验对于 IO 密集型的操做咱们通常设置,线程数 = 2 * CPU 数量 + 1,对于 CPU 密集型的操做通常设置线程 = CPU 数量 + 1。框架
固然各类书籍或者网上也有一个详细的计算公式能够算出更加合适准确的线程数量,可是获得的结果每每是一个比较小的值,像阻塞式 IO 这也动则建立成千上万的线程,系统是没法承载这样的负荷的更加弹不上高效的吞吐量和服务了。异步
而多路复用 IO 模型的作法是,用一个线程将这一万个创建成功的连接陆续的放入 event_poll,event_poll 会为这一万个长链接注册回调函数,当某一个长链接准备就绪后(创建创建成功、数据读取完成等),就会经过回调函数写入到 event_poll 的就绪队列 rdlist 中,这样这个单线程就能够经过读取 rdlist 获取到须要的数据。socket
须要注意的是,除了异步 IO 外,其它的 I/O 模型其实均可以归类为阻塞式 I/O 模型,不一样的是像阻塞式 I/O 模型在第一阶段读取数据的时候,若是此时数据未准备就绪须要阻塞,在第二阶段数据准备就绪后须要将数据从内核态复制到用户态这一步也是阻塞的。而多路复用 IO 模型在第一阶段是不阻塞的,只会在第二阶段阻塞。
经过这种方式,就能够用 1 个或者几个线程来处理大量的链接了,极大的提高了吐吞量
三、事件机制
Redis 客户端与 Redis 服务端创建链接,发送命令,Redis 服务器响应命令都是须要经过事件机制来作的,以下图
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 毫秒,同时又会占用较大的带宽致使吞吐量进一步降低。
看完本文有收获?请转发分享给更多人
关注「后端开发者社区」,提高Java技能
关注后端开发者社区微信公众号,后台回复:码农大礼包 能够获取最新整理的技术资料一份。涵盖Java 框架学习、架构师学习等!