单线程的redis为何这么快

为何说Redis是单线程的而且这么快redis

 

其它开源软件采用的模型数据库

 

Nginx:多进程单线程模型 安全

Memcached:单进程多线程模型服务器

Redis:单进程单线程网络

 

 

单线程的redis为何这么快数据结构

主要是如下三点多线程

(一)纯内存操做架构

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

 

(二)单线程操做避免了频繁的上下文切换框架

避免了没必要要的上下文切换和竞争条件,也不存在多进程或者多线程致使的切换而消耗 CPU,不用去考虑各类锁的问题,不存在加锁释放锁操做,没有由于可能出现死锁而致使的性能消耗;

 

多线程处理可能涉及到锁 

多线程处理会涉及到线程切换而消耗CPU

 

多线程对同一个Key操做时,Redis服务是根据先到先做的原则,其余排队(可设置为直接丢弃),由于是单线程。

修改默认的超时时间,默认2秒。可是大部份的操做都在30ms之内。

  

(三)采用了非阻塞I/O多路复用机制

内部实现采用epoll,采用了epoll+本身实现的简单的事件框架。epoll中的读、写、关闭、链接都转化成了事件,而后利用epoll的多路复用特性,毫不在io上浪费一点时间

 

(四)数据结构简单,对数据操做也简单,Redis中的数据结构是专门进行设计的;

 

 

咱们使用单线程的方式是没法发挥多核CPU 性能,不过咱们能够经过在单机开多个Redis 实例来完善!

警告:这里咱们一直在强调的单线程,只是在处理咱们的网络请求的时候只有一个线程来处理,一个正式的Redis Server运行的时候确定是不止一个线程的,这里须要你们明确的注意一下!

例如Redis进行持久化的时候会以子进程或者子线程的方式执行(具体是子线程仍是子进程待读者深刻研究)

 

 

Redis为何是单线程的?

由于CPU不是Redis的瓶颈。Redis的瓶颈最有多是机器内存或者网络带宽。

既然单线程容易实现,并且CPU不会成为瓶颈,那就瓜熟蒂落地采用单线程的方案了。

关于redis的性能,官方网站也有,普通笔记本轻松处理每秒几十万的请求,参见:How fast is Redis?

 

若是万一CPU成为你的Redis瓶颈了,或者,你就是不想让服务器其余核闲置,那怎么办?

那也很简单,你多起几个Redis进程就行了。Redis是keyvalue数据库,又不是关系数据库,数据之间没有约束。只要客户端分清哪些key放在哪一个Redis进程上就能够了。redis-cluster能够帮你作的更好。

 

单线程能够处理高并发请求吗?

固然能够了,Redis都实现了。有一点概念须要澄清,并发并非并行。

(相关概念:并发性I/O流,意味着可以让一个计算单元来处理来自多个客户端的流请求。并行性,意味着服务器可以同时执行几个事情,具备多个计算单元)

 

单线程处理的缺点?

没法发挥多核CPU性能,不过能够经过在单机开多个Redis实例来完善

    

单进程单线程的Redis如何可以高并发

采用多路 I/O 复用技术可让单个线程高效的处理多个链接请求(尽可能减小网络IO的时间消耗) 

 

Redis不存在线程安全问题? 

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

 

 

redis的一些其余特色:

(1)Redis是单进程单线程的

redis利用队列技术将并发访问变为串行访问,消除了传统数据库串行控制的开销

 

(2)读写分离模型

经过增长Slave DB的数量,读的性能能够线性增加。为了不Master DB的单点故障,集群通常都会采用两台Master DB作双机热备,因此整个集群的读和写的可用性都很是高。

读写分离架构的缺陷在于,不论是Master仍是Slave,每一个节点都必须保存完整的数据,若是在数据量很大的状况下,集群的扩展能力仍是受限于单个节点的存储能力,并且对于Write-intensive类型的应用,读写分离架构并不适合。

 

(3)数据分片模型

为了解决读写分离模型的缺陷,能够将数据分片模型应用进来。

能够将每一个节点当作都是独立的master,而后经过业务实现数据分片。

结合上面两种模型,能够将每一个master设计成由一个master和多个slave组成的模型。

 

(4)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

 

 

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,其余不变。

 

 

1. Redis服务端是个单线程的架构,不一样的Client虽然看似能够同时保持链接,但发出去的命令是序列化执行的,这在一般的数据库理论下是最高级别的隔离(serialize)

2. 用MULTI/EXEC 来把多个命令组装成一次发送,达到原子性

3. 用WATCH提供的乐观锁功能,在你EXEC的那一刻,若是被WATCH的键发生过改动,则MULTI到EXEC之间的指令所有不执行,不须要rollback

4. 其余回答中提到的DISCARD指令只是用来撤销EXEC以前被暂存的指令,并非回滚