Redis为什么那么快?

redis是一款高性能的开源内存数据结构存储系统,被广泛应用于缓存、会话存储、实时分析、消息队列等场景。我们都知道Redis很快,它QPS可达10万(每秒请求数)。Redis为什么这么快呢?

redis有值但是查询偶发返回空的问题_redis

1、内存机制

我们都知道,内存读写是比磁盘读写快很多的。Redis是基于内存存储实现的数据库,相对于数据存在磁盘的数据库,就省去磁盘磁盘I/O的消耗。MySQL等磁盘数据库,需要建立索引来加快查询效率,而Redis数据存放在内存,直接操作内存,所以就很快。

redis有值但是查询偶发返回空的问题_数据库_02


此外,Redis还支持将数据持久化到磁盘,以防止系统故障或重启导致的数据丢失。

2、高效的数据结构

Redis内置了多种高效的数据结构,如字符串、哈希表、列表、集合和有序集合等。这些数据结构都经过优化,能够在时间复杂度为O(1)的情况下完成大部分操作。例如,通过使用哈希表存储数据,Redis能够快速地进行读写操作,而不需要像传统数据库那样进行磁盘的随机访问。

MySQL索引为了提高效率,选择了B+树的数据结构。其实合理的数据结构,就是可以让你的应用/程序更快。先看下Redis的数据结构&内部编码图:

redis有值但是查询偶发返回空的问题_缓存_03

数据结构与数据类型的对应关系如下图所示:

redis有值但是查询偶发返回空的问题_缓存_04

3、合理的数据编码

Redis支持多种数据基本类型,每种基本类型对应不同的数据结构,每种数据结构对应不一样的编码。为了提高性能,Redis设计者总结出,数据结构最适合的编码搭配。
Redis是使用对象(redisObject)来表示数据库中的键值,当我们在 Redis 中创建一个键值对时,至少创建两个对象,一个对象是用做键值对的键对象,另一个是键值对的值对象。

typedef struct redisObject{
    //类型
   unsigned type:4;
   //编码
   unsigned encoding:4;
   //指向底层数据结构的指针
   void *ptr;
    //...
 }robj;

redisObject中,type 对应的是对象类型,包含String对象、List对象、Hash对象、Set对象、zset对象。encoding 对应的是编码。

String:如果存储数字的话,是用int类型的编码;如果存储非数字,小于等于39字节的字符串,是embstr;大于39个字节,则是raw编码。
List:如果列表的元素个数小于512个,列表每个元素的值都小于64字节(默认),使用ziplist编码,否则使用linkedlist编码
Hash:哈希类型元素个数小于512个,所有值小于64字节的话,使用ziplist编码,否则使用hashtable编码。
Set:如果集合中的元素都是整数且元素个数小于512个,使用intset编码,否则使用hashtable编码。
Zset:当有序集合的元素个数小于128个,每个元素的值小于64字节时,使用ziplist编码,否则使用skiplist(跳跃表)编码

4、合理的线程模型

4.1 单线程模型:避免了上下文切换

Redis是单线程的,其实是指Redis的网络IO和键值对读写是由一个线程来完成的。但Redis的其他功能,比如持久化、异步删除、集群数据同步等等,实际是由额外的线程执行的。
Redis的单线程模型,避免了CPU不必要的上下文切换和竞争锁的消耗。也正因为是单线程,如果某个命令执行过长(如hgetall命令),会造成阻塞。Redis是面向快速执行场景的内存数据库,所以要慎用如lrange和smembers、hgetall等命令。

4.2 I/O 多路复用

什么是I/O多路复用?

  • I/O :网络 I/O
  • 多路 :多个网络连接
  • 复用:复用同一个线程。
  • IO多路复用其实就是一种同步IO模型,它实现了一个线程可以监视多个文件句柄;一旦某个文件句柄就绪,就能够通知应用程序进行相应的读写操作;而没有文件句柄就绪时,就会阻塞应用程序,交出cpu。

多路I/O复用技术可以让单个线程高效的处理多个连接请求,而Redis使用用epoll作为I/O多路复用技术的实现。并且Redis自身的事件处理模型将epoll中的连接、读写、关闭都转换为事件,不在网络I/O上浪费过多的时间。

在许多场景下,单线程反而能够更高效地利用CPU资源,提高了系统的整体性能。此外,Redis通过非阻塞I/O和事件驱动机制,能够处理大量并发连接。

总结

综合以上几个因素,Redis在性能方面表现出色,能够处理高并发的读写请求,具备出色的响应速度。

那么Redis到底有多快?具体的性能表现会受到多种因素的影响,如硬件配置、数据集大小、访问模式等。在一些基准测试中,Redis能够每秒处理数十万甚至数百万的操作。它的高性能使得它成为许多应用场景中的首选解决方案。

Redis的内存存储。由于内存的读写速度远高于磁盘,Redis将数据存储在内存中,从而实现了高速的读写操作。同时,Redis还采用了一些内存管理技术来优化内存的使用效率。例如,它使用了对象共享、压缩列表、虚拟内存等技术来减少内存的消耗。此外,Redis还支持数据的持久化到磁盘,以确保数据的安全性和可靠性。通过将内存中的数据写入磁盘,Redis可以在系统重启或故障恢复时重新加载数据,从而实现数据的持久化。

另外,Redis的单线程模型也是其高性能的关键之一。虽然单线程在处理大量请求时可能会有一定的限制,但由于Redis的操作都是非阻塞的,它能够快速地执行各种操作。此外,Redis还使用了事件驱动的机制来处理网络请求,通过I/O多路复用技术实现高效的网络通信。这种网络模型能够同时处理大量的连接请求,保持高性能和低延迟。

此外,Redis内置了许多高效的数据结构,这些数据结构在设计时就考虑了性能和效率。例如,Redis的哈希表和有序集合都能在常数时间内完成插入、删除和查找等操作,这使得Redis非常适合用于计数器、排行榜、发布订阅等场景。此外,Redis还提供了丰富的操作命令和功能,如事务、发布订阅、Lua脚本等,进一步提高了其灵活性和性能。

综上所述,Redis之所以如此快速,是由于多个方面的优化和设计。它的内存存储、单线程模型、高效的数据结构、异步方式的持久化和优秀的网络模型等因素共同作用,使得Redis能够在高并发的环境下提供出色的性能和响应速度。无论是作为缓存、会话存储还是消息队列,Redis都是一个强大而高效的解决方案。