奇怪 这段 redis lua 脚本为什么没有返回值 😭

2020-06-28 22:23:23 +08:00
 yanshenxian

script/decr.lua

-- 如果不存在, 则设置值和过期时间 返回
-- 如果已经存在, 直接自减, 过期时间保持不变
local value = redis.call('EXISTS', KEYS[1])
if value == 0 then
    redis.call('set', KEYS[1], ARGV[1], 'EX', ARGV[2])
    return ARGV[1]
else
    return redis.call('decr', KEYS[1])
end

java 代码如下

decrLuaScript = new DefaultRedisScript<>();
decrLuaScript.setResultType(Long.class);
decrLuaScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("script/decr.lua")));

Long result = atomicRedisTemplate.execute(decrLuaScript, ImmutableList.of(key), initValue, seconds);

assert Objects.nonNull(result);

测试发现如果是没有初始值, 结果返回的 null, 如果已有初始值可以正常返回 decr 后的结果

更奇怪的是.. 在 redis-cli 里面手动执行是可以返回初始值的

127.0.0.1:6379> eval "local value = redis.call('EXISTS', KEYS[1]) if value == 0 then redis.call('set', KEYS[1], ARGV[1], 'EX', ARGV[2]) return ARGV[1] else return redis.call('decr', KEYS[1]) end" 1 xxx 100 300

"100"
2364 次点击
所在节点    程序员
7 条回复
echohw
2020-06-28 23:10:05 +08:00
你试试用 Integer 类型接收,我又一次也这样,改成 Integer 类型就好了,不知道为什么┐( ̄ヮ ̄)┌
yanshenxian
2020-06-28 23:16:21 +08:00
@echohw 我刚把返回改成 `return tonumber(ARGV[1])` 可以正常返回了

但是这个 tonumber 只能处理 int 范围的, long 会溢出为负数 不知道咋整
echohw
2020-06-29 00:17:19 +08:00
刚 debug 了一下,发现 Lua 这边对类型比较严格,如果返回的是 string 类型,而使用 Long 接收的话,那么就为 null,而且我这边也没有出现你说的溢出的问题
yanshenxian
2020-06-29 00:55:17 +08:00
@echohw 想问下怎么 debug 的 (哪个地方),我刚换成 tonumber 用 Long.MAX_VALUE 去测试溢出了

> eval "local value = redis.call('EXISTS', KEYS[1]) if value == 0 then redis.call('set', KEYS[1], ARGV[1], 'EX', ARGV[2]) return ARGV[1] else return redis.call('decr', KEYS[1]) end" 1 xx 9223372036854775707 3600
"9223372036854775707"

这个直接 return 执行是正常的,但是 redis 返回的好像确实是个 string

> eval "local value = redis.call('EXISTS', KEYS[1]) if value == 0 then redis.call('set', KEYS[1], ARGV[1], 'EX', ARGV[2]) return tonumber(ARGV[1]) else return redis.call('decr', KEYS[1]) end" 1 xx 9223372036854775707 3600
(integer) -9223372036854775808

这个 tonumber 返回 直接溢出
yanshenxian
2020-06-29 01:08:06 +08:00
补充 Redis server 版本 v=6.0.4 sha=00000000:0 malloc=libc bits=64 build=8e6667c19bc7b263
echohw
2020-06-29 12:31:54 +08:00
@yanshenxian 这个是 Lua 大数运算的锅,你可以在 Lua 中返回 string 类型,然后通过序列化器来进行反序列化

![image.png]( https://i.loli.net/2020/06/29/tYLWuNVaTym5i1p.png)
yanshenxian
2020-06-29 19:00:08 +08:00
@echohw 好像无解, tonumber 最大支持 52 bit 的 integer, 也就是 9007199254740991, 如果是用 tostring 也只能拿到一个科学技术法表示的字符串,string.format("%18.0f", xxx) 同样无法处理超出范围的大数

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/685443

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX