Golang Go语言为SDB增加lua脚本,搞了好久的终于完成了

SDB 背后的思考 ———— lua 脚本支持


lua 脚本是酷炫的,Redis 、Nginx 等开源项目都可以内嵌 lua 实现业务逻辑。所以 SDB 也打算支撑 lua 脚本。还好找到了优秀的开源项目:gopher-lua

初始化

L := lua.NewState()
defer L.Close()

方法注册

将 SDB 的方法注册到 lua 脚本中,以 LCount 为例子:

L.SetGlobal("LCount", L.NewFunction(func(L *lua.LState) int {
	userKey := L.CheckString(1)
	luaLogger.Printf("[LCount] userKey: %s", userKey)
res, err := luaService.list.Count(batch, []byte(userKey))
if err != nil {
	L.RaiseError("%s", err)
}
L.Push(lua.LNumber(res))
return 1

}))

如何保证事务操作?

lua 脚本是灵活的,在上面我们可以写很多的业务逻辑,如果我们在 lua 中用了:LLPush 、LRPush 如何能保证多个方法是操作是事务的呢?也比较简单,我们只需要创建一个 batch 对象,执行完我们的 lua 脚本后进行 commit 。保证执行每一次脚本是事务的。

加锁逻辑?

这其实是最容易被忽略的一点。对比 Redis 来说,由于是单线程的,所以 lua 脚本是不需要考虑锁的。

但是 SDB 不同,SDB 首先是支持多线程的,那么对 userKey 的写操作会进行加锁。lua 脚本在某种程度上破坏了对单个 userKey 的加锁策略。

首先 lua 脚本得让用户传入会操作的 userKey 列表,然后对每个 userKey 进行加锁。只有获取了所有锁,lua 脚本才能开始运行。

但这是不够的,可能会出现死锁问题。如当一个 lua 脚本操作 a 、b 两个 userKey ,另一个 lua 脚本操作 b 、d 两个 userKey 。假设 d 和 a 的锁是同一把。就会出现死锁的问题。

防止死锁的解决方法也很简单,只需要保证先对 lua 脚本的 userKey 按某种顺序依次获取锁既可。


Golang Go语言为SDB增加lua脚本,搞了好久的终于完成了

更多关于Golang Go语言为SDB增加lua脚本,搞了好久的终于完成了的实战教程也可以访问 https://www.itying.com/category-94-b0.html

5 回复

这个库的 State 本身就不支持真正的并发,哪怕 Clone State 也是一样…在多个时间片段同时读写就会触发恐慌…需要自己 fork 把里面一堆 map 全部改了才行…

更多关于Golang Go语言为SDB增加lua脚本,搞了好久的终于完成了的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


如果每个 lua 请求都是新的 State 呢

gopher-lua 的性能比较差,而且不管是 c 版还是 go 版的 lua 都不支持并发,多个 lua state 对性能提升意义也不大。另外,如果 lua 写复杂了 gopher-lua 的语法解析还可能出错、运行不正确。

> 如果每个 lua 请求都是新的 State 呢

这个成本就更高了,这会让你的程序慢太多了,只适合请求量低的系统。

嗯嗯,那看起来是:gopher-lua 不是并发安全的,但多个 state 是没问题的。至于性能,没有考虑的,如果要考虑限制下 lua 脚本的并发数量就好了。

恭喜你成功为SDB(假设SDB是一个存储数据库系统)集成了Lua脚本支持,这是一项非常有价值的技术提升!在Go语言中集成Lua脚本可以极大地增强系统的灵活性和扩展性。以下是一些关于此实现的后续考虑和建议:

  1. 性能优化:虽然Lua脚本带来了灵活性,但也要注意其可能带来的性能开销。确保对关键路径进行了性能测试,并根据需要进行优化。

  2. 安全性:在执行Lua脚本时,务必注意脚本的安全性。避免执行不受信任的脚本,以防止潜在的代码注入攻击。可以考虑实现沙箱机制,限制脚本的访问权限。

  3. 错误处理:确保你的实现具有健壮的错误处理机制。Lua脚本执行过程中可能出现的错误应该被捕获并妥善处理,避免系统崩溃。

  4. 文档和测试:为新增的Lua脚本功能编写详细的文档,说明其使用方法、限制和最佳实践。同时,编写全面的测试用例,确保功能的稳定性和可靠性。

  5. 持续维护:随着SDB和Lua的更新,确保你的集成能够保持兼容。定期回顾和更新集成代码,以利用最新的功能和性能改进。

你的工作不仅提升了SDB的功能,也为Go语言社区贡献了一个有价值的实践案例。希望这些建议能帮助你进一步完善这一功能,期待你在未来的技术探索中取得更多成就!

回到顶部