当前位置: 首页 > news >正文

Redis是单线程的,为啥那么快呢?经典问题

目录

1. Redis 6.0 之前 —— 纯单线程模型的极致优化

1.1 单线程负责内容:

1.2 单线程外异步执行的情况

1.3 Redis高性能的四大核心原因

2. Redis 6.0+ —— 多线程I/O的进一步优化

2.1  Redis 6.0引入了多线程网络I/O来解决

2.2  为什么这么设计?

2.3  配置方式:

3. 总结:Redis快的本质(表格速记)

3.1 作为Java工程师的启示


下面和大家一起深入剖析Redis为什么单线程还这么快。我们需要区分版本来看待,Redis 6.0是一个重要的分水岭。

1. Redis 6.0 之前 —— 纯单线程模型的极致优化

重要:Redis的“单线程”指的是处理客户端请求的核心流程(网络I/O + 命令执行)是单线程的(不包括后台持久化、删除等线程)!!!

1.1 单线程负责内容:
  • 接收客户端连接(accept)

  • 读取网络数据(read)

  • 解析命令

  • 执行命令(核心逻辑)

  • 返回结果(write)

1.2 单线程外异步执行的情况
  • 持久化(fork子进程做RDB)

  • 异步删除(unlinkflushall async

  • AOF刷盘线程

1.3 Redis高性能的四大核心原因

1. 纯内存操作,速度极快

  • 数据完全存放在内存中,所有的操作都是对内存的读写。内存的随机访问速度(纳秒级)远远快于磁盘(毫秒级),这是Redis达到超高吞吐量的物质基础

2. 高效的数据结构

  • Redis内置了多种精心优化的数据结构,如SDS(简单动态字符串)、跳跃表、压缩列表等。这些数据结构的设计在时间和空间上都有很高的效率,使得数据操作的本身开销非常小。

    Redis为每种数据类型选择了最优的底层数据结构,保证操作尽可能快:

    Redis数据类型

    底层数据结构

    时间复杂度

    String

    SDS(动态字符串)

    O(1)

    List

    压缩列表(ziplist) / 快速列表(quicklist)

    O(1) 头尾操作

    Hash

    压缩列表 / 哈希表(hashtable)

    O(1)

    Set

    整数集合(intset) / 哈希表

    O(1)

    Sorted Set

    跳跃表(skiplist) + 哈希表

    O(log N)

    这些结构在设计上做了大量优化,比如:

  • SDS

    预分配内存,避免频繁扩容

  • ziplist

    紧凑存储,节省内存

  • skiplist

    平衡查找与插入性能

3. 单线程模型的巨大优势(核心)

  • 避免锁竞争和上下文切换:这是最关键的一点。多线程虽然能利用多核,但会引入激烈的锁竞争来保证数据一致性。频繁的加锁、释放锁,以及线程上下文切换会消耗大量CPU时间。Redis的单线程模型完全避免了这些问题,没有了锁的开销,CPU不用在多个线程间来回切换,可以将所有的算力都用于处理命令,在核心频率更高的CPU上表现更好。

  • 天然的原子性:所有命令都是按顺序串行执行的,无需额外保证,绝不会出现并发问题,简化了系统设计。

多线程问题

Redis单线程如何避免

上下文切换

无切换,CPU专注执行

锁竞争

无需加锁(如 INCR 原子操作)

死锁、竞态条件

完全不存在

线程创建/销毁开销

✅ 单线程让Redis实现简单、可维护性强,且性能可预测。

4. I/O 多路复用技术 (I/O Multiplexing)

  • 这是单线程能处理高并发连接的神器。单线程并不意味着会阻塞。

  • 原理:通过系统调用(如Linux的epoll),一个线程可以同时监听成千上万个客户端Socket。

  • 工作流程:

    1. 线程阻塞在epoll_wait调用上,等待任何一个Socket变得可读/可写。

    2. 当有事件发生时(例如客户端发送了一个命令),epoll_wait返回,线程开始处理这些就绪的Socket。

    3. 它快速地从Socket中读取命令、执行、然后将结果写入输出缓冲区。

  • 这样,单个线程就像是一个“高效的调度员”,只在有实际工作要做的时候才去工作,而不是盲目地轮询或为每个连接创建一个线程,极大地提升了CPU的利用效率。

重要:什么是I/O多路复用?

  • 允许一个线程同时监听多个Socket连接

  • 使用系统调用如 epoll(Linux)、kqueue(BSD)、select

  • 当某个Socket有数据可读/可写时,内核通知Redis。

工作流程(Reactor模式):

客户端1 ----\\
客户端2 ----- Redis主线程(I/O多路复用 + 事件循环)/
客户端3 ----/
  1. 主线程通过 epoll 监听所有客户端Socket

  2. 当某个Socket有数据到达,epoll 返回就绪事件

  3. 主线程依次处理这些事件(读取、解析、执行、写回)

  4. 所有操作在同一个线程内串行执行

✅ 这种方式避免了为每个连接创建线程,极大降低了资源消耗。

2. Redis 6.0+ —— 多线程I/O的进一步优化

虽然epoll是高效的,但从Socket读取数据(读)和将数据写回Socket(写)这个过程本身仍然是同步的,并且需要占用CPU时间。随着网络硬件性能提升(万兆网卡普及),以及应用对性能的极致追求,网络I/O有时会成为单线程模型的瓶颈,特别是在需要高吞吐量的场景下。

2.1  Redis 6.0引入了多线程网络I/O来解决

设计非常巧妙和谨慎:

  • “多线程”用在何处? 仅用于网络数据的读写和协议解析,而最核心的命令执行(操作内存数据)模块,仍然是单线程的

  • 工作流程:

    1. 主线程通过I/O多路复用接收连接,并将就绪的Socket放入一个队列。

    2. Read 阶段并行读取与解析一组I/O线程(可配置数量)并行地从这些Socket中读取数据,并将原始数据解析成Redis命令。注意:它们只解析命令,绝不执行命令。

    3. Execute 阶段:主线程单线程执行 “命令”)解析好的命令被送入一个队列(主线程的全局请求队列”),等待主线程串行地获取并执行。

    4. 命令执行完成后,需要返回的结果会被放入另一个队列。

    5. Write 阶段:IO 线程并行处理 “写响应”)I/O线程再次并行地从队列中取出结果,并将其写回(回写)到对应的Socket中,发送给客户端。

    6. 最后:同步通知与资源清理:IO 线程完成写操作后,通过信号量通知主线程;主线程确认后,释放该 Socket 的临时资源(如缓冲区),等待下一次请求。

2.2  为什么这么设计?
  • 目的:将最耗时的网络I/O操作并行化(尤其是大value传输),以释放主线程的压力。主线程可以更专注于、更快速地去执行命令。现代服务器CPU核数多,单线程无法充分利用多核。

  • 守护核心优势命令执行依然是单线程,这意味着Redis依然保留了无锁、原子性、无需上下文切换的所有优点。它只是给核心的单线程引擎加上了多涡轮增压(I/O线程)来处理进气和排气,引擎本身没变。

2.3  配置方式:
# redis.conf
io-threads-do-reads yes
io-threads 4  # 建议4~8核CPU用4线程

默认不启用多线程:Redis 6.0 中,多线程默认关闭,需通过配置 io-threads-do-reads yes 启用(io-threads 配置 IO 线程数量,默认 4)。

3. 总结:Redis快的本质(表格速记)

因素

说明

内存存储

数据在RAM,速度极快

高效数据结构

每种类型用最优结构实现

单线程模型

避免锁、切换、竞争开销

I/O多路复用

单线程处理高并发连接

纯C语言实现

接近底层,性能高

非阻塞I/O

不阻塞主线程,及时响应

💡 一句话总结: Redis的“单线程”不是性能瓶颈,而是通过精巧设计,让单线程也能发挥极致性能。它把复杂性留给了自己(数据结构、I/O模型),把高性能留给了用户。

3.1 作为Java工程师的启示
  1. 不是所有系统都必须用多线程才能高性能
  2. I/O多路复用

     是高并发网络编程的核心(Netty、NIO都基于此)

  3. 数据结构选择

     对性能影响巨大

  4. 避免过度设计

    简单、可控的单线程有时比复杂的多线程更高效


文章转载自:

http://uPR8bhlm.rfpxq.cn
http://tbHkGcyH.rfpxq.cn
http://FsTwv0gB.rfpxq.cn
http://XNw5ZBrR.rfpxq.cn
http://eDQjBIC3.rfpxq.cn
http://S6g4ot1E.rfpxq.cn
http://a1ghSIhO.rfpxq.cn
http://mPIFMk3u.rfpxq.cn
http://GcuMR8RK.rfpxq.cn
http://xs9u63A0.rfpxq.cn
http://AJM2vAVo.rfpxq.cn
http://cEMFbH3M.rfpxq.cn
http://09ukWzsw.rfpxq.cn
http://usoeVVS2.rfpxq.cn
http://W0E0YaQm.rfpxq.cn
http://3au4fGee.rfpxq.cn
http://U8mWBJaT.rfpxq.cn
http://9D8VFGSQ.rfpxq.cn
http://YMvWSnVt.rfpxq.cn
http://U8XV2oaO.rfpxq.cn
http://yohaaKDW.rfpxq.cn
http://roNHg1mt.rfpxq.cn
http://N6pRAbEJ.rfpxq.cn
http://CNtWpFB6.rfpxq.cn
http://gOaqIBxQ.rfpxq.cn
http://5y3ZJVcH.rfpxq.cn
http://RyHEV5uB.rfpxq.cn
http://Wp7qOUpK.rfpxq.cn
http://YYNY0VnA.rfpxq.cn
http://AK0IN9j7.rfpxq.cn
http://www.dtcms.com/a/367797.html

相关文章:

  • 【Python】数据可视化之核密度
  • 从传统CNN到残差网络:用PyTorch实现更强大的图像分类模型
  • 【DINOv3教程2-热力图】使用DINOv3直接生成图像热力图【附源码与详解】
  • 追觅极境冰箱震撼上市:以首创超低氧保鲜科技打造家庭健康中心
  • n8n中文版部署步骤说明
  • Leetcode 876. 链表的中间结点 快慢指针
  • JavaSe之多线程
  • java程序员的爬虫技术
  • CPU设计范式(Design Paradigms)有哪些?
  • MVCC是如何工作的?
  • springboot在线投票系统(代码+数据库+LW)
  • 如何设计用户在线时长统计系统?
  • timm==0.5.4 cuda=11.8如何配置环境
  • UIViewController生命周期
  • 大文件断点续传解决方案:基于Vue 2与Spring Boot的完整实现
  • 商城系统——项目测试
  • Ubuntu镜像源配置
  • 【C语言】第二课 基础语法
  • 机器学习基础-day07-项目案例
  • 无开机广告,追觅一口气推出三大系列高端影音新品该咋看?
  • Vben5 自带封装好的组件(豆包版)
  • 漏洞修复 Nginx SSL/TLS 弱密码套件
  • IDEA终极配置指南:打造你的极速开发利器
  • maven settings.xml文件的各个模块、含义以及它们之间的联系
  • 一文详解大模型强化学习(RLHF)算法:PPO、DPO、GRPO、ORPO、KTO、GSPO
  • websocket的key和accept分别是多少个字节
  • lc链表问答
  • [iOS] 折叠 cell
  • Qt 系统相关 - 1
  • JavaScript 实战进阶续篇:从工程化到落地的深度实践