Redis的IO多路复用和多线程特性会破坏分布式锁的原子性吗?(下)

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云解析 DNS,旗舰版 1个月
简介: Redis的IO多路复用和多线程特性会破坏分布式锁的原子性吗?

3.4 结果返回阶段:addReply

调用prepareClientToWrite,并在prepareClientToWrite中调用clientInstallWriteHandler,将待写回客户端加入到全局变量server的clients_pending_write列表。


然后,addReply会调用_addReplyToBuffer等函数,将要返回的结果添加到客户端的输出缓冲区。


至此,这就是一条命令如何从读取,经过解析、执行等步骤,最终将结果返给客户端,该过程以及涉及的主要函数:


1.png

若在前面命令处理过程中,都由I/O主线程处理,则命令执行的原子性肯定能得到保证,分布式锁的原子性也相应得到保证。


但若这个处理过程配合上了I/O多路复用机制和多IO线程机制,那这俩机制是在这个过程的什么阶段发挥作用的?会不会影响命令执行的原子性?


所以现在就要明确,这俩机制到底参与了什么流程,才能知道是否对原子性保证有副作用。

4 I/O多路复用会影响对命令原子性吗?

I/O多路复用机制是在readQueryFromClient执行前发挥作用的。在事件驱动框架中调用aeApiPoll函数,获取一批已就绪的socket描述符。然后执行一个循环,针对每个就绪描述符上的读事件,触发执行readQueryFromClient函数。


如此,即使I/O多路复用机制同时获取了多个就绪的socket描述符,但实际处理时,Redis主线程仍是针对每个事件逐一调用回调函数进行处理。且针对写事件,I/O多路复用机制也是针对每个事件逐一处理。


I/O多路复用机制通过aeApiPoll获取一批事件,然后逐一处理:

1.png

这表明,即使使用I/O多路复用,命令的整个处理过程仍可由I/O主线程完成,也就仍保证命令执行的原子性。如下就是I/O多路复用机制和命令处理过程的关系:

1.png

5 多I/O线程会破坏命令原子性吗?

多I/O线程可执行读操作或写操作。对读操作,readQueryFromClient在执行过程中,会调用 postponeClientRead 将待读客户端加入 clients_pending_read 等待列表。


然后,待读客户端会被分配给多I/O线程执行,每个IO线程执行的函数就是 readQueryFromClient,它会读取命令=》调用processInputBuffer解析命令,该过程和Redis 6.0前代码一致。


而Redis 6.0 processInputBuffer新增了个判断条件:若客户端有CLIENT_PENDING_READ标识,则解析完命令后,processInputBuffer只会把客户端标识改为CLIENT_PENDING_COMMAND,就退出命令解析的循环流程。


此时,processInputBuffer只是解析了第一个命令,不会实际调用processCommand执行命令:

image.png

这样,等所有I/O线程都解析完了第一个命令后,I/O主线程中执行的handleClientsWithPendingReadsUsingThreads会再调用processCommandAndResetClient执行命令及调用processInputBuffer解析剩余命令。


所以,即使使用多I/O线程,其实命令执行阶段也是由主I/O线程完成,所有命令执行的原子性仍得到保证,即不会破坏分布式锁的原子性。

写回数据流程

该阶段,addReply是将客户端写回操作推迟执行的,而此时Redis命令已完成执行,所以,即使有多个I/O线程在同时将客户端数据写回,也只是把结果返给客户端,并不影响命令在Redis Server的执行结果。因此,即使用了多I/O线程写回,Redis同样不会破坏命令执行的原子性。


使用多I/O线程机制后,命令处理过程各个阶段是由什么线程执行:

1.png

6 总结

加锁和解锁操作分别可以使用SET命令和Lua脚本与EVAL命令来完成。那么,分布式锁的原子性保证,就主要依赖SET和EVAL命令在Redis server中执行时的原子性保证了。


Redis中命令处理的整个过程在Redis 6.0版本前都是由主IO线程来执行完成的。虽然Redis使用了IO多路复用机制,但是该机制只是一次性获取多个就绪的socket描述符,对应了多个发送命令请求的客户端。而Redis在主IO线程中,还是逐一来处理每个客户端上的命令的,所以命令执行的原子性依然可以得到保证。


使用Redis 6.0版本后,命令处理过程中的读取、解析和结果写回,就由多IO线程处理。不过多IO线程只是完成解析第一个读到的命令,命令实际执行还是由主IO线程处理。当多IO线程在并发写回结果时,命令就已执行完,不存在多IO线程冲突问题。所以,使用了多IO线程后,命令执行原子性仍可得到保证。


多IO线程实际并不会加快命令的执行,只会将读取解析命令并行化执行,写回结果并行化执行,且读取解析命令还是针对收到的第一条命令。这一设计考虑还是由于网络IO需加速处理。如命令执行本身成为Redis运行时瓶颈,其实可考虑使用Redis切片集群提升处理效率。


相关实践学习
基于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
目录
相关文章
|
2月前
|
NoSQL Java Redis
太惨痛: Redis 分布式锁 5个大坑,又大又深, 如何才能 避开 ?
Redis分布式锁在高并发场景下是重要的技术手段,但其实现过程中常遇到五大深坑:**原子性问题**、**连接耗尽问题**、**锁过期问题**、**锁失效问题**以及**锁分段问题**。这些问题不仅影响系统的稳定性和性能,还可能导致数据不一致。尼恩在实际项目中总结了这些坑,并提供了详细的解决方案,包括使用Lua脚本保证原子性、设置合理的锁过期时间和使用看门狗机制、以及通过锁分段提升性能。这些经验和技巧对面试和实际开发都有很大帮助,值得深入学习和实践。
太惨痛: Redis 分布式锁 5个大坑,又大又深, 如何才能 避开 ?
|
2月前
|
监控 NoSQL 安全
如何在 Redis 中正确使用多线程?
【10月更文挑战第16天】正确使用 Redis 多线程需要综合考虑多个因素,并且需要在实践中不断摸索和总结经验。通过合理的配置和运用,多线程可以为 Redis 带来性能上的提升,同时也要注意避免可能出现的问题,以保障系统的稳定和可靠运行。
61 2
|
2月前
|
存储 NoSQL Redis
Redis 新版本引入多线程的利弊分析
【10月更文挑战第16天】Redis 新版本引入多线程是一个具有挑战性和机遇的改变。虽然多线程带来了一些潜在的问题和挑战,但也为 Redis 提供了进一步提升性能和扩展能力的可能性。在实际应用中,我们需要根据具体的需求和场景,综合评估多线程的利弊,谨慎地选择和使用 Redis 的新版本。同时,Redis 开发者也需要不断努力,优化和完善多线程机制,以提供更加稳定、高效和可靠的 Redis 服务。
70 1
|
7天前
|
NoSQL Redis
单线程传奇Redis,为何引入多线程?
Redis 4.0 引入多线程支持,主要用于后台对象删除、处理阻塞命令和网络 I/O 等操作,以提高并发性和性能。尽管如此,Redis 仍保留单线程执行模型处理客户端请求,确保高效性和简单性。多线程仅用于优化后台任务,如异步删除过期对象和分担读写操作,从而提升整体性能。
24 1
|
26天前
|
存储 NoSQL Java
使用lock4j-redis-template-spring-boot-starter实现redis分布式锁
通过使用 `lock4j-redis-template-spring-boot-starter`,我们可以轻松实现 Redis 分布式锁,从而解决分布式系统中多个实例并发访问共享资源的问题。合理配置和使用分布式锁,可以有效提高系统的稳定性和数据的一致性。希望本文对你在实际项目中使用 Redis 分布式锁有所帮助。
84 5
|
1月前
|
NoSQL Java 数据处理
基于Redis海量数据场景分布式ID架构实践
【11月更文挑战第30天】在现代分布式系统中,生成全局唯一的ID是一个常见且重要的需求。在微服务架构中,各个服务可能需要生成唯一标识符,如用户ID、订单ID等。传统的自增ID已经无法满足在集群环境下保持唯一性的要求,而分布式ID解决方案能够确保即使在多个实例间也能生成全局唯一的标识符。本文将深入探讨如何利用Redis实现分布式ID生成,并通过Java语言展示多个示例,同时分析每个实践方案的优缺点。
63 8
|
1月前
|
NoSQL Redis
Redis分布式锁如何实现 ?
Redis分布式锁通过SETNX指令实现,确保仅在键不存在时设置值。此机制用于控制多个线程对共享资源的访问,避免并发冲突。然而,实际应用中需解决死锁、锁超时、归一化、可重入及阻塞等问题,以确保系统的稳定性和可靠性。解决方案包括设置锁超时、引入Watch Dog机制、使用ThreadLocal绑定加解锁操作、实现计数器支持可重入锁以及采用自旋锁思想处理阻塞请求。
59 16
|
1月前
|
缓存 NoSQL PHP
Redis作为PHP缓存解决方案的优势、实现方式及注意事项。Redis凭借其高性能、丰富的数据结构、数据持久化和分布式支持等特点,在提升应用响应速度和处理能力方面表现突出
本文深入探讨了Redis作为PHP缓存解决方案的优势、实现方式及注意事项。Redis凭借其高性能、丰富的数据结构、数据持久化和分布式支持等特点,在提升应用响应速度和处理能力方面表现突出。文章还介绍了Redis在页面缓存、数据缓存和会话缓存等应用场景中的使用,并强调了缓存数据一致性、过期时间设置、容量控制和安全问题的重要性。
44 5
|
1月前
|
存储 缓存 监控
利用 Redis 缓存特性避免缓存穿透的策略与方法
【10月更文挑战第23天】通过以上对利用 Redis 缓存特性避免缓存穿透的详细阐述,我们对这一策略有了更深入的理解。在实际应用中,我们需要根据具体情况灵活运用这些方法,并结合其他技术手段,共同保障系统的稳定和高效运行。同时,要不断关注 Redis 缓存特性的发展和变化,及时调整策略,以应对不断出现的新挑战。
72 10
|
2月前
|
缓存 NoSQL Java
大数据-50 Redis 分布式锁 乐观锁 Watch SETNX Lua Redisson分布式锁 Java实现分布式锁
大数据-50 Redis 分布式锁 乐观锁 Watch SETNX Lua Redisson分布式锁 Java实现分布式锁
75 3
大数据-50 Redis 分布式锁 乐观锁 Watch SETNX Lua Redisson分布式锁 Java实现分布式锁