Redis 是单线程结构,但为何单线程还能支持高并发?

/ Redis / 没有评论 / 1460浏览

Redis 是单线程结构,但为何单线程还能支持高并发?

现在随便问一个程序员都能大谈特谈 Redis,但是你在深入一点问他:Redis 是单线程结构的,但为何单线程还能支持高并发?你一想,好像有点矛盾啊。和我们理解的有点不一样啊,高并发一般都是需要多个线程来处理吧,为什么 Redis 设计反而倒退来呢?本文将带着这样的疑惑和大家一起来聊聊 Redis 之所以高效的原因!

一说到 Redis,大家想到的肯定是它是一个内存数据库,可以用它来做缓存。那我就问,缓存我也可以用 Map 或者 EHcache 来做啊,为什么要用 Redis?

Map 和 EHcache 只能做单机的,而且 Map 需要自己维护等,我现在需要集群环境,所以我要用 Redis。

做集群 Memcahed 也可以做啊,不一定非要用 Redis 吧?

是的,Memcahed 主要是数据结构单一。而且 Redis 比 Memcahed 更火等等。

说了一大推,其实没说到重点,而且是一点一点的挤牙膏。根本原因是对 Redis 理解的不够深!

关于 Redis 和 Memcahed 的区别不是本文的重点,想要了解的可以参考:memcached redis 对比分析

回到主题,当我把这个问题发到各个技术群后,一些人就开始 Redis 的简介之类的话题。根本不和我意。

要回答完整这个问题,我们先从分布式架构来思考思考。

先说明,这只是我个人的理解!

首先,Redis 最开始的设计可能就是想做一个缓存来用。但是分布式环境复杂,暴露的问题可能比较多,所以 Redis 就要做集群。做集群后,可能和 Memcahed 效果类似了,我们要超越它,所以可能就有了多数据类型的存储结构。光做缓存,如何已宕机数据就丢失了。我们的口号是超越 Memcahed,所以我们要支持数据持久化。于是可能就有了 AOF 和 RDB,就可以当数据库来用来。这样 Redis 的高效可靠的设计,所以它又可以用来做消息中间件。这就是 Redis 的三大特点,可以用来做:缓存、数据库和消息中间件

再来说说,Redis 如何设计成但进程单线程的?

根据官方的测试结果《How fast is Redis?》来看,在操作内存的情况下,CPU 并不能起到决定性的作用,反而可能带来一些其他问题。比如锁,CPU 切换带来的性能开销等。这一点我们可以根据官方的测试报告,提供的数据来证明。而且官方提供的数据是可以达到100000+的QPS(每秒内查询次数),这个数据并不比采用单进程多线程 Memcached 差!所以在基于内存的操作,CPU不是 Redis 瓶颈的情况下,官方采用来单进程单线程的设计。

好了,说完单线程设计后,我们再来讨论讨论单线程的设计为什么能支持高并发?

原因有以下几点:

第一,我们请求 Redis 更多的是操作内存。直接操作内存就很快啊,数据存在内存中,类似于 HashMap。HashMap 的优势就是查找和操作的时间复杂度都是 O(1)。

第二,单线程,没有 CPU 上下文切换带来的开销问题。而且上面也说了,内存操作和 CPU 的多核影响不大。直接采用单线程,就不用考虑各种锁,与之相关的加锁,解锁,死锁等问题就不复存在了。

第三,多路 IO 复用。这个后面我会具体的来讲讲它。能谈到这一点说明对 Redis 有一定的理解。这涉及到基于操作系统的网络 IO 模型。Reactor 网络模式,epoll,poll,select,kqueue 等多路复用 IO。

第四,依赖第二点。由于是单线程的,所以就存在一个顺序读写问题。大家可以比较以下,随机读写和顺序读写的速度。

第五,Redis 的数据结构,是经过专门的研究和设计的。所以操作起来简单且快。

第六,Redis 自己构建了VM 机制 。因为一般的调用系统函数,会浪费一定的时间。

综合以上内容,Redis 才有单线程,高性能的特点。

最后,再说一点,Redis 是单进程和单线程的设计,并不是说它不能多进程多线程。比如备份时会 fork 一个新进程来操作;再比如基于 COW 原理的 RDB 操作就是多线程的