Redis的高性能怎么做到的?(redisson性能)
mhr18 2024-10-31 13:31 20 浏览 0 评论
Redis的高性能怎么做到的?
Redis这个NOSQL数据库在计算机界可谓是无人不知,无人不晓。只要涉及到数据那么就需要数据库,数据库类型很多,但是NOSQL的kv内存数据库也很多,redis作为其中一个是怎么做到行业天花板的呢?是怎么做到高性能的呢?怎么做到高可用的呢?今天这篇八股文我就整理一些redis的设计写写,本篇还是偏关于高性能这一块。
高效数据结构
Redis的数据库相比传统的关系数据库,在数据结构上也是比较特殊的,它的所有数据类型都可以看做是一个map的结构,key作为查询条件。
基本数据结构
Redis基于KV内存数据库,它内部构建了一个哈希表,根据指定的KEY访问时,只需要O(1)的时间复杂度就可以找到对应的数据,而value的值又是一些拥有各种特性的数据结构,这就给redis在数据操作的时候提供很好的性能了。
基于内存存储
相比传统的关系数据库,数据文件可能以lsm tree 或者 b+ tree形式存在硬盘上,这个时候读取文件要有io操作了,而redis在内存中进行,并不会大量消耗CPU资源,所以速度极快。
存储金字塔
内存从上图可以看到它介于硬盘和cpu缓存中间的,相比硬盘查找数据肯定是快的,当然这里笔者个人见解上,如果关系型数据库把一些平凡操作的数据库也放置在内存中缓存,也会得到一些性能的提升,像操作系统里面缺页异常一样处理,把数据片段通过一些特殊算法缓存在内存里面,减少文件io的开销。
io多路复用
传统对于并发情况,假如一个进程不行,那搞多个进程不就可以同时处理多个客户端连接了么?多进程是可以解决一些并发问题,但是还是有一些问题,上下文切换开销,线程循环创建,从PCB来回恢复效率较低。随着客户端请求增多,那么线程也随着请求数量直线上升,如果是并发的时候涉及到数据共享访问,有时候涉及到使用锁来控制范围顺序,影响其他线程执行效率。(进程在Linux也可以理解为线程,每个进程只是有一个线程,当然这里我上面写的进程,别纠结这些。。。)
线程是运行在进程上下文的逻辑流,一个进程可以包含多个线程,多个线程运行在同一进程上下文中,因此可共享这个进程地址空间的所有内容,解决了进程与进程之间通信难的问题,同时,由于一个线程的上下文要比一个进程的上下文小得多,所以线程的上下文切换,要比进程的上下文切换效率高得多。
像redis和Nginx这种应用就是单线程的程序,为什么他们能做到这么强的性能?首先看一个例子:
- Blocking IO
“
中午吃饭,我给餐厅老板说要一碗‘热干面’,然后我就在那边一直等着老板做,老板没有做好,我就一直在哪里等着什么也不做,直到‘热干面’做好。
这个流程就是我们常说的Blocking I/O如图:
blocking io
同步阻塞 IO模型中,应用程序发起 read 调用后,会一直阻塞,直到内核把数据拷贝到用户空间。
- Non Blocking IO
切换一下常见:
“
同样你中午吃饭,给餐厅老板说要一碗‘热干面’,然后老板开始做了,你每隔几分钟向老板问一下‘好了吗?’,直到老板说好了,你取到‘热干面’结束。
非阻塞io
同步非阻塞 IO模型中,应用程序会一直发起read调用,等待数据从内核空间拷贝到用户空间的这段时间里,线程依然是阻塞的,直到在内核把数据拷贝到用户空间,通过轮询操作,避免了一直阻塞,取回热干面的过程就是内核把准备好的数据交换到用户空间过程。
综上两种模型,缺点都是差不多,都是在等待内核准备数据,然后阻塞等待,同样逃不开阻塞这个问题,应用程序不断进行I/O系统调用轮询数据是否已经准备好的过程是十分消耗CPU资源的。
- I/O Multiplexing
还是之前那个例子:
“
中午吃饭,给餐厅老板说要一碗‘热干面’,然后老板安排给下面的厨子做,具体哪个厨子做不知道,有好几个厨子,然后老板每隔一段时间询问下面的厨子有木有做好,如果做好了,就通知我来去取餐。
多路复用
IO多路复用模型中,线程首先发起 select 调用,询问内核数据是否准备就绪,等内核把数据准备好了,用户线程再发起 read调用,read 调用的过程(数据从内核空间->用户空间)还是阻塞的。
Reactor模式
Reactor通过 I/O复用程序监控客户端请求事件,收到事件后通过任务分派器进行分发。针对建立连接请求事件,通过 Acceptor 处理,并建立对应的handler 负责后续业务处理,针对非连接事件,Reactor会调用对应的handler 完成 read->业务处理->write 处理流程,并将结果返回给客户端,整个过程都在一个线程里完成。
redis里面模型
Redis 是基于 Reactor 单线程模式来实现的,IO多路复用程序接收到用户的请求后,全部推送到一个队列里,交给文件分派器。对于后续的操作,和在 reactor 单线程实现方案里看到的一样,整个过程都在一个线程里完成,因此 Redis 被称为是单线程的操作。
我们平时说的Redis单线程快是指它的请求处理过程非常地快!在单线程中监听多个Socket的请求,在任意一个Socket可读/可写时,Redis去读取客户端请求,在内存中操作对应的数据,然后再写回到Socket中。
单线程的好处:
- 没有了访问共享资源加锁的性能损耗
- 开发和调试非常友好,可维护性高
- 没有多个线程上下文切换带来的额外开销,不是没有,是减少了
单线程不是没有缺点,其实缺点也是很明显的,如果前一个请求发生耗时比较久的操作,那么整个Redis就会阻塞住,其他请求也无法进来,直到这个耗时久的操作处理完成并返回,其他请求才能被处理到,但是redis使用的Reactor 单线程模式来实现的可以缓解这种情况。
“
在Redis 4.0之后的版本,引入多线程,而这个多线程是只的异步释放内存,它主要是为了解决在释放大内存数据导致整个redis阻塞的性能问题,单机redis如果处理大数据请求时还是会出现瓶颈,但是redis有集群高可用解决方案可以解决,主节点只负责写,从节点负责读,io复用先写到这里,集群高可用我会另外在出一篇文章。
写时拷贝
有了高效的数据结构和io多路模型,目前能解决数据访问效率问题,但是redis为了保证了数据不丢失有快照机制,说到快照那么会操作磁盘,redis怎么解决的在数据操作的时候并且还能保证数据记录完整性的?不影响数据访问效率的呢?
答案是用了写时复制技术,什么是写时复制?如果你是一个科班的或者你的操作系统学的不错的话,这个问题很清楚。
在操作系统设计中进程的内存可分为 虚拟内存 和 物理内存,什么是虚拟内存?你可以去看我上一篇文章Virtual Memory。redis会从主进程中通过fork()系统调用,创建一个子进程,将父进程的 虚拟内存 与 物理内存 映射关系复制到子进程中,并将设置内存共享的,子进程只负责将内存里面数据写入到rdb进行持久化操作,如果在操作的时候主进程对内存修改了,使用写时拷贝技术,将对应的内存创建一个副本然后进行写入持久化。
示意图
如上图主进程则提供服务,只有当有人修改当前内存数据时,才去复制被修改的内存页,用于生成快照。
管道通讯
除了本地服务器内存和数据结构的操作影响客户端读写效率的还有网络原因。redis的通讯协议是用一种文件协议,有兴趣自己去研究研究吧,我这里不打算写。每次客户端操作的时候,命令和元数据都被打包成redis协议进行传输到服务器上。
按照这样那每个命令的执行时间:客户端发送时间 + 服务器处理和返回时间 + 一个网络来回的时间。
数据包来回
从上图可以看出来如果每操作一条命令,那么就要执行一次网络io,如果客户端频繁操作数据那么就频繁网络操作,这个过程也是非常耗时的,影响性能的。redis在客户端程序中做了一些优化引入了一个管道(pipelining)概念。
管道会把多条无关命令批量执行,以减少多个命令分别执行带来的网络交互时间,在一些批量操作数据的场景。
小结
简单始于复杂!,别看客户端就几个简单api call的事情,这后面还有很多设计值得去学习,看完这篇八股文你或许对redis高性能有新的认识了,不要小看某些细节优化和解决方案选型,有时候可以带来明显性能提升。当然这篇文章没有把redis设计写完,例如还有aof的内核文件描述符映射,异步写数据到硬盘上,零拷贝技术等等。。。。后续文章将会更新redis高可用是怎么做到的?
原文链接:https://mp.weixin.qq.com/s/S9K7BlVjgbkUQ4jzKqyZHQ
相关推荐
- Spring Boot3 连接 Redis 竟有这么多实用方式
-
各位互联网大厂的后端开发精英们,在日常开发中,想必大家都面临过系统性能优化的挑战。当系统数据量逐渐增大、并发请求不断增多时,如何提升系统的响应速度和稳定性,成为了我们必须攻克的难题。而Redis,这...
- 隧道 ssh -L 命令总结 和 windows端口转发配置
-
摘要:隧道ssh-L命令总结和windows端口转发配置关键词:隧道、ssh-L、端口转发、网络映射整体说明最近在项目中,因为内网的安全密级比较高,只能有一台机器连接内网数据库,推送...
- 火爆BOOS直聘的13个大厂Java社招面经(5年经验)助你狂拿offer
-
火爆BOOS直聘的13个大厂Java社招面经(5年经验)助你狂拿offer综上所述,面试遇到的所有问题,整理成了一份文档,希望大家能够喜欢!!Java面试题分享(Java中高级核心知识全面解析)一、J...
- 「第五期」游服务器一二三面 秋招 米哈游
-
一面下午2点,35分钟golang内存模型golang并发模型golanggc原理过程channel用途,原理redis数据结构,底层实现跳跃表查询插入复杂度进程,线程,协程kill原理除了kil...
- RMQ——支持合并和优先级的消息队列
-
业务背景在一个项目中需要实现一个功能,商品价格发生变化时将商品价格打印在商品主图上面,那么需要在价格发生变动的时候触发合成一张带价格的图片,每一次触发合图时计算价格都是获取当前最新的价格。上游价格变化...
- Redis 中的 zset 为什么要用跳跃表,而不是B+ Tree 呢?
-
Redis中的有序集合使用的是一种叫做跳跃表(SkipList)的数据结构来实现,而不是使用B+Tree。本文将介绍为什么Redis中使用跳跃表来实现有序集合,而不是B+Tree,并且探讨跳跃表...
- 一文让你彻底搞懂 WebSocket 的原理
-
作者:木木匠转发链接:https://juejin.im/post/5c693a4f51882561fb1db0ff一、概述上一篇文章《图文深入http三次握手核心问题【思维导图】》我们分析了简单的一...
- Redis与Java整合的最佳实践
-
Redis与Java整合的最佳实践在这个数字化时代,数据处理速度决定了企业的竞争力。Redis作为一款高性能的内存数据库,以其卓越的速度和丰富的数据结构,成为Java开发者的重要伙伴。本文将带你深入了...
- Docker与Redis:轻松部署和管理你的Redis实例
-
在高速发展的云计算时代,应用程序的部署和管理变得越来越复杂。面对各种操作系统、依赖库和环境差异,开发者常常陷入“在我机器上能跑”的泥潭。然而,容器化技术的兴起,尤其是Docker的普及,彻底改变了这一...
- Java开发中的缓存策略:让程序飞得更快
-
Java开发中的缓存策略:让程序飞得更快缓存是什么?首先,让我们来聊聊什么是缓存。简单来说,缓存是一种存储机制,它将数据保存在更快速的存储介质中,以便后续使用时能够更快地访问。比如,当你打开一个网页时...
- 国庆临近,字节后端开发3+4面,终于拿到秋招第一个offer
-
字节跳动,先面了data部门,3面技术面之后hr说需要实习转正,拒绝,之后另一个部门捞起,四面技术面,已oc分享面经,希望对大家有所帮助,秋招顺利在文末分享了我为金九银十准备的备战资源库,包含了源码笔...
- “快”就一个字!Redis凭什么能让你的APP快到飞起?
-
咱们今天就来聊一个字——“快”!在这个信息爆炸、耐心越来越稀缺的时代,谁不希望自己手机里的APP点一下“嗖”就打开,刷一下“唰”就更新?谁要是敢让咱用户盯着个小圈圈干等,那简直就是在“劝退”!而说到让...
- 双十一秒杀,为何总能抢到?Redis功不可没!
-
一年一度的双十一“剁手节”,那场面,简直比春运抢票还刺激!零点的钟声一敲响,亿万个手指头在屏幕上疯狂戳戳戳,眼睛瞪得像铜铃,就为了抢到那个心心念念的半价商品、限量版宝贝。你有没有发现一个奇怪的现象?明...
- 后端开发必看!为什么说Redis是天然的幂等性?
-
你在做后端开发的时候,有没有遇到过这样的困扰:高并发场景下,同一个操作重复执行多次,导致数据混乱、业务逻辑出错?别担心,很多同行都踩过这个坑。某电商平台就曾因订单创建接口在高并发时不具备幂等性,用户多...
- 开发一个app需要哪些技术和工具
-
APP开发需要一系列技术和工具的支持,以下是对这些技术的清晰归纳和分点表示:一、前端开发技术HTML用于构建页面结构。CSS用于样式设计和布局。JavaScript用于页面交互和逻辑处理。React...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- oracle位图索引 (63)
- oracle批量插入数据 (62)
- oracle事务隔离级别 (53)
- oracle 空为0 (50)
- oracle主从同步 (55)
- oracle 乐观锁 (51)
- redis 命令 (78)
- php redis (88)
- redis 存储 (66)
- redis 锁 (69)
- 启动 redis (66)
- redis 时间 (56)
- redis 删除 (67)
- redis内存 (57)
- redis并发 (52)
- redis 主从 (69)
- redis 订阅 (51)
- redis 登录 (54)
- redis 面试 (58)
- 阿里 redis (59)
- redis 搭建 (53)
- redis的缓存 (55)
- lua redis (58)
- redis 连接池 (61)
- redis 限流 (51)