面试必问:Redis为什么快!我整理了份学习文档,现在它是你的了!

本文详细分析了Redis快速的原因,包括内存操作、单线程设计、数据结构如全局哈希表和底层数据结构,如动态字符串、跳跃表等,以及为何选择单线程和内存优化的重要性。
摘要由CSDN通过智能技术生成

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

在开发过程中,最常用的中间件就是Redis了。它可以用作数据库、缓存和消息代理,甚至可以用它实现分布式锁。

我们利用Redis很大的一部分原因就是因为Redis它很“快”!根据官方说法,Redis的性能确实非常出色,可以达到每秒10万次以上的QPS(每秒请求数)。
在这里插入图片描述


一、Redis为什么这么快?

大家都知道Redis很“快”!但是有的时候一问起来就只知道:纯内存、单线程等等,但是一细问,就不明所以了。这篇文章,我就带大家好好理理Redis为什么“快”的原因,如思维导图所示,将从四部分来进行说明:

  1. 纯内存操作;
  2. 单线程;
  3. 数据结构;
    在这里插入图片描述

二、原因之一:存内存操作

Redis以存储在内存中的方式进行操作,这是它高性能的关键之一。因为Redis主要将数据存储在系统的内存中,而不是在硬盘上。这使得数据的读取和写入速度非常快,因为内存的访问速度远高于硬盘。

总的来说,内存操作比硬盘快是因为内存提供了更快的存取速度、无机械部件的结构、更好的并发性能、较好的缓存效果以及更高效的随机访问性能。这使得内存特别适合处理需要快速响应时间和高并发访问的应用场景,如数据库缓存、实时数据处理等。然而,硬盘仍然在大容量、持久性存储等方面有其独特的优势。

三、原因之二:单线程


1、概念

Redis 是单线程,主要是指 Redis 的网络 IO 和键值对读写是由一个线程来完成的,这也是 Redis 对外提供键值存储服务的主要流程。但 Redis 的其他功能,比如持久化、异步删除、集群数据同步等,其实是由额外的线程执行的。

2、为什么用单线程?

Redis采用单线程模型的设计主要基于以下几个考虑:

  1. 原子性操作: Redis强调原子性操作,即在一个事务中执行的操作是不可分割的。单线程模型可以确保这种原子性,因为在同一时间只有一个命令被执行。这对于保持数据的一致性非常重要。
  2. 避免竞态条件: 单线程模型避免了多线程并发执行可能引发的竞态条件(Race Conditions)。在多线程环境中,需要使用锁来保护共享资源,而锁的使用可能导致性能下降和复杂性增加。通过使用单线程,Redis简化了并发控制,减少了潜在的竞态条件。
  3. 避免线程切换开销: 线程切换是由于多线程而引入的额外开销。在单线程模型下,不存在线程切换的开销,从而提高了性能。对于很多 Redis 的应用场景,单线程的性能已经足够满足需求。
  4. 简化设计和维护: 单线程模型使得 Redis 的设计和维护更加简化。复杂的多线程并发控制和同步问题可能会增加系统的复杂性,并引入潜在的难以调试的问题。通过使用单线程,Redis能够更容易理解和维护。
  5. 非阻塞 I/O 和事件驱动: 单线程模型配合非阻塞 I/O 和事件驱动机制,使得 Redis 能够高效地处理并发请求。通过监听和响应事件,而不是阻塞等待,提高了系统的吞吐量。

虽然 Redis 采用单线程模型,但这并不意味着它不能有效利用多核处理器。通过在多个进程之间分片数据、使用主从复制等技术,Redis 能够在多核系统中进行横向扩展,提高整体性能。在很多应用场景下,Redis 的单线程模型已经足够高效,尤其是在读多于写、内存充足的情况下。

四、原因之三:数据结构

1、全局哈希表

Redis整体使用一个全局的哈希表来储存所有的键值对,让我们可以用 O(1) 的时间复杂度来快速查找到键值对——我们只需要计算键的哈希值,就可以知道它所对应的哈希桶位置,然后就可以访问相应的 entry 元素。
在这里插入图片描述

学过哈希表的都知道,通过哈希表可以很快的获取到存储的键值对数据。但是哈希表也有一个很明显的缺点,那就是随着数据量的增大,会引发大量的哈希冲突,会出现不同的key计算出一样的哈希值的情况。

redis采用链式哈希来解决哈希冲突,即同一个桶里面的元素使用链表保存(和Java里面的HashMap是一样的)。但是这样会引来另外一个问题:那就是随着链表的增加,查询效率会降低。毕竟你通过哈希计算,找到对应的桶的位置后,可能还需要遍历一遍链表才能找到对应的元素,这对Redis这个以“快”著称的着实是忍不了。

那怎么办呢?

简单粗暴的办法就是把哈希桶的数量进行扩容,如重新分配一个新的哈希表空间,空间是原来的两倍。Redis也确实是这样做的,不过Redis并不是简单的把哈希表 1 中的数据重新映射并拷贝到哈希表 2 中;因为一旦涉及大量的数据拷贝,会造成redis线程阻塞。

Redis这边采取的是渐进式 Rehash:

  1. 离散的概念,将迁移数据的工作量均摊到每次操作中,避免迁移造成不可用。代价就是在较长的时间存在两个表。
  2. 把一次性大量拷贝的开销,分摊到了多次处理请求的过程中,避免了耗时操作,保证了数据的快速访问。
  3. 如果长时间未有请求进入,redis自身后台会有定时任务,定时执行rehash操作。

Rehash是一种重新分配散列表槽位的过程,用于应对数据集增大导致的哈希冲突和性能下降问题。渐进式rehash则是一种改进的rehash策略,它允许在扩容过程中继续服务请求,而不会造成明显的性能下降。

2、底层数据结构

在前面介绍全局哈希表中,我们知道了Redis中是使用键值对来保存数据的。key的数据结构为String,value的数据结构有String、List、Hash、Sorted Set和Set。

以下是对这些数据类型的简要介绍:

String(字符串):

特性: 最简单的数据类型,可以包含任意数据,如文本、二进制数据等。
适用场景: 用于存储单个值,如计数器、用户信息等。
常用命令: SET、GET、INCR、DECR等。

List(列表):

特性: 有序字符串元素集合,可以在列表的两端进行快速的推入和弹出操作。
适用场景: 用于实现队列、栈、消息队列等。
常用命令: LPUSH、RPUSH、LPOP、RPOP、LRANGE等。

因地制宜地使用 List 类型。例如,既然它的 POP/PUSH 效率很高,那么就将它主要用于 FIFO 队列场景,而不是作为一个可以随机读写的集合。 PS: 其实Redis快的原因,我认为这一点也算:那就是有丰富的数据类型,可以供使用者因地制宜的进行选择,通过场景选择合适的且正确的数据类型,比其他的优化来得更为直接和高效!

Hash(哈希表):

特性: 用于存储字段和与其关联的值,类似于关联数组或对象。
适用场景: 用于表示对象,每个字段是对象的属性,对应的值是属性的值。
常用命令: HSET、HGET、HMSET、HGETALL等。

Sorted Set(有序集合):

特性: 类似于集合,每个元素都有一个分数(score)与之关联,根据分数进行排序。
适用场景: 用于需要有序集合的场合,如排行榜。
常用命令: ZADD、ZRANGE、ZREVRANGE、ZINCRBY等。

Set(集合):

特性: 无序字符串元素集合,每个元素都是唯一的。
适用场景: 用于处理成员唯一性的场合,如标签、用户的兴趣爱好等。
常用命令: SADD、SMEMBERS、SISMEMBER、SUNION等。

但,这只是我们可以操作Redis的数据类型,不同的数据类型有不同的应用场景,这也是Redis受欢迎的原因。但是,更为重要的还是Redis的底层数据结构上,即上图上最下面的黄色框。

Redis在底层使用了多种数据结构来支持不同类型的数据存储和操作。以下是一些Redis底层数据结构:

动态字符串(Dynamic String):

Redis中的字符串数据类型是通过动态字符串实现的。动态字符串是一个可变长度的字符数组,允许在字符串的两端进行高效地追加、删除操作。

双向链表(Doubly Linked List):

Redis使用双向链表来实现列表(List)数据类型。双向链表支持在两端进行快速的插入和删除操作。

哈希表(Hash Table):

Redis使用哈希表来实现哈希表(Hash)数据类型。哈希表支持高效的键值对存储和查找。

跳跃表(Skip List):

跳跃表是一种有序数据结构,使用多级索引,可通过索引的快速跳转,实现数据的快速定位,支持对元素进行快速的插入、删除和查找操作。

整数集合(Intset):

为了节省内存,Redis引入了整数集合数据结构,用于存储整数值。整数集合根据值的大小采用不同的编码方式,如16位整数、32位整数、64位整数等。

压缩列表(ziplist):

为了节省内存,Redis引入了压缩列表数据结构,用于存储小型列表和哈希表。压缩列表通过紧凑的存储格式减少内存消耗

介绍到这里,其实有一个问题,那就是:整数数组和压缩列表在查找时间复杂度方面并没有很大的优势,那为什么 Redis 还会把它们作为底层数据结构呢?

答案就是:

1、内存利用率,数组和压缩列表都是非常紧凑的数据结构,它比链表占用的内存要更少。Redis是内存数据库,大量数据存到内存中,此时需要做尽可能的优化,提高内存的利用率。

2、数组对CPU高速缓存支持更友好,所以Redis在设计时,集合数据元素较少情况下,默认采用内存紧凑排列的方式存储,同时利用CPU高速缓存不会降低访问速度。当数据元素超过设定阈值后,避免查询时间复杂度太高,转为哈希和跳表数据结构存储,保证查询效率。


以上图片,一些引用的是极客时间课程《Redis 核心技术与实战》内插图(作者:蒋德钧),特此说明。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值