共计 1972 个字符,预计需要花费 5 分钟才能阅读完成。
前言
Redis 是以后煊赫一时的 NoSQL 数据库,简直曾经成为高并发、高可用零碎的标配了。若是对 Redis 响应快的认知仅仅停留在基于内存和单线程的层面,恐怕是很难斩获大厂的 Offer。本篇重点全面介绍 Redis 的单线程模型及如何撑持高并发的。
1 Redis 齐全基于内存
Redis 可能撑持高并发的第一个重要的起因就是齐全基于内存,这在很大水平上防止了诸如 MySQL 等关系型数据库须要频繁的磁盘 I /O(磁盘 I / O 的寻道工夫、旋转提早、传输工夫等很耗时,皆是 ms 级的开销,而基于内存根本都是 ns 级的开销,齐全不是一个数量级)。
2 Redis 的线程模型
2.1 阻塞式 I / O 模型 Blocking I/O
说到 I / O 模型,大家肯定能想到传统意义上的 Blocking I/O,该模型的工作形式是:当对某一个文件描述符(File Descriptor,以下简称 FD)读写时,如果以后 FD 不可读或不可写,则不会对其它过程的操作做出响应,导致整体服务不可用。
文件描述符:在模式上是一个非负整数。实际上它指向内核为每一个过程所保护的该过程关上文件的记录表。
2.2 I/ O 多路复用
在 I/O 多路复用模型中,最重要的函数调用就是 select,该办法的可能同时监控多个文件描述符的可读可写状况,当其中的某些文件描述符可读或者可写时,select 办法就会返回可读以及可写的文件描述符个数。
2.3 Redis 的单线程模型
Redis 外部应用文件事件处理器 File event handler,该处理器是单线程的,所以 Redis 才叫做单线程模型。它采纳 I/O 多路复用机制同时监听多个 Socket,依据 Socket 上的事件来抉择对应的事件处理器进行解决。Redis 服务采纳 Reactor 的形式来实现文件事件处理器,如下图所示:
综上剖析,Redis 撑持高并发的第二个起因就是基于非阻塞式的 I / O 多路复用机制,是单线程模型,这在很大水平上防止了多线程频繁上下文切换的耗费。
3 Redis 丰盛灵便的数据类型
Redis 提供了 string、hash、list、set、sorted set 五种根本数据结构,并且还蕴含了 HyperLogLog、Pub/Sub 等多种扩大的优良数据结构。
Redis 中数据结构不仅仅是丰盛,浏览源码你还会发现,设计也很奇妙很灵便,如 string 底层不同于 C 语言的实现,而是通过一种 SDS(Simple Dynamic String)
构造实现的,实质上是一个 带长度信息的字节数组。这里少侠以字典 hash 为例,介绍 Redis 是如何做到疾速响应的。
3.1 hash 内部结构
Redis 中的 hash 和 Java 中的 HashMap 简直一样,都是通过分桶的形式存储元素并解决 hash 抵触,第一维是数组,第二维是链表,如下图所示,数组中保留的是链表中第一个元素的指针。
3.2 hash 函数的抉择
在介绍 Redis 的 hash 函数前,少侠想先引入一个概念:hash 攻打 ,简而言之,就是因为 hash 函数具备偏差性,黑客会依据这种偏差性进行攻打,导致 hash 的链表长度极不平均,甚至所有的元素都集中到一个链表,最终会导致查问性能急剧下降(从 O(1) 进化到 O(n))。
那么 Redis 如何设计 hash 函数的呢?不同于 Java 中的 hash 算法,Redis 实际上采纳了一种称为 siphash
算法,这个算法是兼顾了随机性和性能的后果。
3.3 渐进式 rehash
说到 hash,不可避免的就是扩容,然而 Redis 是单线程的,并且一些热点数据频繁被申请,如果此时扩容 Redis 很难接受这样的耗时过程。所以 Redis 采纳了渐进式 rehash 策略。
渐进式 rehash 会在 rehash 的同时,保留新旧两个 hash 构造,如下图所示,查问时会同时查问两个 hash 构造,而后会在操作指令 (hget,hdel) 中实现数据迁徙,然而如果客户端始终没有命令来触发此类操作,Redis 会在定时工作中被动对 hash 进行数据迁徙。
4 Redis 如何解决并发竞争问题
- 客户端加锁(ReentrantLock 或 synchronized),但此形式只限于单机加锁,无奈解决分布式系统的并发竞争问题。
- 乐观锁(redis 的命令 watch):
当执行多键值事务操作时,Redis 不仅要求这些键值须要落在同一个 Redis 实例上,还要求落在同一个 slot 上,所以 redis 的事务比拟鸡肋,不过能够想方法遵循 redis 外部的分片算法把设计到的所有 key 分到同一个 slot。 - redis 的 setnx 实现分布式锁:
要设置超时工夫,避免抢占到锁的客户端因失败、解体或其余起因没有方法开释锁而造成死锁。
小结
Redis 为何这么快其实是个涉及面很宽泛的问题,面试者如果仅仅停留在基于内存和多路 IO 复用这些概念层的了解,在面试中是要吃亏的。纯熟使用,深刻原理能力在面试中斩获佳绩。
我是少侠露飞,酷爱技术,酷爱分享。