硬核剖析Redis单线程为什么那么快?

Redis目前是使用率最高的内存库数据库,是企业应用开发的必备,它极高的性能和丰富的数据结构为我们的开发提供了极大的便利。它每秒可以承受10W+的QPS,但却是单线程的处理模型,为什么采用单线程的Redis性能还会如此强劲呢?这篇文章我们来深度剖析一下其中的缘由。

首先,我要纠正大家的一个认知,我们通常说的 Redis 是单线程,主要是指 Redis 的网络 IO 和键值对读写是由一个线程来完成的,也可以理解为执行实际命令的处理是单线程的。但 Redis 的其他功能,比如持久化、AOF重写、异步删除、集群数据同步等都是由额外的线程执行的。所以严格来说Redis并不是单线程的。

Redis的高性能概述主要取决于以下几个方面:

  • 数据在内存中,全部是内存操作
  • 采用高性能IO模型
  • 单线程模型降低额外开销
  • 高效合理的数据结构

1.数据存取全部内存操作

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

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

2.采用高性能IO模型

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

Linux 中的 IO 多路复用机制是指一个线程处理多个 IO 流,就是我们经常听到的 select/epoll 机制。简单来说,在 Redis 只运行单线程的情况下,该机制允许内核中,同时存在多个监听套接字和已连接套接字。内核会一直监听这些套接字上的连接请求或数据请求。一旦有请求到达,就会交给 Redis 线程处理,这就实现了一个 Redis 线程处理多个 IO 流的效果。

下图就是基于多路复用的 Redis IO 模型。图中的多个 FD 就是刚才所说的多个套接字。Redis 网络框架调用 epoll 机制,让内核监听这些套接字。此时,Redis 线程不会阻塞在某一个特定的监听或已连接套接字上,也就是说,不会阻塞在某一个特定的客户端请求处理上。正因为此,Redis 可以同时和多个客户端连接并处理请求,从而提升并发性。

硬核剖析Redis单线程为什么那么快?

总结一下就是Redis 采用了IO多路复用机制,使其在网络 IO 操作中能并发处理大量的客户端请求,并实现高吞吐率。

3.单线程模型降低额外开销

日常写程序时,我们经常会听到一种说法:”使用多线程,可以增加系统吞吐率,或是可以增加系统扩展性。”的确,对于一个多线程的系统来说,在有合理的资源分配的情况下,可以增加系统中处理请求操作的资源实体,进而提升系统能够同时处理的请求数,即吞吐率。下面的左图是我们采用多线程时所期待的结果。

硬核剖析Redis单线程为什么那么快?

通常情况下,在我们采用多线程后,如果没有良好的系统设计,实际得到的结果,其实是右图所展示的那样。我们刚开始增加线程数时,系统吞吐率会增加,但是,再进一步增加线程时,系统吞吐率就增长迟缓了,有时甚至还会出现下降的情况。

为什么会出现这种情况呢?一个关键的瓶颈在于,系统中通常会存在被多线程同时访问的共享资源,比如一个共享的数据结构。当有多个线程要修改这个共享资源时,为了保证共享资源的正确性,就需要有额外的机制进行保证,而这个额外的机制,就会带来额外的开销。

并发访问控制一直是多线程开发中的一个难点问题,如果没有精细的设计,比如说,只是简单地采用一个粗粒度互斥锁,就会出现不理想的结果:即使增加了线程,大部分线程也在等待获取访问共享资源的互斥锁,并行变串行,系统吞吐率并没有随着线程的增加而增加。而且,采用多线程开发一般会引入同步原语来保护共享资源的并发访问,这也会降低系统代码的易调试性和可维护性。为了避免这些问题,Redis 直接采用了单线程模式。

总结下Redis采用单线程模型主要考虑的因素:

  • 避免多线程上下文切换的性能损耗
  • 避免访问共享资源加锁的性能损耗
  • 降低系统复杂度,开发可维护性高

4.高效的数据结构

Redis 中有多种数据类型,每种数据类型的底层都由一种或多种数据结构来支持,比如跳表、HashMap、压缩列表等。正是因为有了这些不同的数据结构,使得数据存储时间复杂度降到最低,Redis 在存储与读取上的速度才不受阻碍。

硬核剖析Redis单线程为什么那么快?

最后总结

Redis使用单线程模型,没有了线程上下文切换和访问共享资源加锁的性能损耗,配合IO多路复用技术,可以完成多个连接的请求处理。而且正是由于它的使用定位是内存数据库,这样几乎所有的操作都在内存中完成,它的性能可以达到非常之高。

Redis 6.0 版本为什么又引入了多线程,这里也解释下。

Redis 的性能瓶颈不在 CPU ,而在内存和网络,内存不够可以增加内存或通过数据结构等进行优化;但 Redis 的网络 IO 的读写占用了大部分 CPU 的时间,如果可以把网络处理改成多线程的方式,性能会有很大提升。所以总结下 Redis 6.0 版本引入多线程有两个原因:1.充分利用服务器的多核资源 2.多线程分摊 Redis 同步 IO 读写负荷

注意:执行命令还是由单线程顺序执行,只是处理网络数据读写采用了多线程,而且 IO 线程要么同时读 Socket ,要么同时写 Socket ,不会同时读写。

如果你还想看更多优质原创文章,欢迎关注我的公号「数据库架构师」,提升数据库技能。

如果我的文章对你有所帮助,还请帮忙点赞、在看、转发一下,你的支持会激励我输出更高质量的文章,非常感谢!

Original: https://www.cnblogs.com/databasepub/p/16691115.html
Author: 数据库架构师
Title: 硬核剖析Redis单线程为什么那么快?

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/622721/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 设计模式-建造者模式

    1.变化是永恒的 首先,我们由一个例子来进入入今天的主题 又是一个周三,快要下班了,老大突然拉住我,喜滋滋地告诉我:”××公司很满意我们做的模型,又签订了一个合同,把奔…

    Java 2023年6月8日
    0112
  • SpringBoot进阶教程(七十四)整合ELK

    在上一篇文章《SpringBoot进阶教程(七十三)整合elasticsearch 》,已经详细介绍了关于elasticsearch的安装与使用,现在主要来看看关于ELK的定义、安…

    Java 2023年6月8日
    067
  • 多线程JUC并发篇常见面试详解

    @ 1、JUC 简介 2、线程和进程 3、并非与并行 4、线程的状态 5、wait/sleep的区别 6、Lock 锁(重点) 1、Lock锁 2、公平非公平: 3、Reentra…

    Java 2023年6月5日
    0121
  • JVM学习笔记之类加载机制【八】

    一、类加载时机 1.1 触发类初始化的六个场景: 加载? 1、遇到new、getstatic、putstatic或invokestatic这四条字节码指令时 如果类型没有进行过初始…

    Java 2023年6月5日
    082
  • java通过command调用openssl生成私钥和证书

    在windows环境下进行的测试,前提条件,windows上需要先安装openssl。 配置环境变量,查看版本: import java.io.*; import java.uti…

    Java 2023年5月29日
    083
  • SpringMVC使用指南

    SpringMVC使用指南 一、SpringMVC的工作原理 1.1 SpringMVC的原理流程 图片地址:22. Web MVC framework 用户发起请求,请求被拦截到…

    Java 2023年6月6日
    089
  • 这是一篇致力于解决Linux小白无法安装tunctl工具的文章

    计算机网络——搭建Linux下的实验环境并成功安装tunctl 各位如果是来解决安装tunctl的问题的,请直接到2.2部分的内容。 这个学期终于迎来了计算机网络这门课程,也终于能…

    Java 2023年6月8日
    093
  • Spring Boot【快速入门】

    转自: https://www.cnblogs.com/wmyskxz/p/9010832.html Spring Boot 概述 Build Anything with Spri…

    Java 2023年5月30日
    076
  • 【译】Spring的@EnableCaching注解

    Spring的@EnableCaching注解 @EnableCaching注解是spring framework中的注解驱动的缓存管理功能。自spring版本3.1起加入了该注解…

    Java 2023年5月29日
    0150
  • == 和 equals 的区别是什么?

    ==:基本类型比较的是值的大小,引用类型比较的是内存地址,是不是同一个对象,equals:默认比较同一个对象的内容 == 和 equals 的区别是什么? == : 它的作用是判断…

    Java 2023年6月13日
    071
  • jsoup教程

    在爬虫的时候,当我们用HttpClient之类的框架,获取到网页源码之后,需要从网页源码中取出我们想要的内容, 就可以使用jsoup这类HTML解析器了。可以非常轻松的实现。 虽然…

    Java 2023年6月7日
    084
  • 环形链表_141_142

    给你一个链表的头节点 head ,判断链表中是否有环。 如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 如果链表中存在环 ,则返回 true 。 …

    Java 2023年6月5日
    079
  • 使用Flutter开发的抖音国际版

    简介 最近花了两天时间研究使用Flutter开发一个抖音国际版. 个人感觉使用Flutter开发app快得不要不要的额. 两天就基本可以开发个大概出来. 最主要是热重载,太方便实时…

    Java 2023年6月7日
    082
  • 数据结构之单链表

    总结: 链表是以节点的方式来存储的是链式存储的 每个节点包含data域,next域:指向下一个节点 每个节点的顺序不一定是连续的 链表有带头结点和没有带头节点两种形式(头节点不能动…

    Java 2023年6月5日
    092
  • Mysql和Oracle数据库死锁查看以及解决

    1.1 锁事务查询 1.1.1 查看正在锁的事务 SQL : SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS; 1.1.2 查看等待锁的…

    Java 2023年6月8日
    0158
  • 题目: 给定一个数组,删除数组中指定的元素对应的索引

    package com.gao.test; import java.lang.reflect.Array; import java.util.Arrays; /* 题目: 给定一个…

    Java 2023年6月5日
    090
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球