1:io多路复用epoll 
io多路复用简单来说就是一个线程处理多个网络请求
我们知道epoll in 的事件触发是可读了,这个比较好理解,比如一个连接过来,或者一个数据发送过来了,那么in事件就触发了,那么out事件是如何触发的呢?缓冲区可写(有空的区域),就可以触发,epoll有两种模式LT(水平触发)和ET(边缘触发),LT模式下,主要缓冲区数据一次没有处理完,那么下次epoll_wait返回时,还会返回这个句柄;而ET模式下,缓冲区数据处理一次就结束,下次是不会再通知了,只在第一次返回.所以在ET模式下,一般是通过while循环,一次性读完全部数据.epoll默认使用的是LT.
socket的缓冲区已经满了,此时无法继续send。此时异步程序的正确处理流程是调用epoll_wait,当socket缓冲区中的数据被对方接收之后,缓冲区就会有空闲空间可以继续往里面写数据,此时epoll_wait就会返回这个socket的EPOLLOUT事件,获得这个事件时,你就可以继续往socket中写出数据。
redis的epoll使用的是默认的LT模式,只要写缓冲区可写时,就会不断的触发可写事件,为了避免一直触发可写事件,redis是在有数据可写的时候注册写事件,写完之后就取消写事件的注册
epoll内部数据结构为红黑树和链表,红黑树保存了所有socket和监听的事件信息,链表保存的是就绪的socket信息,就是那些就绪socket已经帮你整理好了
那么,这个准备就绪list链表是怎么维护的呢?当我们执行epoll_ctl时,除了把socket放到epoll文件系统里file对象对应的红黑树上之外,还会给内核中断处理程序注册一个回调函数,告诉内核,如果这个句柄的中断到了,就把它放到准备就绪list链表里。所以,当一个socket上有数据到了,内核在把网卡上的数据copy到内核中后就来把socket插入到准备就绪链表里了。
如此,一颗红黑树,一张准备就绪句柄链表,少量的内核cache,就帮我们解决了大并发下的socket处理问题。执行epoll_create时,创建了红黑树和就绪链表,执行epoll_ctl时,如果增加socket句柄,则检查在红黑树中是否存在,存在立即返回,不存在则添加到树干上,然后向内核注册回调函数,用于当中断事件来临时向准备就绪链表中插入数据。执行epoll_wait时立刻返回准备就绪链表里的数据即可。
 
 
2:redis单线程是怎么做到高性能的呢?
以前我一直在想一个问题:如果一个redis命令很长,redis接收处理这个命令就要100毫秒,那么别的命令会不会延迟100毫秒呢?后续命令处理会不会像消息队列一样积压呢?
答案:不会
上面我们已经说了epoll的原理,它不是让我们一次处理完一个命令后,再去处理另一个命令,epoll是帮我们一次接收多个命令的部分数据(如果命令很短则是完整的数据),每个socket都有一个缓冲区,写满了就不能写了,需要读出来后才能继续往里面写,redis为每个client分配了一个变长缓冲区,从socket中读出后存在缓冲区中,当接收到一个完整的命令,就解析并执行这个命令,然后把缓冲区后面的数据往前移动,反复利用这块内存,当这块内存超过一定值后就会释放,在需要的时候重新分配一块内存
也就是说epoll的水平触发模式将一个较长的命令请求分成了多次接收,一次能接收多个命令的请求,天生就只支持高并发的,加上redis会将耗时的命令会分多次处理,保证了我们的读写操作都很快。
综述单线程高性能的原因:
1:纯内存操作本来就很快
2:redis使用epoll支持io多路复用,天生支持高并发请求
3:redis将耗时的操作分多次处理,保证每次处理的时间都很短,保证了读写性能,如果数据很长的话处理时间就会变长,所以redis不建议保存太长的数据
还有redis6.0实现了多线程的功能,性能至少翻倍,那你还要问题单线程为什么性能高吗?而且还是在数据的接收解析和数据的发送使用多线程的情况下,性能就至少翻倍了。可能是为了保证代码的简洁性,作者不愿意使用多线程,为了提升性能用了多线程,也是部分功能使用多线程,操作redis数据库的逻辑还是单线程,如果数据是写少读多的情况下,采用多线程读写锁性能会不会提升很多呢?
所以redis一开始采用单线程的原因:
1:代码简洁又简单 
2:性能已经很好了
3:性能不够我再搞多线程吗
 
3:redis单线程是怎么同时处理文件事件和时间事件
文件事件主要是网络I/O的读写,请求的接收和回复
时间事件就是单次/多次执行的定时器,如主从复制、定时删除过期数据、字典rehash等
redis所有核心功能都是跑在主线程中的,像aof文件落盘操作是在子线程中执行的,那么在高并发情况下它是怎么做到高性能的呢?
由于这两种事件在同一个线程中执行,就会出现互相影响的问题,如时间事件到了还在等待/执行文件事件,或者文件事件已经就绪却在执行时间事件,这就是单线程的缺点,所以在实现上要将这些影响降到最低。那么redis是怎么实现的呢?
 
定时执行的时间事件保存在一个链表中,由于链表中任务没有按照执行时间排序,所以每次需要扫描单链表,找到最近需要执行的任务,时间复杂度是O(N),redis敢这么实现就是因为这个链表很短,大部分定时任务都是在serverCron方法中被调用。从现在开始到最近需要执行的任务的开始时间,时长定位T,这段时间就是属于文件事件的处理时间,以epoll为例,执行epoll_wait最多等待的时长为T,如果有就绪任务epoll会返回所有就绪的网络任务,存在一个数组中,这时我们知道了所有就绪的socket和对应的事件(读、写、错误、挂断),然后就可以接收数据,解析,执行对应的命令函数。
如果最近要执行的定时任务时间已经过了,那么epoll就不会阻塞,直接返回已经就绪的网络事件,即不等待。
总之单线程,定时事件和网络事件还是会互相影响的,正在处理定时事件网络任务来了,正在处理网络事件定时任务的时间到了。所以redis必须保证每个任务的处理时间不能太长。 

聊聊redis单线程为什么能做到高性能和io多路复用到底是个什么鬼的更多相关文章

  1. 为什么 redis 单线程却能支撑高并发

    redis 和 memcached 有什么区别?redis 的线程模型是什么?为什么 redis 单线程却能支撑高并发? 这个是问 redis 的时候,最基本的问题吧,redis 最基本的一个内部原理 ...

  2. 2.redis 和 memcached 有什么区别?redis 的线程模型是什么?为什么 redis 单线程却能支撑高并发?

    作者:中华石杉 面试题 redis 和 memcached 有什么区别?redis 的线程模型是什么?为什么 redis 单线程却能支撑高并发? 面试官心理分析 这个是问 redis 的时候,最基本的 ...

  3. redis 和 memcached 有什么区别?redis 的线程模型是什么?为什么 redis 单线程却能支撑高并发?

    redis 和 memcached 有啥区别? redis 支持复杂的数据结构 redis 相比 memcached 来说,拥有更多的数据结构,能支持更丰富的数据操作.如果需要缓存能够支持更复杂的结构 ...

  4. 爬虫基础--IO多路复用单线程异步非阻塞

    最近一直的学习爬虫  ,进行基础的学习 性能相关 参考 https://www.cnblogs.com/wupeiqi/p/6229292.html # 目标:单线程实现并发HTTP请求 # # so ...

  5. redis 单线程的理解

    单线程模型 Redis客户端对服务端的每次调用都经历了发送命令,执行命令,返回结果三个过程.其中执行命令阶段,由于Redis是单线程来处理命令的,所有每一条到达服务端的命令不会立刻执行,所有的命令都会 ...

  6. 【Redis破障之路】三:Redis单线程架构

    众所周知,Redis是一个单线程架构的NoSQL数据库,但是是单线程模型的Redis为什么性能如此之高?这就是我们接下来要探究学习的内容. 1.Redis的单线程架构 1.1.Redis单线程简介 首 ...

  7. Redis系列:深刻理解高性能Redis的本质

    1 背景 分布式系统绕不开的核心之一的就是数据缓存,有了缓存的支撑,系统的整体吞吐量会有很大的提升.通过使用缓存,我们把频繁查询的数据由磁盘调度到缓存中,保证数据的高效率读写. 当然,除了在内存内运行 ...

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

    (本文首发于"数据库架构师"公号,订阅"数据库架构师"公号,一起学习数据库技术,助力职业发展) Redis目前是使用率最高的内存库数据库,是企业应用开发的必备, ...

  9. Redis单线程原理

    redis是以socket方式通信,socket服务端可同时接受多个客户端请求连接,也就是说,redis服务同时面对多个redis客户端连接请求,而redis服务本身是单线程运行. 假设,现在有A,B ...

随机推荐

  1. C++基础面试题及答案

    C++ C++ 和C的主要区别 C语言是面向过程编程,C++是面向对象编程,C++ 完全兼容C C++有哪些特性,简述对他们的理解 封装.继承.多态 封装 将的事物抽象成一个个集合(也就是所说的类), ...

  2. DotNet Core

    安装 dotnet add package Pomelo.EntityFrameworkCore.MySql 使用 MySQL 作为后端     在继承 DbContext 类中重写 OnConfig ...

  3. Docker 快速搭建 LDAP

    Docker 快速搭建 LDAP 步骤 # 拉取镜像 docker pull osixia/openldap:1.3.0 # 创建并进入映射目录 mkdir -p /usr/local/ldap &a ...

  4. ArrayList继承关系分析

    目录 继承关系 Iterable Collection List AbstractCollection AbstractList RandomAccess Serializable Cloneable ...

  5. C#LeetCode刷题之#622-设计循环队列​​​​​​​(Design Circular Queue)

    问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/4126 访问. 设计你的循环队列实现. 循环队列是一种线性数据结构 ...

  6. P6087 [JSOI2015]送礼物 01分数规划+单调队列+ST表

    P6087 [JSOI2015]送礼物 01分数规划+单调队列+ST表 题目背景 \(JYY\) 和 \(CX\) 的结婚纪念日即将到来,\(JYY\) 来到萌萌开的礼品店选购纪念礼物. 萌萌的礼品店 ...

  7. Linux用户锁定、解锁及锁定查看

    [root@l01 ~]# passwd -S pispread pispread PS -- - (Password set, SHA512 crypt.)用户锁定 [root@l01 ~]# pa ...

  8. try easy pytest 1tep介绍

    『 tep is a testing tool to help you write pytest more easily. Try Easy Pytest! 』 tep前身 tep的前身是接口自动化测 ...

  9. DataGrid样式

    1.自定义列(DataGridTemplateColumn) <DataGridTemplateColumn Width="130"> <DataGridTemp ...

  10. 第2章 Hive安装

    第2章 Hive安装 2.1 Hive安装地址 1.Hive官网地址 http://hive.apache.org/ 2.文档查看地址 https://cwiki.apache.org/conflue ...