Go语言中通过Lua脚本操作Redis的方法

栏目: 编程语言 · Go · 发布时间: 6年前

内容简介:这篇文章主要给大家介绍了关于Go语言中通过Lua脚本操作Redis的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考借鉴,下面随着小编来一起学习学习吧。

前言

为了在我的一个基本库中降低与 Redis 的通讯成本,我将一系列操作封装到 LUA 脚本中,借助Redis提供的EVAL命令来简化操作。

EVAL能够提供的特性:

  • 可以在LUA脚本中封装若干操作,如果有多条Redis指令,封装好之后只需向Redis一次性发送所有参数即可获得结果
  • Redis可以保证Lua脚本运行期间不会有其他命令插入执行,提供像数据库事务一样的原子性
  • Redis会根据脚本的SHA值缓存脚本,已经缓存过的脚本不需要再次传输Lua代码,减少了通信成本,此外在自己代码中改变Lua脚本,执行时Redis必定也会使用最新的代码。

导入常见的 Go 库如 "github.com/go-redis/redis",就可以实现以下代码。

生成一段Lua脚本

// KEYS: key for record
// ARGV: fieldName, currentUnixTimestamp, recordTTL
// Update expire field of record key to current timestamp, and renew key expiration
var updateRecordExpireScript = redis.NewScript(`
redis.call("EXPIRE", KEYS[1], ARGV[3])
redis.call("HSET", KEYS[1], ARGV[1], ARGV[2])
return 1
`)

该变量创建时,Lua代码不会被执行,也不需要有已存的Redis连接。

Redis提供的Lua脚本支持,默认有KEYS、ARGV两个数组,KEYS代表脚本运行时传入的若干键值,ARGV代表传入的若干参数。由于Lua代码需要保持简洁,难免难以读懂,最好为这些参数写一些注释

注意:上面一段代码使用``跨行,`所在的行虽然空白回车,也会被认为是一行,报错时不要看错代码行号。

运行一段Lua脚本

 updateRecordExpireScript.Run(c.Client, []string{recordKey(key)}, 
         expireField,
         time.Now().UTC().UnixNano(), int64(c.opt.RecordTTL/time.Second)).Err()

运行时,Run将会先通过EVALSHA尝试通过缓存运行脚本。如果没有缓存,则使用EVAL运行,这时Lua脚本才会被整个传入Redis。

Lua脚本的限制

  • Redis不提供引入额外的包,例如os等,只有redis这一个包可用。
  • Lua脚本将会在一个函数中运行,所有变量必须使用local声明
  • return返回多个值时,Redis将会只给你第一个

脚本中的类型限制

  • 脚本返回nil时,Go中得到的是err = redis.Nil(与Get找不到值相同)
  • 脚本返回false时,Go中得到的是nil,脚本返回true时,Go中得到的是int64类型的1
  • 脚本返回{"ok": ...}时,Go中得到的是redis的status类型(true/false)
  • 脚本返回{"err": ...}时,Go中得到的是err值,也可以通过return redis.error_reply("My Error")达成
  • 脚本返回number类型时,Go中得到的是int64类型
  • 传入脚本的KEYS/ARGV中的值一律为string类型,要转换为数字类型应当使用to_number

如果脚本运行了很久会发生什么?

Lua脚本运行期间,为了避免被其他操作污染数据,这期间将不能执行其它命令,一直等到执行完毕才可以继续执行其它请求。当Lua脚本执行时间超过了lua-time-limit时,其他请求将会收到Busy错误,除非这些请求是SCRIPT KILL(杀掉脚本)或者SHUTDOWN NOSAVE(不保存结果直接关闭Redis)

更多内容参考以下地址,我这里主要是根据使用Go的经验提供一些总结。https://redis.io/commands/eval

一段更“复杂”的脚本,它要求在获取一个key值时,如果该值访问较多,就延长生存周期。此外还要比较更新时间,如果不需要更新,则直接返回取到的值,否则返回redis.Nil

// KEYS: rec:key, key
// ARGV: currentUnixTimestamp, hotHit, recordTTL, ttl
// When there's a hit,
var fetchRecordScript = redis.NewScript(local value = redis.call("GET", KEYS[2]) if(value == nil) then return nil end local hit = redis.call("HINCRBY", KEYS[1], "hit", 1) redis.call("EXPIRE", KEYS[1], ARGV[3]) local minHotHit = tonumber(ARGV[2]) local keyTTL = tonumber(ARGV[4]) if(hit > minHotHit)then keyTTL = keyTTL * 2 end redis.call("EXPIRE", KEYS[2], keyTTL) local expire = tonumber(redis.call("HGET", KEYS[1], "expire")) local unixTime = tonumber(ARGV[1]) if(expire == nil or expire < unixTime) then return nil else return value end)
// KEYS: key for record
// ARGV: fieldName, currentUnixTimestamp, recordTTL
// Update expire field of record key to current timestamp, and renew key expiration
var updateRecordExpireScript = redis.NewScript(redis.call("EXPIRE", KEYS[1], ARGV[3]) redis.call("HSET", KEYS[1], ARGV[1], ARGV[2]) return 1)

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

C++程序设计语言

C++程序设计语言

Bjarne Stroustrup / 裘宗燕 / 机械工业出版社 / 2010-3-1 / 99.00元

本书是在C++语言和程序设计领域具有深远影响、畅销不衰的著作,由C++语言的设计者编写,对C++语言进行了最全面、最权威的论述,覆盖标准C++以及由C++所支持的关键性编程技术和设计技术。本书英文原版一经面世,即引起业内人士的高度评价和热烈欢迎,先后被翻译成德、希、匈、西、荷、法、日、俄、中、韩等近20种语言,数以百万计的程序员从中获益,是无可取代的C++经典力作。 在本书英文原版面世10年......一起来看看 《C++程序设计语言》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

MD5 加密
MD5 加密

MD5 加密工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具