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"
1
echohw 2020-06-28 23:10:05 +08:00
你试试用 Integer 类型接收,我又一次也这样,改成 Integer 类型就好了,不知道为什么┐( ̄ヮ ̄)┌
|
2
yanshenxian OP |
3
echohw 2020-06-29 00:17:19 +08:00
刚 debug 了一下,发现 Lua 这边对类型比较严格,如果返回的是 string 类型,而使用 Long 接收的话,那么就为 null,而且我这边也没有出现你说的溢出的问题
|
4
yanshenxian OP @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 返回 直接溢出 |
5
yanshenxian OP 补充 Redis server 版本 v=6.0.4 sha=00000000:0 malloc=libc bits=64 build=8e6667c19bc7b263
|
6
echohw 2020-06-29 12:31:54 +08:00
@yanshenxian 这个是 Lua 大数运算的锅,你可以在 Lua 中返回 string 类型,然后通过序列化器来进行反序列化
![image.png]( https://i.loli.net/2020/06/29/tYLWuNVaTym5i1p.png) |
7
yanshenxian OP @echohw 好像无解, tonumber 最大支持 52 bit 的 integer, 也就是 9007199254740991, 如果是用 tostring 也只能拿到一个科学技术法表示的字符串,string.format("%18.0f", xxx) 同样无法处理超出范围的大数
|