【Redis原理机制 一】Redis高性能原因、单线程模型及多路复用技术

简介: 【Redis原理机制 一】Redis高性能原因、单线程模型及多路复用技术

为什么Redis快呢,其实在学习完了应用场景和集群等模式后可以回归本源了,用Redis的本质原因到底是什么?Redis的设计方式有什么好处,这是向更深处探索的第一步。

Redis为什么快

因为Redis是基于内存的一个数据库,所以第一点可想而知,内存当然比IO快啊,其次就是两个不常见的点:

  • redis是基于内存的,内存的读写速度非常快, 数据存放在内存中,内存的响应时间大约是 100纳秒 ,这是Redis每秒万亿级别访问的重要基础。
  • redis是单线程的,省去了很多上下文切换线程的时间,单线 避免了线程切换和竞态产生的消耗,当然要注意:每条命令执行如果占用大量时间, 会造成其他线程阻塞,对于Redis这种高性能服务是致命的,所以Redis是面向高速执行的数据库
  • redis使用多路复用技术,可以处理并发的连接。非阻塞IO 内部实现采用epoll,采用了epoll+自己实现的简单的事件框架。epoll中的读、写、关闭、连接都转化成了事件,然后利用epoll的多路复用特性,绝不在io上浪费一点时间。
  • 数据结构简单,操作节省时间,Redis全程使用hash结构,读取速度快,还有一些特殊的数据结构,对数据存储进行了优化,如压缩表,对短数据进行压缩存储,再如,跳表,使用有序的数据结构加快读取的速度。当然解决的场景也有限,还是以性能为第一位的。

以上四点就是Redis快的原因。

Redis多路复用技术

IO多路复用技术以及Redis的实现模式,redis 采用网络IO多路复用技术来保证在多连接的时候, 系统的高吞吐量

  • 多路-指的是多个socket连接,也就是多个客户端连接
  • 复用-指的是复用一个线程,也就是复用Redis的单个存取线程
  • 多路复用主要有三种技术:select,poll,epoll。epoll是最新的也是目前最好的多路复用技术

采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络IO的时间消耗)。数据输入操作一般包含两个步骤:

  1. 等待数据准备好waiting for data to be ready)。对于一个套接口上的操作,这一步骤关系到数据从网络到达,并将其复制到内核的某个缓冲区。
  2. 将数据从内核缓冲区复制到进程缓冲区(copying the data from the kernel to the process)

以下内容来自于Redis 和 IO 多路复用

阻塞模型

当使用 read 或者 write 对某一个文件描述符(File Descriptor 以下简称 FD)进行读写时,如果当前 FD 不可读或不可写,整个 Redis 服务就不会对其它的操作作出响应,导致整个服务不可用。这也就是传统意义上的,也就是我们在编程中使用最多的阻塞模型

阻塞模型虽然开发中非常常见也非常易于理解,但是由于它会影响其他 FD 对应的服务,所以在需要处理多个客户端任务的时候,往往都不会使用阻塞模型

I/O 多路复用

阻塞式的 I/O 模型并不能满足这里的需求,我们需要一种效率更高的 I/O 模型来支撑 Redis 的多个客户(redis-cli),这里涉及的就是 I/O 多路复用模型了:

在 I/O 多路复用模型中,最重要的函数调用就是 select,该方法的能够同时监控多个文件描述符的可读可写情况,当其中的某些文件描述符可读或者可写时,select 方法就会返回可读以及可写的文件描述符个数

一个例子

从网上找了一个讲的特别好的例子,生动形象的表明了I/O多路复用模式的好处:IO模式一般分为同步IO和异步IO.

  • 同步IO会阻塞进程, 同步IO又分为阻塞IO, 非阻塞IO, IO多路复用,非阻塞IO虽然在请求数据时不阻塞, 但真正数据来临时,也就是内核数据拷贝到用户数据时, 此时进程是阻塞的
  • 异步IO不会阻塞进程. 目前linux上大部分用的是同步IO, 异步IO在linux上目前还不成熟, 不过windows的iocp算是

那么这些IO模式的区别分别是什么? 接下来举个小例子来说明. 假设你现在去女生宿舍楼找自己的女神, 但是你只知道女神的手机号,并不知道女神的具体房间 先说同步IO的情况,

  • 阻塞IO,给女神发一条短信, 说我来找你了, 然后就默默的一直等着女神下楼, 这个期间除了等待你不会做其他事情, 属于备胎做法.
  • 非阻塞IO, 给女神发短信, 如果不回, 接着再发, 一直发到女神下楼, 这个期间你除了发短信等待不会做其他事情, 属于专一做法.
  • IO多路复用, 是找一个宿管大妈来帮你监视下楼的女生, 这个期间你可以些其他的事情. 例如可以顺便看看其他妹子,玩玩王者荣耀, 上个厕所等等. IO复用又包括 select, poll, epoll 模式. 那么它们的区别是什么?
  • select大妈, 每一个女生下楼, select大妈都不知道这个是不是你的女神, 她需要一个一个询问, 并且select大妈能力还有限, 最多一次帮你监视1024个妹子
  • poll大妈不限制盯着女生的数量, 只要是经过宿舍楼门口的女生, 都会帮你去问是不是你女神
  • epoll大妈,不限制盯着女生的数量, 并且也不需要一个一个去问,那么如何做呢? epoll大妈会为每个进宿舍楼的女生脸上贴上一个大字条,上面写上女生自己的名字, 只要女生下楼了, epoll大妈就知道这个是不是你女神了, 然后大妈再通知你。以空间换时间,时间复杂度O(1)

  • I/O多路复用模型使用了Reactor设计模式实现了这一机制。通过Reactor的方式,可以将用户线程轮询I/O操作状态的工作统一交给handle_events事件循环进行处理。用户线程注册事件处理器之后可以继续执行做其他的工作,而Reactor线程负责调用内核的select函数检查socket状态。当有socket被激活时,则通知相应的用户线程(或执行用户线程的回调函数),执行handle_event进行数据读取、处理的工作。由于select函数是阻塞的,因此多路I/O复用模型也被称为异步阻塞I/O模型。注意,这里的所说的阻塞是指select函数执行时线程被阻塞,而不是指socket

上面这些同步IO有一个共同点就是, 当女神走出宿舍门口的时候【数据来临时】, 你已经站在宿舍门口等着女神的, 此时你属于阻塞状态

Reactor 设计模式

Redis 服务采用 Reactor 的方式来实现文件事件处理器(每一个网络连接其实都对应一个文件描述符):

文件事件处理器使用 I/O 多路复用模块同时监听多个 FD,当 accept、read、write 和 close 文件事件产生时,文件事件处理器就会回调 FD 绑定的事件处理器。

虽然整个文件事件处理器是在单线程上运行的,但是通过 I/O 多路复用模块的引入,实现了同时对多个 FD 读写的监控,提高了网络通信模型的性能,同时也可以保证整个 Redis 服务实现的简单。

Redis单线程模型

什么是Redis单线程模型?Redis客户端对服务端的每次调用都经历了发送命令,执行命令,返回结果三个过程。其中执行命令阶段,由于Redis是单线程来处理命令的,所有每一条到达服务端的命令不会立刻执行,所有的命令都会进入一个队列中,然后逐个被执行。并且多个客户端发送的命令的执行顺序是不确定的。但是可以确定的是不会有两条命令被同时执行,不会产生并发问题,这就是Redis的单线程基本模型,优势有:

  • 不需要各种锁的性能消耗,Redis的数据结构并不全是简单的Key-Value,还有list,hash等复杂的结构,这些结构有可能会进行很细粒度的操作,比如在很长的列表后面添加一个元素,在hash当中添加或者删除一个对象。这些操作可能就需要加非常多的锁,导致的结果是同步开销大大增加。总之,在单线程的情况下,就不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗。
  • CPU消耗低,采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗CPU
  • 代码更清晰,处理逻辑更简单

缺点也很明显,无法发挥多核CPU性能,不过可以通过在单机开多个Redis实例来完善

Redis的性能瓶颈是什么

Redis在内存里,省下来CPU从硬盘里取数据拿到内存的时间,也就是更依赖内存,CPU不可能对它一点影响没有,但是相比别的数据库,Redis受CPU的限制更少一点。而Redis还需要经常和网络进行连接,也就是网络IO高。相比于内存和网络,Redis对CPU的依赖稍低,所以Redis的性能瓶颈不是CPU而是内存和网络,但是如果有多线程比较好的解决方案,可以把CPU利用起来。

Redis为什么是单线程

当性能出现问题时,我们一般考虑的是使用多线程进行调度,而多线程调度需要CPU、内存、网络带宽但是CPU不是Redis的瓶颈。Redis的瓶颈最有可能是机器内存或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了。当然单线程的Redis在瓶颈是CPU的IO时(这不是大多数应用的实际应用场景),确实速度会比多线程慢。

如何不让CPU闲置

如果万一CPU成为你的Redis瓶颈了,或者你就是不想让服务器其他核闲置,那怎么办?那也很简单,你多起几个Redis进程就好了。Redis是keyvalue数据库,又不是关系数据库,数据之间没有约束。只要客户端分清哪些key放在哪个Redis进程上就可以了【通过配置文件启动可以很轻松的区分】,redis-cluster当然是更好的解决方案。

相关文章
|
6月前
|
存储 NoSQL Redis
阿里云高性能数据库Tair(兼容 Redis)收费价格,稳定可靠成本低
阿里云高性能云数据库Tair兼容Redis,提供Redis开源版和Tair企业版,支持多种存储介质与灵活扩展,适用于高并发场景。Tair具备亚毫秒级稳定延迟,保障业务连续性。价格方面,Redis开源版年费从72元起,Tair企业版年费从1224元起,具体费用根据配置不同有所变化。
|
8月前
|
负载均衡 算法 安全
基于Reactor模式的高性能网络库之线程池组件设计篇
EventLoopThreadPool 是 Reactor 模式中实现“一个主线程 + 多个工作线程”的关键组件,用于高效管理多个 EventLoop 并在多核 CPU 上分担高并发 I/O 压力。通过封装 Thread 类和 EventLoopThread,实现线程创建、管理和事件循环的调度,形成线程池结构。每个 EventLoopThread 管理一个子线程与对应的 EventLoop(subloop),主线程(base loop)通过负载均衡算法将任务派发至各 subloop,从而提升系统性能与并发处理能力。
440 3
|
9月前
|
存储 SQL 安全
Java 无锁方式实现高性能线程实战操作指南
本文深入探讨了现代高并发Java应用中单例模式的实现方式,分析了传统单例(如DCL)的局限性,并提出了多种无锁实现方案。包括基于ThreadLocal的延迟初始化、VarHandle原子操作、Record不可变对象、响应式编程(Reactor)以及CDI依赖注入等实现方式。每种方案均附有代码示例及适用场景,同时通过JMH性能测试对比各实现的优劣。最后,结合实际案例设计了一个高性能配置中心,展示了无锁单例在实际开发中的应用。总结中提出根据场景选择合适的实现方式,并遵循现代单例设计原则以优化性能和安全性。文中还提供了代码获取链接,便于读者实践与学习。
173 0
|
10月前
|
缓存 NoSQL Java
Redis+Caffeine构建高性能二级缓存
大家好,我是摘星。今天为大家带来的是Redis+Caffeine构建高性能二级缓存,废话不多说直接开始~
1363 0
|
5月前
|
缓存 运维 监控
Redis 7.0 高性能缓存架构设计与优化
🌟蒋星熠Jaxonic,技术宇宙中的星际旅人。深耕Redis 7.0高性能缓存架构,探索函数化编程、多层缓存、集群优化与分片消息系统,用代码在二进制星河中谱写极客诗篇。
|
监控 NoSQL 安全
如何在 Redis 中正确使用多线程?
【10月更文挑战第16天】正确使用 Redis 多线程需要综合考虑多个因素,并且需要在实践中不断摸索和总结经验。通过合理的配置和运用,多线程可以为 Redis 带来性能上的提升,同时也要注意避免可能出现的问题,以保障系统的稳定和可靠运行。
406 2
|
存储 NoSQL Redis
Redis 新版本引入多线程的利弊分析
【10月更文挑战第16天】Redis 新版本引入多线程是一个具有挑战性和机遇的改变。虽然多线程带来了一些潜在的问题和挑战,但也为 Redis 提供了进一步提升性能和扩展能力的可能性。在实际应用中,我们需要根据具体的需求和场景,综合评估多线程的利弊,谨慎地选择和使用 Redis 的新版本。同时,Redis 开发者也需要不断努力,优化和完善多线程机制,以提供更加稳定、高效和可靠的 Redis 服务。
351 1
|
缓存 NoSQL 中间件
Redis的线程模型
Redis采用单线程模型确保操作的原子性,每次只执行一个操作,避免并发冲突。它通过MULTI/EXEC事务机制、Lua脚本和复合指令(如MSET、GETSET等)保证多个操作要么全成功,要么全失败,确保数据一致性。Redis事务在EXEC前失败则不执行任何操作,EXEC后失败不影响其他操作。Pipeline虽高效但不具备原子性,适合非热点时段的数据调整。Redis 7引入Function功能,支持函数复用,简化复杂业务逻辑。总结来说,Redis的单线程模型简单高效,适用于高并发场景,但仍需合理选择指令执行方式以发挥其性能优势。
307 6
|
NoSQL Redis
单线程传奇Redis,为何引入多线程?
Redis 4.0 引入多线程支持,主要用于后台对象删除、处理阻塞命令和网络 I/O 等操作,以提高并发性和性能。尽管如此,Redis 仍保留单线程执行模型处理客户端请求,确保高效性和简单性。多线程仅用于优化后台任务,如异步删除过期对象和分担读写操作,从而提升整体性能。
284 1