redis为什么这个快?

一、什么是redis

Redis(Remote Dictionary Server)是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。它通常被用作缓存系统,可以显著提高应用的性能。

Redis是一个基于Key-Value存储结构的Nosql开源内存数据库。它提供了5种常用的数据类型,String、Map、Set、ZSet、List。针对不同的结构,可以解决不同场景的问题。因此它可以覆盖应用开发中大部分的业务场景,比如top10问题、好友关注列表、热点话题等。

其次,由于Redis是基于内存存储,并且在数据结构上做了大量的优化所以IO性能比较好,在实际开发中,会把它作为应用与数据库之间的一个分布式缓存组件。

并且它又是一个非关系型数据的存储,不存在表之间的关联查询问题,所以它可以很好的提升应用程序的数据IO效率。

最后,作为企业级开发来说,它又提供了主从复制+哨兵、以及集群方式实现高可用在Redis集群里面,通过hash槽的方式实现了数据分片,进一步提升了性能。

二、Redis为什么这么快?

redis为什么这么快?将有以下几个方面揭晓:

1. 内存数据存储

Redis将所有数据存储在内存中,我们都知道内存存取速度远快于磁盘,因此Redis能提供极高的性能。==省去磁盘I/O的消耗==。虽然它也支持数据持久化,但这并不影响其读写速度,因为数据的读写都是直接与内存交互。

2. 高效的数据结构

Redis支持多种数据结构(如字符串、列表、集合、散列等),这些数据结构都被设计为能够高效地处理数据。这意味着Redis可以更快地执行复杂的操作。

先看看Redis的数据结构&内部编码:

* SDS 简单动态字符串

二进制安全:Redis 可以存储一些二进制数据,在 C 语言中字符串遇到'\0'会 结束,而 SDS 中标志字符串结束的是 len 属性。

* 字典

Redis 作为 K-V 型内存数据库,所有的键值就是用字典来存储。字典就是哈希表,比如HashMap,通过key 就可以直接获取到对应的 value。而哈希表的特性,在O(1)时间复杂度就可以获得对应的值。

* 跳跃表

字符串长度处理:Redis 获取字符串长度,时间复杂度为 O(1),而 C 语言中,需要从头开始遍历,复杂度为 O(n);

空间预分配:字符串修改越频繁的话,内存分配越频繁,就会消耗性能,而SDS 修改和空间扩充,会额外分配未使用的空间,减少性能损耗。

惰性空间释放:SDS 缩短时,不是回收多余的内存空间,而是 free 记录下多余的空间,后续有变更,直接使用 free 中记录的空间,减少分配。
跳跃表是 Redis 特有的数据结构,就是在链表的基础上,增加多级索引提升查找效率。

跳跃表支持平均 O(logN),最坏 O(N)复杂度的节点查找,还可以通过顺序性操作批量处理节点。

3. 合理的数据编码

Redis 支持多种数据数据类型,每种基本类型,可能对多种数据结构。什么时候,使用什么样数据结构,使用什么样编码,是 redis 设计者总结优化的结果。

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. 单线程模型

Redis使用单线程模型来处理命令,避免了常见的多线程或多进程环境下的并发问题和复杂的同步问题,这使得Redis能够更快地处理请求。

5. 优化的网络IO

Redis使用多路复用技术和非阻塞IO来处理并发连接,这使得Redis可以在单个线程内同时处理多个网络连接,从而提供更高的吞吐量。

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

6. 虚拟内存机制

因此,通过优化内存存储、数据结构、线程模型和网络IO,Redis能够提供极高的性能。

7、简单高效的操作

Redis提供了简单、直观且高效的操作接口,使得开发者能够快速地进行数据操作。例如,通过使用Redis的原子性操作来实现分布式锁、计数器等常见的功能。

三、多方面深入的了解I/O多路复用

1、概念

IO多路复用机制,==核心思想是让单个线程去监视多个连接,一旦某个连接就绪,也就是触发了读/写事件==。

就通知应用程序,去获取这个就绪的连接进行读写操作。

也就是在应用程序里面可以使用单个线程同时处理多个客户端连接,在对系统资源消耗较少的情况下提升服务端的链接处理数量。

在IO多路复用机制的实现原理中,客户端请求到服务端后,此时客户端在传输数据过程中,为了避免Server端在read客户端数据过程中阻塞,服务端会把该请求注册到Selector复路器上,服务端此时不需要等待,只需要启动一个线程,通过selector.select()阻塞轮询复路器上就绪的channel即可,也就是说,如果某个客户端连接数据传输完成,那么select()方法会返回就绪的channel,然后执行相关的处理就可以了。

IO多路复用是一种同步的IO模型。利用IO多路复用模型,可以实现一个线程监视多个文件句柄;一旦某个文件句柄就绪,就能够通知到对应应用程序进行相应的读写操作;没有文件句柄就绪时就会阻塞应用程序,从而释放出CPU资源。

IO可以理解为,在操作系统中,数据在内核态和用户态之间的读、写操作,大部分情况下是指网络IO;

多路大部分情况下是指多个TCP连接,也就是多个Socket或者多个Channel;

复用是指复用一个或多个线程资源。IO多路复用意思就是说,一个或多个线程处理多个TCP连接。尽可能地减少系统开销,无需创建和维护过多的进程/线程。

graph TD A[开始] -->|创建文件描述符集合| B[创建文件描述符集合] B -->|将Redis服务器的监听套接字添加到集合| C[将Redis服务器的监听套接字添加到集合] C -->|设置超时时间| D[设置超时时间] D -->|等待I/O事件发生| E[等待I/O事件发生] E -->|有新连接| F[有新连接] E -->|有客户端可读事件| G[有客户端可读事件] E -->|有客户端可写事件| H[有客户端可写事件] E -->|客户端异常| I[客户端异常] F -->|接受新连接| E G -->|读取客户端请求| E H -->|向客户端发送响应| E I -->|关闭客户端连接| E E -->|结束| K[结束]

2、实现IO多路复用的三种模型

* Select模型

select模型,它的基本原理是,采用轮询和遍历的方式。也就是说,在客户端操作服务器时,会创建三种文件描述符,简称FD。分别是writefds(写描述符)、readfds(读描述符)和exceptfds(异常描述符)。

而select会阻塞监视这三种文件描述符,等有数据、可读、可写、出异常或超时都会返回;返回后通过遍历fdset,也就是文件描述符的集合,来找到就绪的FD,然后,触发相应的IO操;

它的==优点==是跨平台支持性好,几乎在所有的平台上支持。

它的==缺点==也很明显,由于select是采用轮询的方式进行全盘扫描,因此,随着FD数量增多而导致性能下降。

因此,每次调用select()方法,都需要把FD集合从用户态拷贝到内核态,并进行遍历。而操作系统对单个进程打开的FD数量是有限制的,一般默认是1024个。虽然,可以通过操作系统的宏定义FD_SETSIZE修改最大FD数量限制,但是,在IO吞吐量巨大的情况下,效率提升仍然有限。

* poll模型

poll模型的原理与select模型基本一致,也是采用轮询加遍历,唯一的区别就是poll采用链表的方式来存储FD。

所以,它的==优点==是没有最大FD的数量限制。

它的==缺点==和select一样,也是采用轮询方式全盘扫描,同样也会随着FD数量增多而导致性能下降。

* epoll模型

由于select和poll都会因为吞吐量增加而导致性能下降,因此,才出现了epoll模型。epoll模型是采用时间通知机制来触发相关的IO操作。它没有FD个数限制,而且从用户态拷贝到内核态只需要一次。它主要通过系统底层的函数来注册、激活FD,从而触发相关的IO操作,这样大大提高了性能。主要是通过调用以下三个系统函数:

1、epoll_create()函数,在系统启动时,会在Linux内核里面申请一个B+树结构的文件系统,然后,返回epoll对象,也是一个FD。

2、epoll_ctl()函数,每新建一个连接的时候,会同步更新epoll对象中的FD,并且绑定一个callback回调函数。

3、epoll_wait()函数,轮询所有的callback集合,并触发对应的IO操作
所以,epoll模型最大的==优点==是将轮询改成了回调,大大提高了CPU执行效率,也不会随FD数量的增加而导致效率下降。当然,它也没有FD数量限制,也就是说,它能支持的FD上限是操作系统的最大文件句柄数。一般而言,1G内存大概支持10万个句柄。分布式系统中常用的组件如Redis、Nginx都是优先采用epoll模型。

它的==缺点==是只能在Linux下工作。

3、I/O多路复用模型综合对比

select poll epoll
数据结构 数组 链表 B+树
最大连接数 1024 无上限 无上限
FD拷贝 每次调用select拷贝 每次调用poll拷贝 FD首次调用epoll_ctl拷贝 每次调用epoll_wait不拷贝
工作效率 轮询:O(N) 轮询:O(N) 轮询:O(1)
相关推荐
Q_192849990612 分钟前
基于Spring Boot的九州美食城商户一体化系统
java·spring boot·后端
ZSYP-S40 分钟前
Day 15:Spring 框架基础
java·开发语言·数据结构·后端·spring
yuanbenshidiaos1 小时前
C++----------函数的调用机制
java·c++·算法
唐叔在学习1 小时前
【唐叔学算法】第21天:超越比较-计数排序、桶排序与基数排序的Java实践及性能剖析
数据结构·算法·排序算法
ALISHENGYA1 小时前
全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之分支结构(switch语句)
数据结构·算法
chengooooooo1 小时前
代码随想录训练营第二十七天| 贪心理论基础 455.分发饼干 376. 摆动序列 53. 最大子序和
算法·leetcode·职场和发展
jackiendsc1 小时前
Java的垃圾回收机制介绍、工作原理、算法及分析调优
java·开发语言·算法
Yuan_o_1 小时前
Linux 基本使用和程序部署
java·linux·运维·服务器·数据库·后端
程序员一诺2 小时前
【Python使用】嘿马python高级进阶全体系教程第10篇:静态Web服务器-返回固定页面数据,1. 开发自己的静态Web服务器【附代码文档】
后端·python
DT辰白2 小时前
如何解决基于 Redis 的网关鉴权导致的 RESTful API 拦截问题?
后端·微服务·架构