Redis是当前煊赫一时的NoSQL数据库,几乎已经成为高并发、高可用系统的标配了。如果对Redis响应快的认知仅仅停留在基于内存和单线程的层面,恐怕是很难斩获大厂的Offer。本篇重点全面介绍Redis的单线程模型及如何支撑高并发的。
面试
Redis可以支撑高并发的第一个重要的缘由就是彻底基于内存,这在很大程度上避免了诸如MySQL等关系型数据库须要频繁的磁盘I/O(磁盘I/O的寻道时间、旋转延迟、传输时间等很耗时,皆是ms级的开销,而基于内存基本都是ns级的开销,彻底不是一个数量级)。
redis
说到I/O模型,你们必定能想到传统意义上的Blocking I/O,该模型的工做方式是:当对某一个文件描述符(File Descriptor,如下简称FD)读写时,若是当前FD不可读或不可写,则不会对其它进程的操做作出响应,致使总体服务不可用。算法
文件描述符:在形式上是一个非负整数。实际上它指向内核为每个进程所维护的该进程打开文件的记录表。
在 I/O 多路复用模型中,最重要的函数调用就是 select,该方法的可以同时监控多个文件描述符的可读可写状况,当其中的某些文件描述符可读或者可写时,select 方法就会返回可读以及可写的文件描述符个数。
数据库
Redis内部使用文件事件处理器File event handler,该处理器是单线程的,因此Redis才叫作单线程模型。它采用 I/O 多路复用机制同时监听多个 Socket,根据 Socket上的事件来选择对应的事件处理器进行处理。Redis服务采用Reactor的方式来实现文件事件处理器,以下图所示:
综上分析,Redis支撑高并发的第二个缘由就是基于非阻塞式的I/O多路复用机制,是单线程模型,这在很大程度上避免了多线程频繁上下文切换的消耗。
数组
Redis提供了string、hash、list、set、sorted set五种基本数据结构,而且还包含了HyperLogLog、Pub/Sub等多种扩展的优秀数据结构。
Redis中数据结构不只仅是丰富,阅读源码你还会发现,设计也很巧妙很灵活,如string底层不一样于C语言的实现,而是经过一种SDS(Simple Dynamic String)
结构实现的,本质上是一个带长度信息的字节数组。这里少侠以字典hash为例,介绍Redis是如何作到快速响应的。
数据结构
Redis中的hash和Java中的HashMap几乎同样,都是经过分桶的方式存储元素并解决hash冲突,第一维是数组,第二维是链表,以下图所示,数组中保存的是链表中第一个元素的指针。多线程
在介绍Redis的hash函数前,少侠想先引入一个概念:hash攻击,简而言之,就是由于hash函数具备偏向性,黑客会根据这种偏向性进行攻击,致使hash的链表长度极不均匀,甚至全部的元素都集中到一个链表,最终会致使查询性能急剧降低(从O(1)退化到O(n))。
那么Redis如何设计hash函数的呢?不一样于Java中的hash算法,Redis实际上采用了一种称为siphash
算法,这个算法是兼顾了随机性和性能的结果。
架构
说到hash,不可避免的就是扩容,可是Redis是单线程的,而且一些热点数据频繁被请求,若是此时扩容Redis很难承受这样的耗时过程。因此Redis采用了渐进式rehash策略。
渐进式rehash会在rehash的同时,保留新旧两个hash结构,以下图所示,查询时会同时查询两个hash结构,而后会在操做指令(hget,hdel)中实现数据迁移,可是若是客户端一直没有命令来触发此类操做,Redis会在定时任务中主动对hash进行数据迁移。并发
Redis为什么这么快实际上是个涉及面很普遍的问题,面试者若是仅仅停留在基于内存和多路IO复用这些概念层的理解,在面试中是要吃亏的。熟练运用,深刻原理才能在面试中斩获佳绩。分布式
我是少侠露飞,热爱技术,热爱分享。