如何利用Redis Lua脚本实现分布式信号量控制?

轻伟姑娘_5300

轻伟姑娘_5300

2026-07-14

401人浏览

原创

不能直接用 incr + expire 实现信号量,因为二者非原子执行,中间可能中断导致 key 有值无过期或设了过期却未自增,引发超卖;必须用 lua 脚本将检查、增减、设过期封装为原子操作。

如何利用redis lua脚本实现分布式信号量控制?

为什么不能直接用 INCR + EXPIRE 实现信号量?

因为这两条命令不是原子的,中间可能被中断或超时,导致 key 有值但没过期时间,或者设置了过期时间但没成功自增。后续请求会误判资源可用,造成超卖或并发越界。Lua 脚本在 Redis 中是原子执行的,能真正保证“检查+加锁+设过期”三步不被拆开。

EVAL 脚本里怎么安全地实现 acquire 和 release?

acquire 脚本要判断当前计数是否小于最大许可数,且只在满足条件时 INCR 并设置过期时间;release 则需先检查 key 是否存在、是否为数字类型,再 DECR,并可选地在归零后自动删 key。注意:不能用 GET 后再判断,必须用 redis.call('GET', KEYS[1]) 配合类型检查,否则空值或字符串值会导致 INCR 报错 ERR value is not an integer or out of range。

常见写法示例(acquire):

if redis.call('EXISTS', KEYS[1]) == 0 then
  redis.call('SET', KEYS[1], 0)
  redis.call('EXPIRE', KEYS[1], ARGV[2])
end
local current = tonumber(redis.call('GET', KEYS[1]))
if current and current 

<h3>用 <code>EVALSHA</code> 替代 <code>EVAL</code> 有什么坑?</h3>
<p>脚本 SHA1 值需要提前 <code>SCRIPT LOAD</code>,否则 <code>EVALSHA</code> 会返回 <code>NOSCRIPT</code> 错误。更关键的是:集群模式下,所有 <code>KEYS</code> 必须落在同一个 slot,否则 Redis Cluster 会拒绝执行——这意味着信号量 key 必须带固定 hash tag,比如 <code>semaphore:{order}</code>,否则 <code>EVALSHA</code> 在集群中直接失败。</p><div class="aritcle_card flexRow artxards">
											<div class="artcardd flexRow">
												<a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill3464" title="Redis Skill - 高性能缓存管理"><img
														src="https://img.php.cn/upload/skill/000/000/081/178960683454849.jpg" alt="Redis Skill - 高性能缓存管理" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
												<div class="aritcle_card_info flexColumn">
													<a rel="nofollow" href="/xiazai/skill3464" title="Redis Skill - 高性能缓存管理" class="overflowclass">Redis Skill - 高性能缓存管理</a>
													<p class="overflowclass">Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。</p>
												</div>
												<a rel="nofollow" href="/xiazai/skill3464" title="Redis Skill - 高性能缓存管理" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
												</a>
											</div>
										</div>
<p>容易忽略的点:</p>
  • SCRIPT LOAD 返回的 SHA 是字符串,调用 EVALSHA 时必须原样传入,大小写敏感
  • 脚本中所有 key 必须通过 KEYS 数组传入,硬编码 key 名会导致集群路由失败
  • 释放操作如果用 DEL 粗暴删除,可能误删别人刚 acquire 的计数,应只 DECR 不删

客户端如何处理 acquire 失败和连接异常?

Redis 执行 Lua 后返回整数:1 表示成功获取,0 表示拒绝。但网络中断、timeout、LOADING 状态等不会返回 0,而是抛出异常。这时候不能简单重试,否则可能重复 acquire——必须配合唯一 client token(如 UUID)写入 value,或用 HASH 结构记录持有者,否则无法区分“到底有没有拿到锁”。

更现实的做法是:把信号量 key 的 value 设为 current_count:timestamp:client_id 这类复合结构,便于 debug 和主动清理过期持有者。不过这会让脚本变复杂,权衡点在于你是否真需要强持有追溯能力。

真正难处理的是:acquire 成功但客户端在后续业务逻辑中崩溃,又没来得及 release。这时只能靠 EXPIRE 时间兜底,所以 ARGV[2](TTL)不能设太长,也不能太短——要略大于最长业务耗时。

相关专题

更多
什么是分布式
什么是分布式

分布式是一种计算和数据处理的方式,将计算任务或数据分散到多个计算机或节点中进行处理。本专题为大家提供分布式相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.11

1933

5

分布式和微服务的区别
分布式和微服务的区别

分布式和微服务的区别在定义和概念、设计思想、粒度和复杂性、服务边界和自治性、技术栈和部署方式等。本专题为大家提供分布式和微服务相关的文章、下载、课程内容,供大家免费下载体验。

2023.10.07

2774

5

常用的数据库软件
常用的数据库软件

常用的数据库软件有MySQL、Oracle、SQL Server、PostgreSQL、MongoDB、Redis、Cassandra、Hadoop、Spark和Amazon DynamoDB。更多关于数据库软件的内容详情请看本专题下面的文章。php中文网欢迎大家前来学习。

2023.11.02

4289

19

内存数据库有哪些
内存数据库有哪些

内存数据库有Redis、Memcached、Apache Ignite、VoltDB、TimesTen、H2 Database、Aerospike、Oracle TimesTen In-Memory Database、SAP HANA和ache Cassandra。更多关于内存数据库相关问题,详情请看本专题下面的文章。php中文网欢迎大家前来学习。

2023.11.14

3795

11

mongodb和redis哪个读取速度快
mongodb和redis哪个读取速度快

redis 的读取速度比 mongodb 更快。原因包括:1. redis 使用简单的键值存储,而 mongodb 存储 json 格式的数据,需要解析和反序列化。2. redis 使用哈希表快速查找数据,而 mongodb 使用 b-tree 索引。因此,redis 在需要高性能读取操作的应用程序中是一个更好的选择。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2024.04.02

6872

6

redis怎么做缓存服务器
redis怎么做缓存服务器

redis 作为缓存服务器的答案:redis 是一款开源、高性能、分布式的键值存储,可作为缓存服务器使用。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2024.04.07

623

6

redis怎么解决数据一致性
redis怎么解决数据一致性

redis 提供了两种一致性模型,以维护副本数据一致性:强一致性 (sync) 确保写操作仅在复制到所有从节点后才完成;最终一致性 (async) 则在主节点上写操作后认为已完成,牺牲一致性换取性能。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2024.04.07

736

6

mysql和redis怎么保证双写一致性
mysql和redis怎么保证双写一致性

确保 mysql 和 redis 双写一致性的技术包括:1、事务性更新:同时更新 mysql 和 redis,保证一致性;2、主从复制:mysql 主服务器更改同步到 redis 从服务器;3、基于事件的更新:mysql 记录更改并发送到 redis等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2024.04.07

6482

6

redis缓存一般存些什么数据
redis缓存一般存些什么数据

redis缓存中存储的数据类型包括:字符串、哈希、列表、集合、有序集合、位图、地理空间数据和hyperloglog。这些数据类型适用于存储各种数据,从简单信息到复杂对象和地理位置。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2024.04.07

1140

6

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
phpEnv手册
phpEnv手册

共0课时 | 0人学习

进程与SOCKET
进程与SOCKET

共6课时 | 0.5万人学习