为什么Redis使用单线程模型会达到每秒万级别的处理能力呢?可以将其归结为三点使Redis具有很高的吞吐量?
1. 纯内存访问
Redis将所有数据放在内存中,内存的响应时长大约为100纳秒,这是Redis达到每秒万级别访问的重要基础。
纯内存数据库,如果只是简单的 key-value,内存不是瓶颈。一般情况下,hash 查找可以达到每秒数百万次的数量级。瓶颈在于网络 IO 上。每次请求需要通过网络把请求发送到 redis 所在的机器,然后等待 redis 返回数据。时间大部分消耗在网络传输中。如果把 redis 和客户端放在同一台机器,网络延迟会更小,一般情况下可以打到 60000 次每秒甚至更高,取决于机器性能。
2. I/O多路复用、事件驱动模型
旨在解决IO的问题。多路I/O复用模型是非阻塞IO,内部实现采用epoll和自己实现的事件分离框架。
其利用select、poll、epoll 可以同时检测多个流的 I/O 事件的能力,在空闲的时候,会把当前线程阻塞掉,当有一个或多个流有 I/O 事件时,就从阻塞态中唤醒,于是程序就会轮询一遍所有的流(epoll 是只轮询那些真正发出了事件的流),并且只依次顺序的处理就绪的流,这种做法就避免了大量的无用操作。
“多路”指的是多个网络连接,“复用”指的是复用同一个线程。采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络 IO 的时间消耗)。
总结就是Redis使用epoll作为I/O多路复用技术的实现,再加上Redis自身的事件处理模型将epoll中的连接、读写、关闭都转换为事件,不在网络I/O上浪费过多的时间,如下图所示。
3. 单线程避免了线程切换和竞态产生的消耗。
线程模型
主线程
其它
Redis会fork得到的子进程,用来处理RDB持久化以及AOF持久化等任务
一组异步任务处理线程,即Redis异步化组件——BIO组件
BIO组件目前包括三个线程,分别处理三种类型的任务:
1)文件句柄关闭任务
2)AOF持久化任务
3)空间懒释放
持久化
Redis 提供了两种持久化策略
RDB 持久化机制,会在一段时间内生成指定时间点的数据集快照(snapshot)
AOF 持久化机制,记录 server 端收到的每一条写命令,当 server 重启时会进行重放以此来重建之前的数据集。AOF 文件中的命令全部以 Redis 协议的格式来保存,新命令会被追加(append)到文件的末尾。 Redis 还可以在后台对 AOF 文件进行重写(rewrite) ,使得 AOF 文件的体积不会超出保存数据集状态所需的实际大小。
如果你仅使用 Redis 作为缓存加速访问,你可以关闭这两个持久化设置
你也可以同时开启这两个持久化设置,但是在这种情况下,Redis 重启时会使用 AOF 文件来重建数据集,因为 AOF 文件保存的数据往往更加完整
单线程利弊
单线程模型能带来几个好处:
第一,单线程可以简化数据结构和算法的实现。并发数据结构实现不但困难而且开发测试比较麻烦。
第二,单线程避免了线程切换和竞态产生的消耗,对于服务端开发来说,锁和线程切换通常是性能杀手。
但是单线程会有一个问题:
对于每个命令的执行时间是有要求的。如果某个命令执行过长,会造成其他命令的阻塞,对于Redis这种高性能的服务来说是致命的,所以Redis是面向快速执行场景的数据库。