Redis怎样利用Lua脚本实现多Key的分布式计数_确保数据同步

小宇同学_1036

小宇同学_1036

2026-05-23

933人浏览

原创

不能直接用多个incr命令做多key计数,因为redis多命令非原子,易导致并发覆盖;lua脚本在服务端原子执行,是唯一轻量可靠方案,但需满足keys同slot、避免超时与过度复杂等约束。

redis怎样利用lua脚本实现多key的分布式计数_确保数据同步

为什么不能直接用多个 INCR 命令做多 Key 计数

因为 Redis 的多个命令不是原子的——哪怕只差几微秒,两个客户端就可能读到同一份旧值、各自加 1、再写回去,最终只 +1 而非 +2。分布式环境下,靠客户端重试或加锁(比如 SETNX)成本高、易死锁、还可能丢数据。Lua 脚本在 Redis 服务端原子执行,是唯一轻量又可靠的选择。

  • Redis 执行 Lua 脚本时会阻塞当前连接,但整个脚本作为一个不可分割的操作完成,不会被其他命令插入
  • EVAL 和 EVALSHA 都支持传入多个 key,脚本里通过 KEYS 表访问,完全规避客户端并发竞争
  • 注意:KEYS 只能用于真正要操作的 Redis key;如果只是读配置或传参,必须走 ARGV,否则会触发集群模式下的 CROSSSLOT 错误

怎样写一个安全的多 Key 批量自增 Lua 脚本

核心是把所有要操作的 key 放进 KEYS 数组,用 redis.call("INCR", KEYS[i]) 逐个调用,并统一返回结果。不要在脚本里做条件判断后只更新部分 key——那会破坏原子性边界。

-- multi_incr.lua
local results = {}
for i, key in ipairs(KEYS) do
    table.insert(results, redis.call("INCR", key))
end
return results
  • 调用时必须显式列出所有 key:EVAL "..." 3 counter:a counter:b counter:c,其中 3 是 key 的数量
  • 如果某个 key 不存在,INCR 自动初始化为 0 再 +1,行为符合预期
  • 避免在脚本中使用 redis.call("GET", ...) 后再 INCR——这等于手动拆解原子操作,失去意义
  • 脚本长度别超过 4KB(Redis 默认限制),否则 EVAL 会报错 ERR Error running script (call to f_...): @user_script: N: user_script: too long

在 Redis Cluster 中执行多 Key Lua 脚本的硬约束

Cluster 模式下,KEYS 中的所有 key 必须落在同一个 slot,否则直接报错 CROSSSLOT Keys in request don't hash to the same slot。这不是脚本能绕过的限制,而是架构决定的。

Redis Skill - 高性能缓存管理
Redis Skill - 高性能缓存管理

Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。

下载
  • 解决办法只有两个:用哈希标签强制路由(例如 counter:{user123}:a 和 counter:{user123}:b),让大括号内内容决定 slot;或者改用单 key 结构,把多个计数存进一个 Hash,用 HINCRBY
  • 千万别用 KEYS * 或模糊匹配——Cluster 下不支持,且性能极差
  • 如果业务上确实需要跨 slot 计数(比如全局排行榜 + 用户维度计数),那就不能靠单个 Lua 脚本原子完成,得接受最终一致性,用消息队列或定期对账补偿

如何避免 Lua 脚本超时导致连接卡死

Redis 对 Lua 脚本有默认 5 秒超时(lua-time-limit 配置),超时后连接会被断开,脚本中止,但已执行的部分无法回滚——比如前 3 个 key 加了,第 4 个没来得及,就会产生数据倾斜。

  • 务必在脚本开头加简单校验,比如 if #KEYS > 100 then return {err="too many keys"} end,防止误传海量 key
  • 避免在脚本里调用 redis.call("KEYS", "...") 或遍历大集合,这些操作在生产环境极易超时
  • 上线前用 redis-benchmark -r 10000 -n 10000 --eval multi_incr.lua , key1 key2 key3 做压测,观察 P99 延迟是否稳定在毫秒级
  • 监控 latency doctor 和 INFO commandstats 中 cmdstat_eval 的 failed 和 duration 字段,及时发现异常脚本

实际最难的不是写脚本,是厘清哪些场景真的需要多 key 原子性——很多时候用 Hash 或客户端预聚合更简单,也更可控。Lua 是利器,但别把它当万能胶。

相关专题

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

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

2023.08.11

1953

5

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

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

2023.10.07

2834

5

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

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

2023.11.02

4329

19

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

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

2023.11.14

3835

11

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

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

2024.04.02

6972

6

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

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

2024.04.07

623

6

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

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

2024.04.07

756

6

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

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

2024.04.07

6642

6

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

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

2024.04.07

1160

6

热门下载

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

精品课程

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

共0课时 | 0人学习

进程与SOCKET
进程与SOCKET

共6课时 | 0.5万人学习