为什么单线程的Redis能够达到百万级的QPS?

浅殇忆流年 2020-10-20 15:57:43 ⋅ 1366 阅读

作者:在江湖中coding
链接:https://juejin.im/post/5e6097846fb9a07c9f3fe744

性能测试报告

查看了下阿里云 Redis 的性能测试报告如下,能够达到数十万、百万级别的 QPS(暂时忽略阿里对 Redis 所做的优化),我们从 Redis 的设计和实现来分析一下 Redis 是怎么做的。

为什么单线程的Redis能够达到百万级的QPS?

 

Redis 的设计与实现

其实 Redis 主要是通过三个方面来满足这样高效吞吐量的性能需求

  • 高效的数据结构
  • 多路复用 IO 模型
  • 事件机制

高效的数据结构

Redis 支持的几种高效的数据结构 string(字符串)、hash(哈希)、list(列表)、set(集合)、zset(有序集 合)

以上几种对外暴露的数据结构它们的底层编码方式都是做了不同的优化的,不细说了,不是本文重点

多路复用 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 个或者几个线程来处理大量的连接了,极大的提升了吐吞量

为什么单线程的Redis能够达到百万级的QPS?

 

事件机制

redis 客户端与 redis 服务端建立连接,发送命令,redis 服务器响应命令都是需要通过事件机制来做的,如下图(来自互联网的某处...)

为什么单线程的Redis能够达到百万级的QPS?

 

  1. 首先 redis 服务器运行,监听套接字的 AE_READABLE 事件处于监听的状态下,此时连接应答处理器工作,
  2. 客户端与 redis 服务器发起建立连接,监听套接字产生 AE_READABLE 事件,当 IO 多路复用程序监听到其准备就绪后,将该事件压入队列中,由文件事件分派器获取队列中的事件交于连接应答处理器工作处理,应答客户端建立连接成功,同时将客户端 socket 的 AE_READABLE 事件压入队列由文件事件分派器获取队列中的事件交命令请求处理器关联
  3. 客户端发送 set key value 请求,客户端 socket 的 AE_READABLE 事件,当 IO 多路复用程序监听到其准备就绪后,将该事件压入队列中,由文件事件分派器获取队列中的事件交于命令请求处理器关联处理
  4. 命令请求处理器关联处理完成后,需要响应客户端操作完成,此时将产生 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 的读取时间,此时其它 socket 所有的请求都将处于等待过程中,就会导致阻塞了 2.5S,同时又会占用较大的带宽导致吞吐量进一步下降


全部评论: 0

    我有话说:

    Redis多线演进

    Redis作为一个基于内存缓存系统,一直以高性能著称,因没有上下文切换以及无锁操作,即使在单线处理情况下,读速度仍可达到11次/s,写速度达到8.1次/s。但是,单线设计也给Redis

    “12306”是如何支撑QPS

    作者:绘你一世倾城链接:https://juejin.im/post/5d84e21f6fb9a06ac8248149 每到节假日期间,一二线城市返乡、外出游玩人们几乎都面临着一个问题:抢火车票

    为什么说作为程序员分库分表必要性一定要掌握?

      互联网大厂程序员必须掌握海量数据和高并发问题处理技能,期望进入大厂程序员一定要仔细看这篇! MySQL 分库分表是做什么? 相信很多程序员对 MySQL 都比较熟悉了,目前国内

    Java Web实战篇-轻松提高千数据库查询效率

    通过优化数据库设计、java后台和数据库优化达到提高千数据查询效率。

    Redis系列四 锁

      本文目标 1. 熟悉乐观锁ABA概念 2. 理解掌握redis事务以及watch回滚; 3. 实战redis锁 乐观锁 乐观锁是一种不会阻塞其他线并发机制,它不会使用数据库

    创业团队如何设计支撑并发数据库架构?

    我们来聊一下对于一个支撑日活用户高并系统,他数据库架构应该如何设计?

    Redis为什么变慢了?Redis性能问题排查详述

    Redis 作为优秀内存数据库,其拥有非常高性能,单个实例 OPS 能够达到 10W 左右。但也正因此如此,当我们在使用 Redis 时,如果发现操作延迟变大情况,就会与我们预期不符。 你

    Redis系列四 GEO附近

    GEO算法 GeoHash是一种地址编码方法。将二维空间经纬度数据编码成一个字符串; 地球上经度范围:[-180, 180],纬度范围:[-90,90]。如果以本初子午线、赤道为界,地球可以

    Redis系列六 Lua

      本文目标 学习lua基本语法 能够采用redis+lua lua 基本语法 Lua 是一种轻量小巧脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用

    面试官:HashMap为什么是线不安全

    一直以来只是知道HashMap是线不安全,但是到底HashMap为什么线不安全?

    打造千流量秒杀系统

    讲师:易乐天 前小米国际电商技术负责人 10 年软件开发经验。多年 Linux 系统编程、高性能和高并发编程经验。参与过亿用户、千日活、并发系统开发,曾经将 IM 云 WebSocket

    Redis系列七 Debug Lua

      调试redis+lua 学了lua基本语法,了解了redis+lua配套用法,但是却不知道怎么断点调试。学就学全面点, 官网中有dubug相关说明。地址:Redis Lua

    京东技术:如何实现TPS?详解JMQ4存储设计

    JMQ是京东中间件团队自研消息中间件,诞生于2014年,服务京东近个应用,2018年11.11大促期间峰值流量超过5000亿条消息

    如何实现单服务器300个长连接

    有没有试验过单机能抗300个长连接操作?分享一下

    Redis系列一 基本用法&应用场景

        说明 redis最基本使用方法以及使用场景。 字符串 // stringasync function stringFun() { const [key

    Redisson 3.13.6 发布,官方推荐 Redis 客户端

    Redisson 3.13.6 已发布,这是一个 Java 编写 Redis 客户端,具备驻内存数据网格(In-Memory Data Grid)功能,并获得了 Redis 官方推荐

    Netty单机连接及高性能优化

    关于netty学习和介绍,可以去github看官方文档,这里良心推荐《netty实战》和《netty权威指南》两本书,前者对于新手更友好,原理和应用都有讲到,多读读会发现很多高性能优化点。

    Redis系列二:位图实战,实现打卡签到

    前言 如果要统计一篇文章阅读量,可以直接使用 Redis  incr 指令来完成。 如果要求阅读量必须按用户去重,那就可以使用 set 来记录阅读了这篇文章所有用户 id,获取

    Redis系列八 抢红包

      本文概述 掌握红包两种常见生成算法 掌握lua+redis 实现原子性抢红包 项目中还有mysql相关内容 了解jmeter基本用法 遗留问题 redis同步DB时机问题