redis为什么这么快?

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: redis为什么这么快?

1 redis的数据时存储在内存中


读取的时候属于纯内存操作,不需要进行磁盘的io,时间复杂度O(1)

要实现高的并发性能,redis是不是要创建非常多的线程呢,恰恰相反,redis是单线程的。


redis为什么是单线程的?


官方解释说,因为单线程已经够用了,CPU 不是 redis 的瓶颈。Redis 的瓶颈最有可能是机器内存或者网络带宽。


单线程为什么这么快?


因为redis是基于内存操作的。


2 redis是单线程的


单线程有如下好处:


不需要频繁创建和销毁线程


单线程保证了系统没有线程的上下文切换


避免线程之间的资源竞争,比如加锁释放锁死锁等


3 异步非阻塞IO,多路复用处理并发连接


传统 I/O 数据拷贝


以读操作为例:当应用程序执行 read 系统调用读取文件描述符(FD)的时候,如果这块数据已经存在于用户进程的页内存中,就直接从内存中读取数据。如果数据不存在,则先将数据从磁盘加载数据到内核缓冲区中,再从内核缓冲区拷贝到用户进程的页内存中。(两次拷贝,两次 user 和 kernel 的上下文切换)。


网络异常,图片无法展示
|


I/O 的阻塞到底阻塞在哪里?


当使用 read 或 write 对某个文件描述符进行过读写时,如果当前 FD 不可读,系统就不会对其他的操作做出响应。从设备复制数据到内核缓冲区是阻塞的,从内核缓冲区拷贝到用户空间,也是阻塞的,直到 copy complete,内核返回结果,用户进程才解除block 的状态。


网络异常,图片无法展示
|


为了解决阻塞的问题,我们有几个思路。


  1. 在服务端创建多个线程或者使用线程池,但是在高并发的情况下需要的线程会很多,系统无法承受,而且创建和释放线程都需要消耗资源。


  1. 由请求方定期轮询,在数据准备完毕后再从内核缓存缓冲区复制数据到用户空间(非阻塞式 I/O),这种方式会存在一定的延迟。


能不能用一个线程处理多个客户端请求?


I/O 多路复用(I/O Multiplexing)


  • I/O 指的是网络 I/O。


  • 多路指的是多个 TCP 连接(Socket 或 Channel)。


  • 复用指的是复用一个或多个线程。


它的基本原理就是不再由应用程序自己监视连接,而是由内核替应用程序监视文件描述符。


客户端在操作的时候,会产生具有不同事件类型的 socket。在服务端,I/O 多路复用程序(I/O Multiplexing Module)会把消息放入队列中,然后通过文件事件分派器(File event Dispatcher),转发到不同的事件处理器中。


网络异常,图片无法展示
|


多路复用有很多的实现,以 select 为例,当用户进程调用了多路复用器,进程会被阻塞。内核会监视多路复用器负责的所有 socket,当任何一个 socket 的数据准备好了,多路复用器就会返回。这时候用户进程再调用 read 操作,把数据从内核缓冲区拷贝到用户空间。


网络异常,图片无法展示
|


所以,I/O 多路复用的特点是通过一种机制一个进程能同时等待多个文件描述符,而这些文件描述符(套接字描述符)其中的任意一个进入读就绪(readable)状态,select()函数就可以返回。


4 高效的数据结构,合理的数据编码


在 Redis 中,常用的 5 种数据结构和应用场景如下:


String:缓存、计数器、分布式锁等。


List:链表、队列、微博关注人时间轴列表等。


Hash:用户信息、Hash 表等。


Set:去重、赞、踩、共同好友等。


Zset:访问量排行榜、点击量排行榜等。


具体数据结构如何体现出高效,数据编码又如何体现出合理,此处先留个坑,有待后面进行填上。


5 过期数据的删除对 Redis 性能影响


当我们对某些 key 设置了 expire 时,数据到了时间会自动删除。如果一个键过期了,它会在什么时候删除呢?


下面介绍三种删除策略:


定时删除:在这是键的过期时间的同时,创建一个定时器 Timer,让定时器在键过期时间来临时立即执行对过期键的删除。


惰性删除:键过期后不管,每次读取该键时,判断该键是否过期,如果过期删除该键返回空。


定期删除:每隔一段时间对数据库中的过期键进行一次检查。


定时删除:对内存友好,对 CPU 不友好。如果过期删除的键比较多的时候,删除键这一行为会占用相当一部分 CPU 性能,会对 Redis 的吞吐量造成一定影响。


惰性删除:对 CPU 友好,内存不友好。如果很多键过期了,但在将来很长一段时间内没有很多客户端访问该键导致过期键不会被删除,占用大量内存空间。


定期删除:是定时删除和惰性删除的一种折中。每隔一段时间执行一次删除过期键的操作,并且限制删除操作执行的时长和频率。


具体的操作如下:


Redis 会将每一个设置了 expire 的键存储在一个独立的字典中,以后会定时遍历这个字典来删除过期的 key。除了定时遍历外,它还会使用惰性删除策略来删除过期的 key。

Redis 默认每秒进行十次过期扫描,过期扫描不会扫描所有过期字典中的 key,而是采用了一种简单的贪心策略。


从过期字典中随机选择 20 个 key;删除这 20 个 key 中已过期的 key;如果过期 key 比例超过 1/4,那就重复步骤 1。


同时,为了保证在过期扫描期间不会出现过度循环,导致线程卡死,算法还增加了扫描时间上限,默认不会超过 25ms。

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
相关文章
|
关系型数据库 MySQL 数据安全/隐私保护
关于Navicat Premium连接MySQL出现2059错误解决方法
关于Navicat Premium连接MySQL出现2059错误解决方法
|
10月前
|
JavaScript 前端开发 API
Vue 3 中 v-model 与 Vue 2 中 v-model 的区别是什么?
总的来说,Vue 3 中的 `v-model` 在灵活性、与组合式 API 的结合、对自定义组件的支持等方面都有了明显的提升和改进,使其更适应现代前端开发的需求和趋势。但需要注意的是,在迁移过程中可能需要对一些代码进行调整和适配。
428 60
|
9月前
|
监控 测试技术 定位技术
HTTP代理IP响应速度测试方案设计与指标体系
随着数字化发展,网络安全、隐私保护及内容访问自由成为核心需求。HTTP代理因其技术优势成为热门选择。本文介绍HTTP代理IP响应速度测试方案,包括基础性能、稳定性、地理位置、实际应用、安全性测试及监控指标,推荐测试工具,并提供测试结果评估标准。
173 2
|
11月前
|
监控 应用服务中间件 nginx
详细解释容器以及虚拟机centos7.9容器化部署基础服务(容器化部署nginx)
容器是一种轻量级、可移植的软件打包和隔离技术,将应用程序及其依赖项打包,确保在任何环境中一致运行。容器共享主机操作系统内核,相比虚拟机更高效、轻量,具有快速启动和高资源利用率的特点。容器的关键技术包括命名空间(如 PID、NET 等)、控制组(cgroups)和联合文件系统(UnionFS)。使用容器可以提高开发和部署效率,简化管理,确保环境一致性。例如,在 CentOS 7.9 上部署 Nginx 时,可以通过 Docker 下载和运行 `nginx:1.20` 镜像,并通过端口映射使外部请求访问 Nginx 服务。此外,还可以将测试页面复制到容器中,进一步验证容器的功能。
286 0
要将ModelScope的应用检测模型转换为ONNX格式或RKNN格式
要将ModelScope的应用检测模型转换为ONNX格式或RKNN格式
729 1
|
XML 数据格式
IDEA 行注释设置,使其不从顶格开始,让其处于代码前开始
这篇文章提供了IntelliJ IDEA中如何设置行注释不从顶格开始,而是紧接在代码前面的方法,通过访问Settings中的Code Style选项进行调整,以改善代码注释的视觉效果。
|
XML 数据格式
节点类型(Node Types)
该教程介绍了XML文档的根节点通过documentElement属性获取,节点信息包括nodeName(节点名)和nodeType(节点类型)。下一章将深入探讨节点属性。示例代码展示了如何遍历根节点的子节点,仅输出元素节点的名称,通过检查nodeType为1来判断是否为元素节点。
|
运维 监控 Java
微服务心跳监测机制讲解与实现,与面试过程中如何回答这个问题
微服务心跳监测机制讲解与实现,与面试过程中如何回答这个问题
292 0
|
Python
Python内置函数map、split、join讲解
Python内置函数map、split、join讲解
759 0
|
SQL 前端开发 索引
sql 性能优化基于explain调优
sql 性能优化基于explain调优
106 0

热门文章

最新文章