目录
二、redis他能不能和JVM一样跑满cpu?为什么用单线程的还那么redis快?
前言
本文主要起因是最近感觉对架构师职位有了新的认知, 然后开始重新审视每一个技术.整个的博客围绕的更多是为什么来解析所有的问题.
推荐的几个地址:
https://blog.csdn.net/xiaoxiaole0313/article/details/108332577
https://blog.csdn.net/Forlogen/article/details/109298253
https://www.cnblogs.com/gz666666/p/12901507.html
https://mp.weixin.qq.com/s/Lf87PnraSm5UgxUhJ7fq5A
一、redis是什么?
redis是一种基于C语言编写的内存cach.
Redis服务器时一个事件驱动程序,服务器主要处理如下的两类事件:
文件事件(file event):服务器和客户端、主服务器和从服务器之间的Socket连接都会产生相应的文件事件,服务器通过对事件的监听来决定执行的操作
时间事件(time event):Redis服务器内部存在一些定时的操作,由此而产生的为时间事件
这里请先记下来:
二、redis他能不能和JVM一样跑满cpu?为什么用单线程的还那么redis快?
- 首先说个结论,redis不能跑满cpu,因为他是单线程,JVM是单进程多线程的.一个程序被执行的最小单位是线程, 每一个线程只能在一个cpu里面执行.所以如果有一台四核服务器, redis最多只能利用一个核心.如果有多个请求进来也只能一条条的执行.而JVM可以分成多个线程,每个请求分配一个线程然后不同的线程可以被不同的cpu执行,
- 那为啥他还那么快:
- 基于内存的存储,就是硬件上不一样.
- 存储结构比较简单,不会像是mysql一样有join啥的
- 用了多路复用
这时候我们说啥是多路复用,其实可以说就是NIO(异步非阻塞IO).多路复用解决的是线程等待和线程阻塞问题.听起来有点蒙,首先说最开始操作系统是没有NIO的,只有BIO.如果我们启动redis,假设这时候linux生成一个8888端口的进程,然后我们就可以通过ip:8888链接redis,各种get,set存取参数, 如果这个时候有一万个客户端链接来.然后这1万个人排成队,这时候因为我们只创建了一个线程,redis就只能一次服务一个人,干完了再干下一个.我盗了一幅图.
我们所有的client是不能够直接访问软件程序滴,,,,,,那么问题来了,如果是BIO(阻塞同步IO)的时候,epoll那个位置是干不完就的一直干(write/read).他是按照client列表执行的,假设说client2要存一个电影,redis呢已经接手client2.其他所有的client必须等到client2干完了才能接着干,如果传一半网络抖动吗卡起来......那有没有办法呢, 有的,就是多搞几个线程和进程,就是一帮redis去服务这一群client.但这个时候问题更多了,首先就是你创建了若干个进程,cpu就那么几个,你卡卡的切换一样也浪费时间,而且要是大家都在改一个值你是不是得加锁.这玩意是不是也费时间.
这个时候我们有了NIO,他的想法就是多少个链接其实redis呢也不咋太在意,每一个client的请求如果准备完全了,就会通过epoll主动的通知redis,这个可以了.然后redis开始干,这个干完了就下一个,那如果100个都同时完事了咋办呢,,,,,,,还是从前往后一个个的干.就这么简单.原来BIO呢是redis挨个linux保存的client的列表地址问,有活没有,不管活好没好,有就干活别的不管了,epoll变成了准备好的才干.
实现nio的几种方式:首先说select,poll,epoll都是NIO, BIO的是wirte和read.poll可以理j解为select的升级版,
- select/poll:将之前传入的fd_set拷贝传出到用户态并返回就绪的文件描述符总数。用户态并不知道是哪些文件描述符处于就绪态,需要遍历来判断。
这里的fd_set说的是每一个client都有一个自己的.fd文件,然后形成的set.用户态说的是用户可以操作的内存区,也就是你得软件区.
- epoll的实现早期是一个哈希表,但是后来由于占用空间比较大,改为了红黑树和链表
其中链表中全部为活跃的链接,红黑树中放的是所有事件。两部分各司其职。这样一来,当收到内核的数据时,只需遍历链表中的数据就行了,而注册read事件或者write事件的时候,向红黑树中记录。
三、redis内部究竟是怎么干的?Reactor模式
又盗了一副图.首先说这个图和上面的不矛盾, 我们更多关注的是文件分派器开始.这部分往后是redis的内部逻辑细节.
文件分派器是单线程的,所以我们经常说的redis是单线程是这个意思!!,然后每一个事件处理器自己都是单线程的.(比如读写,这个就可以保证一次只读写一个)
Socket、IO多路复用器、文件事件分派器、事件处理器和队列组成
- 连接应答处理器:用于对连接服务器的各个客户端进行应答
- 命令请求处理器:用于接收客户端传来的命令请求
- 回复处理器:用于向客户端返回命令的执行结果
- 复制处理器:用于处理主从之间的复制操作
时间事件处理器
它主要用于处理Redis内部产生的一些时间事件,目前Redis中只存在周期性事件,它指每隔一段时间就会执行的事件。一个时间事件主要由如下三部分信息组成:
id:全局的唯一ID
when:毫秒级别的UNIX时间戳,记录事件到达的时间
timeProc:事件处理器
所有的时间事件都会放入一个无序链表中,每当时间事件执行器运行时,它就会遍历整个链表,查找所有已经到达的时间事件,最后调用相应的事件处理器。e
Reactor模型说的就是一个单线程负责接待准备好的网络请求, 拿到这个请求之后他就交给后面真正的小弟去处理业务了,也就是netty的boss线程和worker线程池.
四、redis的面试题
https://www.cnblogs.com/jasontec/p/9699242.html
https://blog.csdn.net/qq_35190492/article/details/102841400
总结
redis里面也是有的多线程的,这个指的是事件处理器. redis4开始在清除过期数据上采用了多线程.redis6在网络io上也可以开启多线程.主要就是更好的使用cpu,但是读写部分一直都是单线程,所以明白我们说的redis单线程都是redis的事件分配器(reactor模型)和读写事件的处理是单线程的.