Redis面试题(一): Redis究竟是多线程仍是单线程?

0. redis单线程问题nginx

    单线程指的是网络请求模块使用了一个线程(因此不需考虑并发安全性),即一个线程处理全部网络请求,其余模块仍用了多个线程。redis


1. 为何说redis可以快速执行编程

(1) 绝大部分请求是纯粹的内存操做(很是快速)缓存

(2) 采用单线程,避免了没必要要的上下文切换和竞争条件安全

(3) 非阻塞IO - IO多路复用服务器


2. redis的内部实现网络

内部实现采用epoll,采用了epoll+本身实现的简单的事件框架。epoll中的读、写、关闭、链接都转化成了事件,而后利用epoll的多路复用特性,毫不在io上浪费一点时间 这3个条件不是相互独立的,特别是第一条,若是请求都是耗时的,采用单线程吞吐量及性能可想而知了。应该说redis为特殊的场景选择了合适的技术方案。多线程


3. Redis关于线程安全问题并发

     redis其实是采用了线程封闭的观念,把任务封闭在一个线程,天然避免了线程安全问题,不过对于须要依赖多个redis操做的复合操做来讲,依然须要锁,并且有多是分布式锁。


4. IO多路复用框架

        参考: https://www.zhihu.com/question/32163005

要弄清问题先要知道问题的出现缘由

缘由:

因为进程的执行过程是线性的(也就是顺序执行),当咱们调用低速系统I/O(read,write,accept等等),进程可能阻塞,此时进程就阻塞在这个调用上,不能执行其余操做.阻塞很正常.

接下来考虑这么一个问题:一个服务器进程和一个客户端进程通讯,服务器端read(sockfd1,bud,bufsize),此时客户端进程没有发送数据,那么read(阻塞调用)将阻塞,直到客户端调用write(sockfd,but,size)发来数据.在一个客户和服务器通讯时这没什么问题;

当多个客户与服务器通讯时当多个客户与服务器通讯时,若服务器阻塞于其中一个客户sockfd1,当另外一个客户的数据到达套接字sockfd2时,服务器不能处理,仍然阻塞在read(sockfd1,...)上;此时问题就出现了,不能及时处理另外一个客户的服务,咋么办?

I/O多路复用来解决!


I/O多路复用:

继续上面的问题,有多个客户链接,sockfd1,sockfd2,sockfd3..sockfdn同时监听这n个客户,当其中有一个发来消息时就从select的阻塞中返回,而后就调用read读取收到消息的sockfd,而后又循环回select阻塞;这样就不会由于阻塞在其中一个上而不能处理另外一个客户的消息

Q:

那这样子,在读取socket1的数据时,若是其它socket有数据来,那么也要等到socket1读取完了才能继续读取其它socket的数据吧。那不是也阻塞住了吗?并且读取到的数据也要开启线程处理吧,那这和多线程IO有什么区别呢?

A:

1.CPU原本就是线性的不论什么都须要顺序处理并行只能是多核CPU

2.io多路复用原本就是用来解决对多个I/O监听时,一个I/O阻塞影响其余I/O的问题,跟多线程不要紧.

3.跟多线程相比较,线程切换须要切换到内核进行线程切换,须要消耗时间和资源.而I/O多路复用不须要切换线/进程,效率相对较高,特别是对高并发的应用nginx就是用I/O多路复用,故而性能极佳.但多线程编程逻辑和处理上比I/O多路复用简单.而I/O多路复用处理起来较为复杂.


5. 使用Redis有哪些好处?

(1) 速度快,由于数据存在内存中,相似于HashMap,HashMap的优点就是查找和操做的时间复杂度都是O(1)

(2) 支持丰富数据类型,支持string,list,set,sorted set,hash

(3) 支持事务,操做都是原子性,所谓的原子性就是对数据的更改要么所有执行,要么所有不执行

(4) 丰富的特性:可用于缓存,消息,按key设置过时时间,过时后将会自动删除




6. Redis相比memcached有哪些优点?

(1) memcached全部的值均是简单的字符串,redis做为其替代者,支持更为丰富的数据类型

(2) redis的速度比memcached快不少

(3) redis能够持久化其数据

(4)Redis支持数据的备份,即master-slave模式的数据备份。

(5) 使用底层模型不一样,它们之间底层实现方式 以及与客户端之间通讯的应用协议不同。Redis直接本身构建了VM 机制 ,由于通常的系统调用系统函数的话,会浪费必定的时间去移动和请求。

(6)value大小:redis最大能够达到1GB,而memcache只有1MB




7. Redis常见性能问题和解决方案:

(1) Master最好不要作任何持久化工做,如RDB内存快照和AOF日志文件;(Master写内存快照,save命令调度rdbSave函数,会阻塞主线程的工做,当快照比较大时对性能影响是很是大的,会间断性暂停服务,因此Master最好不要写内存快照;AOF文件过大会影响Master重启的恢复速度)

(2) 若是数据比较重要,某个Slave开启AOF备份数据,策略设置为每秒同步一次

(3) 为了主从复制的速度和链接的稳定性,Master和Slave最好在同一个局域网内

(4) 尽可能避免在压力很大的主库上增长从库

(5) 主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1 <- Slave2 <- Slave3...;这样的结构方便解决单点故障问题,实现Slave对Master的替换。若是Master挂了,能够马上启用Slave1作Master,其余不变。



8. Redis的回收策略

volatile-lru:从已设置过时时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰

volatile-ttl:从已设置过时时间的数据集(server.db[i].expires)中挑选将要过时的数据淘汰

volatile-random:从已设置过时时间的数据集(server.db[i].expires)中任意选择数据淘汰

allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰

allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰

no-enviction(驱逐):禁止驱逐数据

注意这里的6种机制,volatile和allkeys规定了是对已设置过时时间的数据集淘汰数据仍是从所有数据集淘汰数据,后面的lru、ttl以及random是三种不一样的淘汰策略,再加上一种no-enviction永不回收的策略。

  使用策略规则:

  一、若是数据呈现幂律分布,也就是一部分数据访问频率高,一部分数据访问频率低,则使用allkeys-lru

  二、若是数据呈现平等分布,也就是全部的数据访问频率都相同,则使用allkeys-random



9. 五种I/O模型介绍

IO 多路复用是5种I/O模型中的第3种,对各类模型讲个故事,描述下区别:

故事情节为:老李去买火车票,三天后买到一张退票。参演人员(老李,黄牛,售票员,快递员),往返车站耗费1小时。

1.阻塞I/O模型

老李去火车站买票,排队三天买到一张退票。

耗费:在车站吃喝拉撒睡 3天,其余事一件没干。



2.非阻塞I/O模型

老李去火车站买票,隔12小时去火车站问有没有退票,三天后买到一张票。

耗费:往返车站6次,路上6小时,其余时间作了好多事。



3.I/O复用模型

1.select/poll

老李去火车站买票,委托黄牛,而后每隔6小时电话黄牛询问,黄牛三天内买到票,而后老李去火车站交钱领票。 

耗费:往返车站2次,路上2小时,黄牛手续费100元,打电话17次

2.epoll

老李去火车站买票,委托黄牛,黄牛买到后即通知老李去领,而后老李去火车站交钱领票。 

耗费:往返车站2次,路上2小时,黄牛手续费100元,无需打电话


4.信号驱动I/O模型

老李去火车站买票,给售票员留下电话,有票后,售票员电话通知老李,而后老李去火车站交钱领票。 

耗费:往返车站2次,路上2小时,免黄牛费100元,无需打电话



5.异步I/O模型

老李去火车站买票,给售票员留下电话,有票后,售票员电话通知老李并快递送票上门。 

耗费:往返车站1次,路上1小时,免黄牛费100元,无需打电话


1同2的区别是:本身轮询

2同3的区别是:委托黄牛

3同4的区别是:电话代替黄牛

4同5的区别是:电话通知是自取仍是送票上门