Redis是如何实现高性能的?

2023-05-16

Redis作为应对高并发场景的利器,它是如何实现高性能的呢?

  1. IO多路复用
    传统对于并发情况,假如一个进程不行,那搞多个进程不就可以同时处理多个客户端连接了么?多进程是可以解决一些并发问题,但是还是有一些问题,上下文切换开销,线程循环创建,从PCB来回恢复效率较低。随着客户端请求增多,那么线程也随着请求数量直线上升,如果是并发的时候涉及到数据共享访问,有时候涉及到使用锁来控制范围顺序,影响其他线程执行效率。
    在学NIO之前得先去了解IO模型

(1)同步阻塞IO(Blocking IO):即传统的IO模型。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-njOowevo-1651371737212)(https://www.hengyumo.cn/momoclouddisk/file/download?code=202204162119998_image.png)]

(2)同步非阻塞IO(Non-blocking IO):默认创建的socket都是阻塞的,非阻塞IO要求socket被设置为NONBLOCK。注意这里所说的NIO并非Java的NIO(New IO)库。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p8O8nZGn-1651371737213)(https://www.hengyumo.cn/momoclouddisk/file/download?code=202204162120904_image.png)]

(3)多路复用IO(IO Multiplexing):即经典的Reactor设计模式,有时也称为异步阻塞IO,Java中的Selector和Linux中的epoll都是这种模型。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qlY4S5h7-1651371737213)(https://www.hengyumo.cn/momoclouddisk/file/download?code=202204162120150_image.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WEHg3VNQ-1651371737214)(https://www.hengyumo.cn/momoclouddisk/file/download?code=202204162121212_image.png)]

(4)异步IO(Asynchronous IO):即经典的Proactor设计模式,也称为异步非阻塞IO。

https://blog.csdn.net/didixiao_/article/details/89163723
https://blog.csdn.net/m0_67505824/article/details/123871848

  1. 单线程
  • 没有了访问共享资源加锁的性能损耗
  • 开发和调试非常友好,可维护性高
  • 减少多个线程上下文切换带来的额外开销
  1. 基于内存存储
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-85swSfh3-1651371737214)(https://www.hengyumo.cn/momoclouddisk/file/download?code=202204162123538_image.png)]

https://blog.csdn.net/zpwangshisuifeng/article/details/102610064
google 工程师Jeff Dean 首先在他关于分布式系统的ppt文档列出来的,到处被引用的很多。
1纳秒等于10亿分之一秒,= 10 ^ -9 秒

操作耗时
L1 cache reference 读取CPU的一级缓存0.5 ns
Branch mispredict(转移、分支预测)5 ns
L2 cache reference 读取CPU的二级缓存7 ns
Mutex lock/unlock 互斥锁\解锁100 ns
Main memory reference 读取内存数据100 ns
Compress 1K bytes with Zippy 1k字节压缩10,000 ns
Send 2K bytes over 1 Gbps network 在1Gbps的网络上发送2k字节20,000 ns
Read 1 MB sequentially from memory 从内存顺序读取1MB250,000 ns
Round trip within same datacenter 从一个数据中心往返一次,ping一下500,000 ns
Disk seek 磁盘搜索10,000,000 ns
Read 1 MB sequentially from network 从网络上顺序读取1兆的数据10,000,000 ns
Read 1 MB sequentially from disk 从磁盘里面读出1MB30,000,000 ns
Send packet CA->Netherlands->CA 一个包的一次远程访问150,000,000 ns

可以看到内存和磁盘操作的性能差距是巨大的。这也是redis之所以读写如此快的原因。

  1. 高效数据结构
    常用的六大Redis的数据结构,及他们各自的底层实现结构
  • string hash list set sortset(zset)
  • string的底层实现是简单动态字符串(SDS -simple dynamic string)
  • hash的底层实现是hash表或则压缩列表(ziplist)
  • list的底层实现是双向列表(quicklist)或者压缩列表
  • set的底层实现是hash表(hashtable)或者整数数组
  • sortset(zset)的底层实现是压缩列表或者跳表

各个数据结构的底层实现概览
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YPeHlRHs-1651371737214)(https://www.hengyumo.cn/momoclouddisk/file/download?code=202204162133750_image.png)]

https://www.jianshu.com/p/45def7c525dd

  1. 写时拷贝(CopyOnWrite)
    redis会从主进程中通过fork()系统调用,创建一个子进程,将父进程的 虚拟内存 与 物理内存 映射关系复制到子进程中,并将设置内存共享的,子进程只负责将内存里面数据写入到rdb进行持久化操作,如果在操作的时候主进程对内存修改了,使用写时拷贝技术,将对应的内存创建一个副本然后进行写入持久化。
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZRnAlgBt-1651371737215)(https://www.hengyumo.cn/momoclouddisk/file/download?code=202204162136362_image.png)]

RDB的流程:

当redis需要做持久化时,会fork一个子进程
redis根据pid判断下一步操作,如果是子进程,即pid==0时,则负责将内容写入临时RDB文件,完成之后通知父进程。
而父进程则继续处理命令并轮询等待子进程信号完成BGSAVE(“后台保存”)功能

pid = fork();
if(pid == 0){
	// 写入临时RDB文件
	rdbsave();
	// 通知父进程
	signal_parent();
} else if(pid > 0){
	// 父进程继续处理请求,并轮询子进程信号
	handle_request_and_wait_signal();
}

伪代码很简单,Redis每次快照持久化都是将内存数据完整写入到磁盘一次,并不是增量的只同步新数据。

如果数据量大的话,而且写操作比较多,必然会引起大量的磁盘io操作,可能会严重影响性能

因此redis会fork一个子进程出来干活,这也是为什么线上禁止使用SAVE命令的原因:可能会导致Redis阻塞!

https://blog.csdn.net/foxException/article/details/113268279

写时复制(Copy-On-Write),由前文可知,Linux复制子进程时,并不会为所有程序空间的块都分配物理块,写时复制技术在Fork技术上有了进一步的优化,Text段也不重新分配物理内存,也就是刚分配时是下面这种形式:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AaGSKFAA-1651371737215)(https://www.hengyumo.cn/momoclouddisk/file/download?code=202204162150215_image.png)]

写时复制:内核只为新生成的子进程创建虚拟空间结构,它们来复制于父进程的虚拟空间结构,但是不为这些段分配物理内存,任何段都不分配,它们共享父进程的物理空间,当父子进程中有更改相应段的行为发生时,再为子进程相应的段分配物理空间,例如途中的Stack块,注意重新分配是以内存页,也就是pagecache(4k)为基本单位的。

https://www.shuzhiduo.com/A/o75N6Nk95W/

  1. 客户端管道批量命令
    redis在客户端程序中做了一些优化引入了一个管道(pipelining)概念。
    管道会把多条无关命令批量执行,以减少多个命令分别执行带来的网络交互时间,在一些批量操作数据的场景。

redis客户端执行一条命令分4个过程:

发送命令-〉命令排队-〉命令执行-〉返回结果

这个过程称为Round trip time(简称RTT, 往返时间),Redis的原生批命令(mget和mset)有效节约了RTT,但大部分命令(如hgetall,并没有mhgetall)不支持批量操作,需要消耗N次RTT ,这个时候需要pipeline来解决这个问题。

pipeline可以将多次IO往返的时间缩减为一次RTT ,前提是pipeline执行的指令之间没有因果相关性。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GlVywtKQ-1651371737215)(https://www.hengyumo.cn/momoclouddisk/file/download?code=202204162200630_image.png)]

https://blog.csdn.net/TABE_/article/details/123775837

  1. 零拷贝技术
    我们知道,在服务端,处理消费的大致逻辑是这样的:
    首先,从文件中找到消息数据,读到内存中;
    然后,把消息通过网络发给客户端。
    这个过程中,数据实际上做了 2 次或者 3 次复制:
  • 从文件复制数据到 PageCache 中,如果命中 PageCache,这一步可以省掉;
  • 从 PageCache 复制到应用程序的内存空间中,也就是我们可以操作的对象所在的内存;
  • 从应用程序的内存空间复制到 Socket 的缓冲区,这个过程就是我们调用网络应用框架的 API 发送数据的过程。
    使用零拷贝技术可以把这个复制次数减少一次,上面的 2、3 步骤两次复制合并成一次复制。直接从 PageCache 中把数据复制到 Socket 缓冲区中,这样不仅减少一次数据复制,更重要的是,由于不用把数据复制到用户内存空间,DMA 控制器可以直接完成数据复制,不需要 CPU 参与,速度更快。
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rVvA8dO7-1651371737215)(https://www.hengyumo.cn/momoclouddisk/file/download?code=202204162044983_image.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vDUyL1Fz-1651371737216)(https://www.hengyumo.cn/momoclouddisk/file/download?code=202204162044407_image.png)]

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Redis是如何实现高性能的? 的相关文章

  • PooledRedisClientManager 未释放连接

    我将 json 数据列表存储在 redis 中并使用 ServiceStack c 客户端访问它 我本质上是在管理自己的外键 我在其中存储zrangeid 我使用应用程序内部的接口从zrange然后从 Redis 获取底层 json 对象并
  • 如何让客户端下载动态生成的非常大的文件

    我有一个导出功能 可以读取整个数据库并创建一个包含所有记录的 xls 文件 然后文件被发送到客户端 当然 导出完整数据库的时间需要大量时间 并且请求很快就会以超时错误结束 处理这种情况的最佳解决方案是什么 例如 我听说过使用 Redis 创
  • 是否有可嵌入的 Java 替代 Redis?

    根据这个线程 https stackoverflow com questions 3047010 best redis library for java 如果我想从Java中使用Redis Jedis是最好的选择 然而 我想知道是否有任何库
  • Docker-compose Predis 不通过 PHP 连接

    我正在尝试使用 docker compose 将 PHP 与 redis 连接 docker compose yml version 2 services redis image redis 3 2 2 php image company
  • Redis hash写入速度非常慢

    我面临一个非常奇怪的问题 使用 Redis 时 我的写入速度非常糟糕 在理想的情况下 写入速度应该接近 RAM 上的写入速度 这是我的基准 package redisbenchmark import redis clients jedis
  • Spring Data Redis - Lettuce连接池设置

    尝试在 spring data redis 环境中设置 Lettuce 连接池 下面是代码 Bean LettuceConnectionFactory redisConnectionFactory GenericObjectPoolConf
  • Redis INCRBY 有限制

    我想知道是否有一种方法可以通过我的应用程序的单次往返在 Redis 中执行此操作 对于给定的键K 其可能值V是范围内的任意整数 A B 基本上 它有上限和下限 When an INCRBY or DECRBY发出命令 例如INCRBY ke
  • 从redis中检索大数据集

    一台服务器上的应用程序查询另一台服务器上运行的 Redis 查询的结果数据集约为 250kzrangebyscore objects locations inf inf这在应用程序服务器上似乎需要 40 秒 当使用命令执行时redis cl
  • Caffeine Expiry 中如何设置多个过期标准?

    我正在使用 Caffeine v2 8 5 我想创建一个具有可变到期时间的缓存 基于 值的创建 更新以及 该值的最后一次访问 读取 无论先发生什么都应该触发该条目的删除 缓存将成为三层值解析的一部分 The key is present i
  • 无法启动redis.service:单元redis-server.service被屏蔽

    我在 ubuntu 16 04 上安装了 Redis 服务器 但是当我尝试使用启动redis服务时 sudo systemctl start redis 我收到消息 Failed to start redis service Unit re
  • Redis Docker compose无法处理RDB格式版本10

    我无法在 docker compose 文件中启动 redis 容器 我知道docker compose文件没问题 因为我的同事可以成功启动项目 我读到有一个删除 dump rdb 文件的解决方案 但我找不到它 我使用Windows机器 任
  • 如何在Redis中从hmset()切换到hset()?

    我收到弃用警告 即 Redis hmset 已弃用 请改用 Redis hset 但是 hset 采用第三个参数 我不知道是什么name应该是 info users 10 timestamp datetime utcnow strftime
  • 有没有办法用Lettuce自动发现Redis集群中新的集群节点IP

    我有一个Redis集群 3主3从 运行在一个库伯内斯簇 该集群通过Kubernetes 服务 Kube 服务 我将我的应用程序服务器连接到 Redis 集群 使用Kube 服务作为 URI 通过 Redis 的 Lettuce java 客
  • 使用 Celery 通过 Gevent 进行实时、同步的外部 API 查询

    我正在开发一个 Web 应用程序 该应用程序将接收用户的请求 并且必须调用许多外部 API 来编写对该请求的答案 这可以直接从主 Web 线程使用 gevent 之类的东西来扇出请求来完成 或者 我在想 我可以将传入的请求放入队列中 并使用
  • Scala 使用的 Redis 客户端库建议

    我正在计划使用 Scala 中的 Redis 实例进行一些工作 并正在寻找有关使用哪些客户端库的建议 理想情况下 如果存在一个好的库 我希望有一个为 Scala 而不是 Java 设计的库 但如果现在这是更好的方法 那么仅使用 Java 客
  • Redis+Docker+Django - 错误 111 连接被拒绝

    我正在尝试使用 Redis 作为使用 Docker Compose 的 Django 项目的 Celery 代理 我无法弄清楚我到底做错了什么 但尽管控制台日志消息告诉我 Redis 正在运行并接受连接 事实上 当我这样做时 docker
  • 如何使用 Redis 自动删除与模式匹配的键

    在我的 Redis DB 中 我有很多prefix
  • Spring Redis 排序键

    我在 Redis Spring Data Redis 中有以下键 localhost gt Keys 1 id 1 Name C5796 Site DRG1 2 id 2 Name CX1XE Site DG1 3 id 3 Name C5
  • 使用环境变量在 redis.conf 中设置动态路径

    我有一个环境变量MY HOME其中有一个目录的路径 home abc 现在 我有一个redis conf文件 我需要像这样设置这个路径 redis conf pidfile MY HOME local var pids redis pid
  • Web API 缓存 - 如何使用分布式缓存实现失效

    我有一个 API 目前不使用任何缓存 我确实有一个正在使用的中间件 它可以生成缓存标头 Cache Control Expires ETag Last Modified 使用https github com KevinDockx HttpC

随机推荐

  • Anaconda上设置虚拟环境,并在jupyter notebook中切换。

    个人记录 xff0c 但欢迎阅读和赐教 我之前在Anaconda Navigator中建立虚拟环境 xff0c 然后在jupyter notebook的terminal中增加对应环境的ipykernel xff0c 这样可行 xff0c 但
  • 字符,字节和编码

    级别 xff1a 初级 摘要 xff1a 本文介绍了字符与编码的发展过程 xff0c 相关概念的正确理解 举例说明了一些实际应用中 xff0c 编码的实现方法 然后 xff0c 本文讲述了通常对字符与编码的几种误解 xff0c 由于这些误解
  • http协议原理

    HTTP工作原理 HTTP协议定义Web客户端如何从Web服务器请求Web页面 xff0c 以及服务器如何把Web页面传送给客户端 HTTP协议采用了请求 响应模型 客户端向服务器发送一个请求报文 xff0c 请求报文包含请求的方法 URL
  • TLS协议/SSL协议

    历史背景 SSL Secure Socket Layer 安全套接层 是基于HTTPS下的一个协议加密层 xff0c 最初是由网景公司 xff08 Netscape xff09 研发 xff0c 后被IETF xff08 The Inter
  • TCP协议

    TCP 基础 https www jianshu com p ef892323e68f TCP 使用固定的连接 TCP 用于应用程序之间的通信 当应用程序希望通过 TCP 与另一个应用程序通信时 xff0c 它会发送一个通信请求 这个请求必
  • UDP协议

    UDP 概述 xff08 User Datagram Protocol xff0c 用户数据报协议 xff09 用户数据报协议 UDP 只在 IP 的数据报服务之上增加了很少一点的功能 xff0c 这就是复用和分用的功能以及查错检测的功能
  • TCP和UDP的区别

    TCP协议与UDP协议的区别 首先咱们弄清楚 xff0c TCP协议和UDP协议与TCP IP协议的联系 xff0c 很多人犯糊涂了 xff0c 一直都是说TCP协议与UDP协议的区别 xff0c 我觉得这是没有从本质上弄清楚网络通信 xf
  • 网络协议概述

    互联网协议介绍 互联网的核心是一系列协议 xff0c 总称为 互联网协议 xff08 Internet Protocol Suite xff09 xff0c 正是这一些协议规定了电脑如何连接和组网 我们理解了这些协议 xff0c 就理解了互
  • go 编写tcp和udp服务端和客户端

    TCP协议 TCP IP Transmission Control Protocol Internet Protocol 即传输控制协议 网间协议 xff0c 是一种面向连接 xff08 连接导向 xff09 的 可靠的 基于字节流的传输层
  • tcp黏包问题

    服务端代码如下 xff1a span class token keyword package span main span class token keyword import span span class token punctuati
  • go sync.Pool 深入

    new函数的调用时机和pool的内存释放规则 以下代码调用了四次Get函数 xff0c 但是并不是每次都会new 第一次 xff0c 是a 61 pool Get byte xff0c 首次Get xff0c 在pool的private私有
  • 【AI理论学习】深入理解扩散模型:Diffusion Models(DDPM)(理论篇)

    深入理解扩散模型 xff1a Diffusion Models 引言扩散模型的原理扩散过程反向过程优化目标 模型设计代码实现Stable Diffusion DALL E Imagen背后共同的套路Stable DiffusionDALL
  • gin 框架原理

    Gin的路由原理 Gin的路由基于Trie树和压缩字典树算法 xff0c 什么是Trie树 xff1f 其实很好理解 xff0c 看下图 xff1a 单词at xff0c bee xff0c ben xff0c bt xff0c q组成的T
  • PowerDesigner导入sql脚本

    1 依次点击File gt Reverse Engineer gt Database 2 弹出弹窗对模型进行命名 xff0c 同时在DBMS下拉选择框中需要选择自己对应的数据库类型 xff0c 点击确定 新的弹窗 xff0c 选中Using
  • Gin路由算法模拟

    概述 Gin的路由算法是采用压缩字典树实现的 xff0c 基数树 xff08 Radix Tree xff09 又称为PAT位树 xff08 Patricia Trie or crit bit tree xff09 xff0c 是一种更节省
  • 使用gomail发送邮件

    概述 为了实现一个邮件验证码功能 xff0c 特意了解了一下go如何发送邮件 本来以为会很麻烦 xff0c 没想到其实很简单 工具类 实现的工具类如下 xff1a span class token keyword package span
  • redis实现消息队列的几种方式及其优劣

    概述 常用的消息队列有 xff0c rabbitMq kafka RocketMq ActiveMq等 这些消息队列需要独立安装部署 xff0c 作为一个中间件来提供服务 xff0c 虽然有着高性能 高可靠的优点 xff0c 但是额外部署这
  • linux系统或者windows WSL安装redis最新版本

    概述 因为windows的最新版本redis已经在16年就停止更新了 xff0c 目前最新的版本只到redis3 xff0c 很多redis新增的特性都无法使用 例如redis5的stream xff0c windows版本就没有 这篇文章
  • 在面对一些亿级流量场景,消息队列届的排头兵kafka是如何保证高性能的呢?

    在面对一些亿级流量场景 xff0c 消息队列届的大哥kafka是如何保证高性能的呢 xff1f Kafka Reactor模型架构 Kafka客户端和服务端通信采取的是NIO的reactor模式 xff0c 它是一种事件驱动模式 react
  • Redis是如何实现高性能的?

    Redis作为应对高并发场景的利器 xff0c 它是如何实现高性能的呢 xff1f IO多路复用 传统对于并发情况 xff0c 假如一个进程不行 xff0c 那搞多个进程不就可以同时处理多个客户端连接了么 xff1f 多进程是可以解决一些并