在使用redis过程中,我们经常会使用到lua脚本,可能大多数的同学使用场景是这样的:
incrScript := "一大段的lua脚本" redis.Eval(incrScript, keys, args...)
lua脚本可能会很长,这里就会有个问题是需要考虑的,每次执行都有传输一大段文本到redis服务器(脚本文本),很浪费网络io;聪明的同学可能会想到解决方案了。把脚本缓存到服务器上,通过
redis.EvalSha(scHash, keys, args...)
来执行;很好,于是乎这样的思路出现了,先加载脚本到redis服务器,得到一个脚本hash值,通过 EvalSha 来执行脚本,大体的代码是这样的:
scHash, e = redis.ScriptLoad(incrScript) redis.EvalSha(scHash, keys, args...)
但是,这里要解决两个问题:
1)怎么保证ScriptLoad尽量少的执行?
2)集群环境多节点怎么保障?
针对第一个问题,我的思路是这样的,通过一个闭包机制,保障一个进程内,第一次调用执行ScriptLoad得到脚本hash值,具体代码实现:
var incrScriptFn = func() func(cli *redis.Redis, keys []string, args ...interface{}) (val interface{}, err error) { once := &sync.Once{} scHash := "" return func(cli *redis.Redis, keys []string, args ...interface{}) (val interface{}, err error) { once.Do(func() { _ = tools.DoWithRetry(func() error { var e error scHash, e = cli.ScriptLoad(incrScript) return e }, time.Millisecond*15) }) // 通过得到的scHash来调用cli.EvalSha(scHash, keys, args...) ...... ...... ...... } }()
针对第二个问题,我的思路是这样的,因为同样的脚本(注意,脚本里面不能出现随机值和可变值,保障在不同的客户端执行hash的结果是一样的)执行的hash值是一样的,并且redis有个机制,在每次支持eval以后,服务器会缓存该脚本,缓存的脚本hash值跟ScriptLoad得到的hash值是一样的
针对这个机制,我们可以代码这样来实现:
var incrScriptFn = func() func(cli *redis.Redis, keys []string, args ...interface{}) (val interface{}, err error) { once := &sync.Once{} scHash := "" return func(cli *redis.Redis, keys []string, args ...interface{}) (val interface{}, err error) { // 得到scHash ..... ..... ..... // 通过执行脚本hash,可以节约数据的传输 val, err = cli.EvalSha(scHash, keys, args...) if err != nil { // 如果失败,有可能是在集群环境中,打到没有缓存的节点上了,这里通过eval直接执行,执行成功以后,该节点会缓存该脚本,下次 // 执行就直接可以运行EvalSha了 return cli.Eval(incrScript, keys, args...) } return } }()
综上,最后完整的闭包代码实现:
var incrScriptFn = func() func(cli *redis.Redis, keys []string, args ...interface{}) (val interface{}, err error) { once := &sync.Once{} scHash := "" return func(cli *redis.Redis, keys []string, args ...interface{}) (val interface{}, err error) { once.Do(func() { _ = tools.DoWithRetry(func() error { var e error scHash, e = cli.ScriptLoad(incrScript) return e }, time.Millisecond*15) }) if len(scHash) == 0 { // 这里是一个保护设置,保证在incrScriptHash获取不到脚本hash的时候还能正常执行 return cli.Eval(incrScript, keys, args...) } // 通过执行脚本hash,可以节约数据的传输 val, err = cli.EvalSha(scHash, keys, args...) if err != nil { // 如果失败,有可能是在集群环境中,打到没有缓存的节点上了,这里通过eval直接执行,执行成功以后,该节点会缓存该脚本,下次 // 执行就直接可以运行EvalSha了 return cli.Eval(incrScript, keys, args...) } return } }()
调用的地方:
说到最后:
当然,有的同学可能会这样想,我们可以让运维同学先把脚本缓存到服务器呀,代码里面直接使用hash值。但是这里有个问题就是运维成本的问题,加之在redis环境中,节点可能是动态伸缩的。所以从简单角度考虑,还是代码实现更方便。
欢迎大家砸砖讨论,留言转发关注
到此这篇执行lua脚本的软件_魔兽世界lua能写一键宏吗的文章就介绍到这了,更多相关内容请继续浏览下面的相关推荐文章,希望大家都能在编程的领域有一番成就!版权声明:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权、违法违规、事实不符,请将相关资料发送至xkadmin@xkablog.com进行投诉反馈,一经查实,立即处理!
转载请注明出处,原文链接:https://www.xkablog.com/luakf/1978.html