redis线程模型

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

一、redis单线程模型

首先需要注意的是,redis整体而言并不是单线程。

redis-server是主线程,所说的redis是单线程主要指redis-server这个线程,用于处理命令。

d45c37de5fab82708b75c7226acb9edb_999c249d49d346f2a7e08fd61410b415.png

所谓的redis单线程,指的是命令处理、逻辑处理在一个单线程中。即【接收客户端请求–>解析请求 -->进行数据读写等操作–>发送数据给客户端】这个过程是由一个线程(主线程)来完成的。


redis 6.0 版本之前的单线模式

6c875299c0ad3bf95d5f1ef19d14a2d2_9b9ddb6a40a848d995031804ecc5795d.png

图中的蓝色部分是一个事件循环,是由主线程负责的,可以看到网络 I/O 和命令处理都是单线程。


1.1 为什么redis命令处理是单线程,而不采用多线程

  1. redis支持多种数据结构(如string、list、hash、set、zset等),每个对象类型都是由多个数据结构实现的。因此多线程环境下,加锁复杂、锁粒度不好控制。
  2. 频繁的上下文切换,会带来更多的时间和性能上的开销,从而抵消多线程的优势。redis作为数据库,并不是每时每刻都有密集访问。在多线程环境下,访问少时需要将一些线程休眠,访问多时又需要唤醒,这就存在频繁的线程调度问题。


1.2 单线程的局限及redis的优化方式

单线程最大的局限,在于不能有耗时操作,即阻塞IO、CPU运算时间比较长的任务等。这会影响redis的响应性能。


redis的耗时操作以及其解决优化方式:

1)IO密集型 —— 磁盘IO : redis提供了类似于日志备份的 aof(Append-Only File)方式以支持持久化,也就是对数据的更改操作需要刷新落到磁盘里。针对这个耗时操作,redis有两种优化方法:1、rdb(Redis Database)文件:redis会fork一个子进程,在子进程中进行持久化,不占用主线程的资源。2、aof持久化策略:redis会创建bio_aof_fsync线程进程异步刷盘。


2)IO密集型 —— 网络IO :当redis服务多个客户端时,如果数据请求或返回数据量比较大时,造成了IO密集型的情况,也是比较耗时的操作。对比,redis通过开启IO多线程(io_thd_*线程)来处理网络IO。


3)CPU密集型:redis支持丰富的数据结构,而有些数据结构操作的事件复杂度比较高,就可能会导致CPU花费大量的时间去计算。对比,redis采用分治的方式。


二、redis单线程为什么这么快

2.1 采用的机制

redis采用了以下机制


  1. redis是内存数据库,数据存储在内存中,可以高效地访问。
  2. redis使用hash table的数据组织方式,查询数据的时间复杂度为O ( 1 ) O(1)O(1),能快速查找数据。
  3. redis采用了高效的数据结构,可以根据性能进行数据结构切换,使得执行效率与空间占用保持平衡。
  4. redis使用高效的reactor网络模型。

2.2 优化的措施

  1. redis采用分治的思想,把rehash分摊到之后的每步增删查改的操作当中。同时,在定时器中最大执行1毫秒的rehash,每次步长100个数组槽位。
  2. redis将耗时阻塞的操作,放在其他线程处理。
  3. redis针对不同的对象类型采用不同的数据结构实现。比如string对象针对不同的数据长度,有int、raw、embstr三种编码方式。
127.0.0.1:6379> set name jack
OK
127.0.0.1:6379> OBJECT encoding name
"embstr"
127.0.0.1:6379> set name "1001"
OK
127.0.0.1:6379> OBJECT encoding name
"int"
127.0.0.1:6379> set name 123456789012345678901234567890123456789012345678901234567890
OK
127.0.0.1:6379> OBJECT encoding name
"raw"

三、redis的IO多线程模型

3.1 redis 为什么引入IO多线程模型

在 redis 6.0 版本之后,也采用了多个 I/O 线程来处理网络请求,这是因为随着网络硬件的性能提升,redis 的性能瓶颈有时会出现在网络 I/O 的处理上。所以为了提高网络 I/O 的并行度,Redis 6.0 对于网络 I/O 采用多线程来处理。但是对于命令的执行,Redis 仍然使用单线程来处理。


即多线程处理网络IO(read、decode和encode、send阶段)。主线程使用单线程,执行命令处理业务逻辑(因为 redis 采用高效的数据结构,其业务逻辑处理较快,所以用单线程即可)。

143b4ed5c94b2b051dad90d7dab8369d_e46fa1c54e8942c6af26f219aa24ca0b.png


3.2 配置io-threads-do-reads

io-threads-do-reads是 redis.conf 文件中的一个配置选项,用于控制 I/O 线程是否执行读取操作。


默认情况下 I/O 多线程只针对发送响应数据( encode, send),并不会以多线程的方式处理读请求( read, decode)。要想开启多线程处理客户端读请求,就需要把 Redis.conf 配置文件中的 io-threads-do-reads 配置项设为 yes。


//读请求也使用io多线程
io-threads-do-reads yes 
// io-threads N,表示启用 N-1 个 I/O 多线程(主线程也算一个 I/O 线程)
io-threads 4 

当将 io-threads-do-reads 设置为 “yes” 时,I/O 线程将负责处理客户端请求的读取操作。这意味着 I/O 线程可以直接从套接字中读取数据,并进行相应的处理,而无需等待主线程来分发任务。


使用 io-threads-do-reads 的好处是能够减轻主线程的负担,提高系统的并发性能和响应速度。通过将读取操作分配给专门的 I/O 线程,可以使主线程更专注于处理其他的任务,如写入操作、协议解析和业务逻辑等。


3.3 流程

对于 redis 来说,它采用的是 I/O 多路复用技术而不是真正的多线程模型。其基本流程:


1.redis-server主线程作为生产者:

∙ \bullet∙ 当有新的客户端连接请求到达时,主线程会将对应的客户端套接字加入到clients_pending_read 队列中。这表示该连接上有数据可读,需要被处理。

∙ \bullet∙ 当有客户端数据写入请求到达时,主线程会将对应的客户端套接字加入到 clients_pending_write 队列中。这表示该连接上可以进行写操作。


2.redis-server主线程作为消费者:

∙ \bullet∙ 主线程通过循环遍历 clients_pending_read 队列中的客户端套接字,并将其分配给合适的 I/O 线程处理。主线程会根据负载均衡策略(如轮询或哈希)来决定将客户端套接字分发给哪个 I/O 线程的专属队列。

∙ \bullet∙ 类似地,主线程也会从 clients_pending_write 队列中获取客户端套接字,并将其分配给适当的 I/O 线程处理。


3.I/O 线程执行任务:

∙ \bullet∙ 每个 I/O 线程拥有一个专属队列(如 io_threads_list[id]),主线程将客户端套接字分配给指定的 I/O 线程,并将其加入到对应的队列中。

∙ \bullet∙ I/O 线程通过从自己的队列中获取客户端套接字,进行实际的读写操作和请求处理。一旦完成操作,也可以将结果返回给主线程。

通过这种队列模型和任务调度方式,主线程在兼顾生产者和消费者角色的同时,能够高效地将任务分发给对应的 I/O 线程进行处理,以提高并发性能和系统的吞吐量。同时,这种设计还能避免多线程并发带来的同步问题和竞争条件,保证了系统的稳定性和可靠性。

0625a9540294994d46667b47ca879675_b5663cefa650493bbcc87e06e8c59cc4.png

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
1月前
|
监控 NoSQL 安全
如何在 Redis 中正确使用多线程?
【10月更文挑战第16天】正确使用 Redis 多线程需要综合考虑多个因素,并且需要在实践中不断摸索和总结经验。通过合理的配置和运用,多线程可以为 Redis 带来性能上的提升,同时也要注意避免可能出现的问题,以保障系统的稳定和可靠运行。
46 2
|
1月前
|
存储 NoSQL Redis
Redis 新版本引入多线程的利弊分析
【10月更文挑战第16天】Redis 新版本引入多线程是一个具有挑战性和机遇的改变。虽然多线程带来了一些潜在的问题和挑战,但也为 Redis 提供了进一步提升性能和扩展能力的可能性。在实际应用中,我们需要根据具体的需求和场景,综合评估多线程的利弊,谨慎地选择和使用 Redis 的新版本。同时,Redis 开发者也需要不断努力,优化和完善多线程机制,以提供更加稳定、高效和可靠的 Redis 服务。
41 1
|
28天前
|
并行计算 JavaScript 前端开发
单线程模型
【10月更文挑战第15天】
|
30天前
|
安全 Java
Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧
【10月更文挑战第20天】Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧,包括避免在循环外调用wait()、优先使用notifyAll()、确保线程安全及处理InterruptedException等,帮助读者更好地掌握这些方法的应用。
19 1
|
1月前
|
存储 运维 NoSQL
Redis为什么最开始被设计成单线程而不是多线程
总之,Redis采用单线程设计是基于对系统特性的深刻洞察和权衡的结果。这种设计不仅保持了Redis的高性能,还确保了其代码的简洁性、可维护性以及部署的便捷性,使之成为众多应用场景下的首选数据存储解决方案。
41 1
|
1月前
|
NoSQL Redis 数据库
Redis单线程模型 redis 为什么是单线程?为什么 redis 单线程效率还能那么高,速度还能特别快
本文解释了Redis为什么采用单线程模型,以及为什么Redis单线程模型的效率和速度依然可以非常高,主要原因包括Redis操作主要访问内存、核心操作简单、单线程避免了线程竞争开销,以及使用了IO多路复用机制epoll。
50 0
Redis单线程模型 redis 为什么是单线程?为什么 redis 单线程效率还能那么高,速度还能特别快
|
1月前
|
安全 调度 C#
STA模型、同步上下文和多线程、异步调度
【10月更文挑战第19天】本文介绍了 STA 模型、同步上下文和多线程、异步调度的概念及其优缺点。STA 模型适用于单线程环境,确保资源访问的顺序性;同步上下文和多线程提高了程序的并发性和响应性,但增加了复杂性;异步调度提升了程序的响应性和资源利用率,但也带来了编程复杂性和错误处理的挑战。选择合适的模型需根据具体应用场景和需求进行权衡。
|
1月前
|
消息中间件 NoSQL 关系型数据库
【多线程-从零开始-捌】阻塞队列,消费者生产者模型
【多线程-从零开始-捌】阻塞队列,消费者生产者模型
25 0
|
2月前
|
消息中间件 存储 NoSQL
18)Redis 的发布订阅模型
18)Redis 的发布订阅模型
33 0
|
1月前
|
存储 消息中间件 资源调度
C++ 多线程之初识多线程
这篇文章介绍了C++多线程的基本概念,包括进程和线程的定义、并发的实现方式,以及如何在C++中创建和管理线程,包括使用`std::thread`库、线程的join和detach方法,并通过示例代码展示了如何创建和使用多线程。
48 1
C++ 多线程之初识多线程
下一篇
无影云桌面