中文
铁叔

天地不仁 以万物为刍狗


  • 首页

  • 归档

  • 关于我

  • 公益404

  • 搜索

使用 lua script 来执行 redis 事务

时间: 2021-11-08   |   分类: redis   | 字数: 878 字 | 阅读: 2分钟 | 阅读次数:

redis 事务有两种实现方式,一种是通过 MULTI 命令 , 另一种是使用 lua script. lua script 更简单, 而且 lua script 由于交互更少,且 redis 可以缓存 lua script,因此,效率更高,非常适合用于追求效率的地方。

lua script

官方文档: https://redis.io/commands/eval

redis的lua脚本执行有两个命令,一个是eval, 另一个是 evalsha.

eval 命令的语法:

> eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2], ARGV[3]}" 2 key1 key2 first second third
1) "key1"
2) "key2"
3) "first"
4) "second"
5) "third"

说明:

  • 第二个字符串参数是执行的 lua 脚本,其中 KEYS ARGV 是关键字;
  • 2 是指在 lua 脚本中,引用了两个 KEYS
  • KEYS 用于指定 redis 的 key
  • ARGV 就是后面的first second third 参数,在 KEYS 之后,序号从1开始
  • return 是返回的数据

如果服务端已经缓存了 lua 脚本,可以使用 evalsha 来执行,当脚本内容很多时,这样,传输的数据更少。

从redis 3.2.0开始,redis 中集成了 lua 调试工具,参考这里: https://redis.io/topics/ldb

使用示例

import "github.com/gomodule/redigo/redis"

// redis lua 脚本, 确保预留分配多个时的原子操作
func ReserveUserToken(conn redis.Conn, accountID uint64, howmany uint32) (latest uint64, err error) {
	ss := `local sid=redis.call('HGET',KEYS[1],'latest') or 0; `
	for i := 0; i < int(howmany); i++ {
		ss += `redis.call('HSET', KEYS[1], sid, ARGV[1]); sid=sid+1; `
	}
	// 更新 latest 值
	ss += `redis.call('HSET', KEYS[1], 'latest', sid); return sid`
	script := redis.NewScript(1, ss)

	key := fmt.Sprintf("userTokens:%d", accountID)
	ts := time.Now().Unix()

	resp, err := script.Do(conn, key, ts)
	if err != nil {
		return
	}
	latest = uint64(resp.(int64))
	return
}

上述代码作用如下:

  1. 在 redis 上为每个用户生成 hashmap userToken:{accountId} , 如果没有,创建该hashmap,且将 hashmap 的 key latest 设置为 0;
  2. 如果 howmany 不为0,则为用户预留 token, 预留的方式为:
    • 从 latest 增加1 作为key, 写入 hashmap 中
    • latest 自增
    • 循环操作
  3. 将最新的 latest 写入 hashmap,并返回

值得注意的是,local sid=redis.call('HGET',KEYS[1],'latest') or 0; 这里一定要有最后的 or 0, 否则 sid 会是一个bool 类型,无法进行数值运算,会提示如下错误:

(error) ERR Error running script (call to f_2bd1a2e80fc04313100937b7b533da6bea026773): @user_script:1: user_script:1: attempt to perform arithmetic on local 'sid' (a boolean value)

几点补充

lua 判断

lua脚本判断的语法为:

if cond then
	clause
else
	clause
end

如果没有else,可以简化为:

if cond then
	clause
end

当然,还有 elif 的语法。

整数与字符串

由于 redis 中的 key 是字符串类型,而 value 可以是整数。当你的key也是整数时,使用key与value相比时,将无法得到预期的结果,这是因为key为字符串类型,如果需要比较,可以用两种方法:

  1. 使用tonumber来转换
  2. 使用 +0 来强制转换

json 处理

redis中的lua脚本内置了cjson库,可以在lua脚本中直接使用cjson encode和decode来操作json数据。

encode 例子如下:

local user={name=ARGV[1],year=ARGV[2],address=ARGV[3]};
local userJson=cjson.encode(user);
if redis.call('HSET', 'users', ARGV[1], userJson) == 0 then
	return -1
else
	return userJson
end

decode 类似:

local userJson = redis.call('HSET', 'users', ARGV[1]);
local user = cjson.decode(userJson)
#redis# #lua script# #transcation# #一致性#

声明:使用 lua script 来执行 redis 事务

链接:https://guotie.github.io/post/2021-11/redis-lua-script-transaction/

作者:铁叔

声明: 本博客文章除特别声明外,均采用 CC BY-NC-SA 3.0许可协议,转载请注明出处!

创作实属不易,如有帮助,那就打赏博主些许茶钱吧 ^_^
WeChat Pay

微信打赏

Alipay

支付宝打赏

AAVE v3 主要功能
hardhat solidity 常见错误
铁叔

铁叔

千里之行 始于足下

25 日志
14 分类
56 标签
GitHub twitter telegram email medium
标签云
  • Solidity
  • Defi
  • Aave
  • Compound
  • Abi
  • Dapp
  • Ethereum
  • Evm
  • Lend protocol
  • Lending
© 2010 - 2024 铁叔
Powered by - Hugo v0.119.0 / Theme by - NexT
/
0%