Redis高性能原理:Redis为什么这么快?

news2024/11/16 11:44:12

目录

前言:

一、Redis知识系统观

 二、Redis为什么这么快?

 三、Redis 唯快不破的原理总结

四、Redis6.x的多线程


前言:

Redis 为了高性能,从各方各面都进行了优化。学习一门技术,通常只接触了零散的技术点,没有在脑海里建立一个完整的知识框架体系。这样会很吃力,而且会出现一看好像自己会,过后就忘记的懵逼情况。知识系统观其实是至关重要的,从某种程度上说,在解决问题时,拥有了系统观,就意味着你能有依据有章法地定位和解决问题。
————————————————

一、Redis知识系统观

Redis从应用维度有:缓存使用、集群运用、数据结构的巧妙使用;

Redis从系统维度有:可以归类为三高

  1. 高性能:线程模型、网络 IO 模型、数据结构、持久化机制;
  2. 高可用:主从复制、哨兵集群;
  3. 高拓展:Cluster 分片集群

Redis 为了高性能,从各方各面都进行了优化。

根据官方数据,Redis 的 QPS 可以达到约 100000(每秒请求数),有兴趣的可以参考官方的基准程序测试《How fast is Redis?》,官方地址:Redis benchmark | Docs

 

横轴是连接数,纵轴是 QPS。此时,这张图反映了一个数量级,希望大家在面试的时候可以正确的描述出来,不要问你的时候,你回答的数量级相差甚远!

 二、Redis为什么这么快?

  • Redis是基于内存操作,需要的时候需要我们手动持久化到硬盘中
  • Redis高效数据结构,对数据的操作也比较简单
  • Redis是单线程模型,从而避开了多线程中上下文频繁切换的操作
  • 使用多路I/O复用模型,非阻塞I/O
  • 使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求

1.1、基于内存的操作实现
Redis 是基于内存的数据库,不论读写操作都是在内存上完成的,完全吊打磁盘数据库的速度。Redis之所以可以使用单线程来处理,其中的一个原因是,内存操作对资源损耗较小,保证了处理的高效性。

如此宝贵的内存资源,Redis是怎么维护和管理的呢?

(1) 除了增删改查还有哪些维护性操作

命中率统计:在读取一个键之后,服务器会根据键是否存在来更新服务器的键空间命中次数或键空间不命中次数。

LRU时间更新:在读取一个键之后,服务器会更新键的LRU时间,这个值可以用于计算键的闲置时间。

惰性删除:如果服务器在读取一个键时发现该键已经过期,那么服务器会先删除这个过期键,然后才执行余下的其他操作。

键的dirty标识:如果有客户端使用WATCH命令监视了该键,服务器会将这个键标记为dirty,让事务程序注意到这个键已经被修改过。每次修改都会对dirty加一,用于触发持久化和复制。

数据库通知:如果服务器开启了数据库通知功能,那么在对键进行修改之后,服务器将按配置发送相应的数据库通知。

(2) Redis数据过期策略

Reids中数据过期策略采用定期删除+惰性删除策略。内存和CPU资源都是宝贵的,Redis的过期键删除通过定期删除设定合理的执行时长和执行频率,配合惰性删除兜底的方式,来达到CPU时间占用和内存浪费之间的平衡。

定期删除策略:Redis启用一个定时器定时监视所有key,判断key是否过期,过期的话就删除。这种策略可以保证过期的key最终都会被删除,但是也存在严重的缺点:每次都遍历内存中所有的数据,非常消耗CPU资源,并且当key已过期,但是定时器还处于未唤起状态,这段时间内key任然可以使用。du

惰性删除策略:在获取 key 时,先判断 key 是否过期,如果过期则删除。这种方式存在一个缺点:如果这个 key 一直未被使用,那么它一直在内存中,其实它已经过期了,会浪费大量的空间。

定期删除+惰性删除策略是如何工作的?

这两种策略天然的互补,结合起来之后,定时删除策略就发生了一些改变,不在是每次扫描全部的 key 了,而是随机抽取一部分 key 进行检查,这样就降低了对 CPU 资源的损耗,惰性删除策略互补了为检查到的key,基本上满足了所有要求。

但是有时候就是那么的巧,既没有被定时器抽取到,又没有被使用触发惰性删除,是否会把内存撑爆?回答是不会,当内存不够用时,内存淘汰机制就会上场。

Redis 内存淘汰机制有以下几种策略:

  • noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。(Redis 默认策略)
  • allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 Key。(推荐使用)
  • allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个 Key。
  • volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的 Key。这种情况一般是把 Redis 既当缓存,又做持久化存储的时候才用。
  • volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个 Key。
  • volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的 Key 优先移除。

修改内存淘汰机制只需要在 redis.conf 配置文件中配置 maxmemory-policy 参数即可。

值得一提的是,这里的lru和平常我们所熟知的lru还不完全一样,Redis使用的是采样概率的思想,省略了双向链表的内存消耗。Redis 会在每一次处理命令的时候判断是否达到了最大限制,如果达到则使用对应的算法去删除涉及到的Key,这时,我们前面所维护过键的LRU值就会派上用场了。

1.2、高效的数据结构
在 Redis 中存储value,常用的 5 种数据类型和应用场景如下:

  • String: 缓存、计数器、分布式锁等。
  • List: 链表、队列、微博关注人时间轴列表等。
  • Hash: 用户信息、Hash 表等。
  • Set: 去重、赞、踩、共同好友等。
  • Zset: 访问量排行榜、点击量排行榜等。

上面的应该叫做 Redis 支持的数据类型,也就是数据的保存形式。针对这 5 种数据类型,Redis底层都运用了哪些高效的数据结构来支持。

数据结构SDS的妙用举例

我们知道Redis的底层是用c语言来编写的,但是,数据结构确没有直接套用C的结构,而是根据Redis的定位自建了一套数据结构,比如说sds(简单动态字符串)。

 

Redis的String底层数据结构实现并没有直接使用C语言中的字符串,Redis中为了实现方便的扩展,考虑到安全和性能,自己定义了一个结构用来存储字符串,这个数据结构就是:简单动态字符串(Simple Dynamic String 简称sds),并将 SDS 用作 Redis 的默认字符串。

/*
 * redis中保存字符串对象的结构
 */
struct sdshdr {
    //用于记录buf数组中使用的字节的数目,和SDS存储的字符串的长度相等 
    int len;
    //用于记录buf数组中没有使用的字节的数目 
    int free;
    //字节数组,用于储存字符串
    char buf[]; //buf的大小等于len+free+1,其中多余的1个字节是用来存储’\0’的
};

 

(1)C语言中的字符串结构

C 语言中传统字符串是使用长度为 N+1 的字符数组来表示长度为 N 的字符串,并且字符串数组的最后一个元素总是空字符 '\0'。如下所示:

 

(2)SDS定义下的字符串结构

 

可以看到,相比于C语言来说,也就多了几个字段,分别用来标识空闲空间和当前数据长度,但简直是神来之笔:(这个\0对于用户来说是通用的,系统自动帮我们加上去)

  • 可以O(1)复杂度获取字符串长度:有len字段的存在,无需像C结构一样遍历计数。
  • 杜绝缓存区溢出:C字符串不记录已占用的长度,所以需要提前分配足够空间,一旦空间不够则会溢出。而有free字段的存在,让SDS在执行前可以判断并分配足够空间给程序
  • 减少字符串修改带来的内存重分配次数:有free字段的存在,使SDS有了空间预分配和惰性释放的能力。
  • 对二进制是安全的:二进制可能会有字符和C字符串结尾符 '\0' 冲突,在遍历和获取数据时产生截断异常,而SDS有了len字段,准确了标识了数据长度,不需担心被中间的 '\0' 截断。

(4)SDS 与 C 字符串区别

 

O(1) 时间复杂度获取字符串长度

C 语言字符串不记录长度信息,需要遍历整个字符串时间复杂度为 O(n),C 字符串遍历时遇到 '\0' 时结束。SDS 中 len 保存这字符串的长度,O(1) 时间复杂度。

空间预分配

SDS 被修改后,程序不仅会为 SDS 分配所需要的必须空间,还会分配额外的未使用空间。

分配规则如下:如果对 SDS 修改后,len 的长度小于 1M,那么程序将分配和 len 相同长度的未使用空间。举个例子,如果 len=10,重新分配后,buf 的实际长度会变为 10(已使用空间)+10(额外空间)+1(空字符)=21。如果对 SDS 修改后 len 长度大于 1M,那么程序将分配 1M 的未使用空间。

惰性空间释放

当对 SDS 进行缩短操作时,程序并不会回收多余的内存空间,而是使用 free 字段将这些字节数量记录下来不释放,后面如果需要 append 操作,则直接使用 free 中未使用的空间,减少了内存的分配。

二进制安全

在 Redis 中不仅可以存储 String 类型的数据,也可能存储一些二进制数据。

二进制数据并不是规则的字符串格式,其中会包含一些特殊的字符如 '\0',在 C 中遇到 '\0' 则表示字符串的结束,但在 SDS 中,标志字符串结束的是 len 属性。

(5)Redis数据类型与底层数据结构

对于链表、hash表和跳表来说,Redis在设计数据结构的时候出发点是一致的,总结起来就是一句话:空间换时间。用牺牲存储空间和微小的计算代价,来换取数据的快速操作。

为了追求速度,每种数据类型使用不同的数据结构支撑,底层数据结构有 6 种:

 

1.3、单线程模型
Redis6.x之前,一直在说单线程如何如之何的好。那么,具体单线程体现在哪里,又是怎么完成数据读写工作的呢?关于新版本的多线程模型在后面小节单独说,这里先说单线程。

(1)单线程模型

我们要明确的是:Redis 的单线程指的是 Redis 的网络 IO 以及键值对指令读写是由一个线程来执行的。 对于 Redis 的持久化、集群数据同步、异步删除等都是其他线程执行。

所谓单线程是指对数据的所有操作都是由一个线程按顺序挨个执行的,使用单线程好处:

不会因为线程创建导致的性能消耗;

避免上多线程上下文切换引起的 CPU开销;

避免了线程之间的竞争问题,比如添加锁、释放锁、死锁等,不需要考虑各种锁的问题。

代码更清晰,处理逻辑简单。

单线程是否没有充分利用 CPU 资源呢?

官方答案:因为 Redis 是基于内存的操作,使用Redis时,几乎不存在CPU成为瓶颈的情况, Redis主要受限于服务器内存和网络。例如在一个普通的Linux系统上,Redis通过使用pipelining每秒可以处理10万个请求,所以如果应用程序主要使用O(N)或O(log(N))的命令,它几乎不会占用太多CPU。既然单线程容易实现,而且 CPU 不会成为瓶颈,那就顺理成章地采用单线程的方案了。

然而,使用了单线程的处理方式,就意味着到达服务端的请求不可能被立即处理。那么怎么来保证单线程的资源利用率和处理效率呢?

(2) IO多路复用模型和性能优良的事件驱动

Redis 采用 I/O 多路复用技术,并发处理连接。采用了 epoll + 自己实现的简单的事件框架。epoll 中的读、写、关闭、连接都转化成了事件,然后利用 epoll 的多路复用特性,绝不在 IO 上浪费一点时间。

Redis服务端,从整体上来看,其实是一个事件驱动的程序,所有的操作都以事件的方式来进行。

 

如图所示,Redis的事件驱动架构由套接字、I/O多路复用、文件事件分派器、事件处理器四个部分组成:

套接字(Socket),是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。

I/O多路复用,通过监视多个描述符,当描述符就绪,则通知程序进行相应的操作,来帮助单个线程高效的处理多个连接请求。

Redis为每个IO多路复用函数都实现了相同的API,因此,底层实现是可以互换的。

Reids默认的IO多路复用机制是epoll,和select/poll等其他多路复用机制相比,epoll具有诸多优点:

 

 

事件驱动,Redis设计的事件分为两种,文件事件和时间事件,文件事件是对套接字操作的抽象,而时间事件则是对一些定时操作的抽象。

文件事件:

客户端连接请求(AE_READABLE事件)

客户端命令请求(AE_READABLE事件)和事

服务端命令回复(AE_WRITABLE事件)

时间事件: 分为定时事件和周期性时间;redis的所有时间事件都存放在一个无序链表中,当时间事件执行器运行时,需要遍历链表以确保已经到达时间的事件被全部处理。

可以看到,Redis整个执行方案是通过高效的I/O多路复用件驱动方式加上单线程内存操作来达到优秀的处理效率和极高的吞吐量。

1.4、IO多路复用模型
多路指的是多个 socket 连接,复用指的是复用一个线程。IO多路复用主要有三种技术:select,poll,epoll。epoll 是目前最新最好的IO多路复用技术。

IO多路复用的基本原理是,内核不是监视应用程序本身的连接,而是监视应用程序的文件描述符。当客户端运行时,它将生成具有不同事件类型的套接字。在服务器端,I / O 多路复用程序(I / O 多路复用模块)会将消息放入队列(也就是 下图的 I/O 多路复用程序的 socket 队列),然后通过文件事件分派器将其转发到不同的事件处理器。

简单来说:Redis 单线程情况下,内核会一直监听 socket 上的连接请求或者数据请求,一旦有请求到达就交给 Redis 线程处理,这就实现了一个 Redis 线程处理多个 IO 流的效果。

select/epoll 提供了基于事件的回调机制,即针对不同事件的发生,调用相应的事件处理器。所以 Redis 一直在处理事件,提升 Redis 的响应性能。

Redis 线程不会阻塞在某一个特定的监听或已连接套接字上,也就是说,不会阻塞在某一个特定的客户端请求处理上。正因为此,Redis 可以同时和多个客户端连接并处理请求,从而提升并发性

1.5、高效的的 Gossip 通信协议
Redis 采用了 Gossip 协议作为通信协议。Gossip 是一种传播消息的方式,可以类比为瘟疫或者流感的传播方式,使用 Gossip 协议的有:Redis Cluster、Consul、Apache Cassandra 等。Gossip 协议类似病毒扩散的方式,将信息传播到其他的节点,这种协议效率很高,只需要广播到附近节点,然后被广播的节点继续做同样的操作即可。当然这种协议也有一个弊端就是:会存在浪费,哪怕一个节点之前被通知到了,下次被广播后仍然会重复转发。

 三、Redis 唯快不破的原理总结

纯内存操作:一般都是简单的存取操作,线程占用的时间很少,时间的花费主要集中在 IO 上,所以读取速度快。

采用单线程模型:保证了每个操作的原子性,也减少了线程的上下文切换和竞争。

Redis 使用IO多路复用模型:非阻塞 IO,使用了单线程来轮询描述符,将数据库的开、关、读、写都转换成了事件,Redis 采用自己实现的事件分离器,效率比较高。

Redis高效数据结构,对数据的操作也比较简单:

        (1)整个 Redis 就是一个全局哈希表:他的时间复杂度是 O(1),而且为了防止哈希冲突导致链表过长,Redis 会执行 rehash 操作,扩充 哈希桶数量,减少哈希冲突。并且防止一次性重新映射数据过大导致线程阻塞,采用 渐进式 rehash。巧妙的将一次性拷贝分摊到多次请求过程后总,避免阻塞。

        (2)Redis 全程使用 hash 结构:读取速度快,还有一些特殊的数据结构,对数据存储进行了优化,如压缩表,对短数据进行压缩存储,再如,跳表,使用有序的数据结构加快读取的速度。可以根据实际存储的数据类型选择不同编码。

四、Redis6.x的多线程

Redis 6.0.1 于 2020 年 5 月 2 日正式发布了,如 Redis 作者 antirez 所说,这是迄今为止最“企业”化的版本,也是有史以来改动最大的一个 Redis 版本。这个版本提供了诸多令人心动的新特性及功能改进,比如新网络协议RESP3,新的集群代理,ACL等,其中关注度最高的应该是“多线程”了。

Redis的多线程模型,不是传统意义上的多线程并发,而是把socket解析回写的这部分操作并行化,以解决IO上的时间消耗带来的系统瓶颈。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1818316.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

pioneer电源维修PM33213BP-10P-1-6PH-H

开关电源出现不启振的时候,我们通常需要查看开关频率是否正确、保护电路是否断路、电压反馈电路、电流反馈电路又没问题,开关管是否击穿等。 电源维修实践中,有许多开关电源采用UC38系列8脚PWM组件,大多数电源不能工作都是因为电…

搜索二叉树的概念及实现

搜索二叉树的概念 搜索二叉树规则(左小右大): 非空左子树的键值小于其根节点的键值非空右子树的键值大于其根节点的键值左右子树均为搜索二叉树 如图: 在搜索时,若大于根,则去右子树寻找;若小…

基于单片机的多功能智能小车设计

第一章 绪论 1.1 课题背景和意义 随着计算机、微电子、信息技术的快速发展,智能化技术的发展速度越来越快,智能化与人们生活的联系也越来越紧密,智能化是未来社会发展的必然趋势。智能小车实际上就是一个可以自由移动的智能机器人,比较适合在人们无法工作的地方工作,也可…

基于WPF技术的换热站智能监控系统06--实现左侧故障统计

1、区域划分 2、ui实现 这里使用的是livechart的柱状图呈现的 3、运行效果 走过路过不要错过,点赞关注收藏又圈粉,共同致富,为财务自由作出贡献

【git使用二】gitee远程仓库创建与本地git命令用法

目录 gitee介绍 管理者注册gitee账号 管理者在gitee网站上创建远程仓库 每个开发者安装git与基本配置 1.git的下载和安装 2.配置SSH公钥 3.开发者信息配置 git命令用法 gitee介绍 Gitee(又称码云)是一个基于Git的代码托管服务,由开源…

python数据分析-量化分析

一、研究背景 随着经济的发展和金融市场的不断完善,股票投资成为了人们重要的投资方式之一。汽车行业作为国民经济的重要支柱产业,其上市公司的股票表现备受关注。Fama-French 三因子模型是一种广泛应用于股票市场的资产定价模型,它考虑了市场…

vue3中用setup写的数据,不能动态渲染(非响应式)解决办法

相比于2.0&#xff0c;vue3.0在新增了一个setup函数&#xff0c;我们在setup中可以写数据也可以写方法&#xff0c;就像我们以前最开始学习js一样&#xff0c;在js文件中写代码。 For instance <template><div class"person"><h2>姓名&#xff1…

中电联系列四:rocket手把手教你理解中电联协议!

分享《慧哥的充电桩开源SAAS系统&#xff0c;支持汽车充电桩、二轮自行车充电桩。》 电动汽车充换电服务信息交换 第4部分&#xff1a;数据传输与安全 Interactive of charging and battery swap service information for electric vehicle Part 4:Data transmission and secu…

平安科技智能运维案例

平安科技智能运维案例 在信息技术迅速发展的背景下&#xff0c;平安科技面临着运维规模庞大、内容复杂和交付要求高等挑战。通过探索智能运维&#xff0c;平安科技建立了集中配置管理、完善的运营管理体系和全生命周期运维平台&#xff0c;实施了全链路监控&#xff0c;显著提…

基于块生成最大剩余空间的三维装箱算法

问题简介 三维装箱问题&#xff08;3D Bin Packing Problem&#xff0c;3D BPP&#xff09;是一类组合优化问题。它涉及到将一定数量的三维物品放入一个或多个三维容器&#xff08;称为“箱子”&#xff09;中&#xff0c;同时遵循一定的约束&#xff0c;通常目标是最大化空间…

2024/06/13--代码随想录算法2/17| 62.不同路径、63. 不同路径 II、343. 整数拆分 (可跳过)、96.不同的二叉搜索树 (可跳过)

62.不同路径 力扣链接 动态规划5步曲 确定dp数组&#xff08;dp table&#xff09;以及下标的含义&#xff1a; dp[i][j] &#xff1a;表示从&#xff08;0 &#xff0c;0&#xff09;出发&#xff0c;到(i, j) 有dp[i][j]条不同的路径。确定递推公式&#xff0c;dp[i][j] d…

听说前端都是切图仔,所以学了PS

PS 从零开始-基础篇 什么话都不想说了&#xff0c;前端以死后端已死&#xff0c;毁灭即是新生&#xff0c;我要开始追梦了&#xff0c; 从小就希望&#xff0c;制作一款自己的游戏&#x1f3ae;去学了编程&#xff0c;了解了&#xff1a;Java、C#、前端... 不小心入了web领域…

分享一个开发者工具

网站地址&#xff1a;https://daxia.olalo.asia/计算器 IP地址查询 BMI体脂计算 AI ChatGPT GPT 黄历 图片缩放 图片裁剪 图片水印 图片拼接 json查看器 二维码识别 二维码生成 Base64编解码 时间戳 天气 取色器 拾色器https://daxia.olalo.asia/ 很干净的一个小网站&#xff0…

ppt添加圆角矩形,并调整圆角弧度方法

一、背景 我们看的论文&#xff0c;许多好看的图都是用PPT做的&#xff0c;下面介绍用ppt添加圆角矩形&#xff0c;并调整圆角弧度方法。 二、ppt添加圆角矩形&#xff0c;并调整圆角弧度 添加矩形&#xff1a; 在顶部工具栏中&#xff0c;点击“插入”选项卡。 在“插图”…

Llama-3安装方法及应用

Hi~&#xff01;这里是奋斗的小羊&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f4a5;&#x1f4a5;个人主页&#xff1a;奋斗的小羊 &#x1f4a5;&#x1f4a5;所属专栏&#xff1a;C语言 &#x1f680;本系列文章为个人学习…

【日记】第一次养植物,没什么经验……(781 字)

正文 前两天梦见灵送的几盆植物全都死掉了。梦里好伤心。醒来与她说这件事&#xff0c;她宽慰我说&#xff0c;梦都是反着的&#xff0c;肯定能活得很好的。于是忽然记起昨天给植物换水时&#xff0c;文竹的根居然从花盆底部伸吊了出来&#xff0c;以前都没有这种情况来着&…

为什么要用AI大模型?

前言 2021 年 8 月份&#xff0c;李飞飞和 100 多位学者联名发表一份 200 多页的研究报告《On the Opportunities and Risk of Foundation Models》&#xff0c;深度地综述了当前大规模预训练模型面临的机遇和挑战。 语言模型已经深刻变革了自然语言处理领域的研究和实践。近…

ui自动化中,几种文件上传整理2024

input标签的 对于input标签实现的文件上传&#xff0c;看成输入框&#xff0c;直接send_keys 非input标签的 要使用第三方库&#xff1a; 方式1&#xff1a; pip install pypiwin32 要点&#xff1a;图片路径要写路径 方式2&#xff1a;pip install pyautogui 方式3&#…

Idea | Idea提交.properties文件乱码问题

这里 Transparent natice-to-ascii conversion 自动转换ASCII码 千万别勾选