
# Lua脚本编写规范
Lua是一种脚本语言，目的是为了嵌入应用程序中，为应用程序提供灵活的扩展和定制功能。GeminiDB Redis使用的是Lua5.1.5版本，与开源Redis5.0使用的Lua版本是一致的。
![](https://support.huaweicloud.com/redisug-nosql/public_sys-resources/note_3.0-zh-cn.png)
使用Lua脚本时，需要经过谨慎的校验，否则可能出现死循环、业务超时等情况，甚至会导致业务不可用。
#### 与开源Redis Lua的区别
1. **EVAL** **/** **EVALSHA** **命令**
   命令格式：
   **EVAL script numkeys key \[key ...\] arg \[arg ...\]**
   **EVALSHA sha1 numkeys key \[key ...\] arg \[arg ...\]**
   上述命令的语法与操作与开源Redis一致。用户需自己保证将脚本中使用到的Redis key显式地通过key参数传入，而不是直接在脚本中编码。
   若使用集群版实例，如果带有多个key参数，则要求所有的key参数必须拥有相同的hashtag。
   如果不遵循上述约束，则在Lua中执行涉及这些key的Redis操作时，可能会返回错误信息，甚至可能导致数据的一致性被破坏。
   
2. **SCRIPT** **命令**
   SCRIPT命令包含了一组管理Lua脚本的子命令，具体可以通过SCRIPT HELP命令查询具体的操作。
   SCRIPT大部分命令都与开源Redis兼容，其中需要特别说明的命令如下：
   - **SCRIPT KILL**
     GeminiDB Redis是多线程执行的环境，允许同时执行多个Lua脚本，执行SCRIPT KILL，会终止所有正在运行的Lua脚本。
     为了方便使用，GeminiDB Redis扩展了SCRIPT KILL命令，用户可以通过'SCRIPT KILL SHA1'来终止指定哈希值的脚本。若同一时间存在多个节点在执行哈希值相同的脚本，那么这些脚本都会被终止。
     另外，由于用户无法设置Lua超时时间（config set lua-time-limit），因此在任意时刻执行SCRIPT KILL都能直接终止脚本，而不是等待脚本超时后才终止。
     
   
   
   
   - **SCRIPT DEBUG**
     目前GeminiDB Redis不支持DEBUG功能，所以该命令执行无效。
     
   
   - **SCRIPT GET**
     GeminiDB Redis新增命令, 可用来查询通过 "SCRIPT LOAD" 保存到数据库中的脚本内容。
     语法为：SCRIPT GET SHA1。
     
    
3. **Lua** **脚本中执行Redis命令**
   与开源Redis一致，GeminiDB Redis的Lua环境中也提供了一个全局的"redis"表，用于提供各类和Redis Server交互的函数。
   如[表1]为GeminiDB Redis目前支持和不支持的操作列表。
    表1函数列表 
   | **支持的操作**                                                                                                                                                                                                                                                                                                                                                                                                                                          | **不支持的操作**                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
   |:---|:---|
   | - redis.call()  - redis.pcall()  - redis.sha1hex()  - redis.error_reply()  - redis.status_reply()   | - redis.log()  - redis.LOG_DEBUG  - redis.LOG_VERBOSE  - redis.LOG_NOTICE  - redis.LOG_WARNING  - redis.replicate_commands()  - redis.set_repl()  - redis.REPL_NONE  - redis.REPL_AOF  - redis.REPL_SLAVE  - redis.REPL_REPLICA  - redis.REPL_ALL  - redis.breakpoint()  - redis.debug()   |
      
   
4. **Lua** **执行环境限制**
   开源Redis对Lua脚本的执行有一定的限制，比如限制脚本操作全局变量，限制随机函数的结果，限定能够使用的系统库和第三方库等。
   GeminiDB Redis也继承了绝大多数的限制，但是针对如下情况，GeminiDB Redis与开源Redis存在差异：
   - **Write Dirty**
     开源Redis规定，如果某个脚本已经执行了写操作，那么就不能被SCRIPT KILL停止执行，必须使用SHUTDOWN NOSAVE来直接关闭Redis Server。
     GeminiDB Redis不支持执行SHUTDOWN命令，因此这条限制不会被执行，用户仍然可以通过SCRIPT KILL来停止脚本的执行。
     
   
   
   
   - **Random Dirty**
     由于主从复制的原因，开源Redis规定，若脚本执行了带有随机性质的命令（Time, randomkey），则不允许再执行写语义的命令。
     例如，如下Lua脚本：
     ```
     local t = redis.call("time")
     return redis.call("set", "time", t[1]);
     ```
     当该脚本的执行传递到从节点时，Time命令获取到的时间一定晚于主节点，因此从节点执行的Set命令的值就会和主节点产生冲突。开源Redis引入了replicate_commands来允许用户决定这种场景下的行为模式。
     对于GeminiDB Redis来说，由于没有主从的概念，数据在逻辑上只有一份，因此也就不存在该限制。
     
    
 
#### Lua脚本中禁止执行的命令
Hash：hscan
List：blpop、brpop、brpoplpush、blmpop 、blmove
Set：sscan
Sorted Set：bzpopmax、bzpopmin、zscan、bzmpop
Stream：xread、xreadgroup
Generic：rename、renamenx、restore、scan、client、command、config、dbsize、flushall、flushdb、info、keys
Lua：eval、evalsha、script
Pub/Sub：psubscribe、publish、punsubscribe、subscribe、unsubscribe
Transactions：discard、exec、multi、unwatch、watch
