redis之lua脚本系列问题

1,redis加入了lua虚拟机,可以执行lua脚本。为复杂的redis操作提供了解决方案。性能方面也不错。

2,lua执行是原子性。

3,redis加入 lua是一件伟大的事情。

 

以前使用redis lua脚本。只是解决一些简单的事情。这次需要处理复杂的事情。遇到了很多问题。

1,方法的定义。

        这次lua脚本需要多个操作。通过参数需要执行不同的操作。不想以 if else if 进行处理。需要定义方法。普通的方式定义方法是有问题的。

 

全局变量保护

为了防止不必要的数据泄漏进 Lua 环境, Redis 脚本不允许创建全局变量。如果一个脚本需要在多次执行之间维持某种状态,它应该使用 Redis key 来进行状态保存。

企图在脚本中访问一个全局变量(不论这个变量是否存在)将引起脚本停止, EVAL 命令会返回一个错误:

redis 127.0.0.1:6379> eval 'a=10' 0
(error) ERR Error running script (call to f_933044db579a2f8fd45d8065f04a8d0249383e57): user_script:1: Script attempted to create global variable 'a'

Lua 的 debug 工具,或者其他设施,比如打印(alter)用于实现全局保护的 meta table ,都可以用于实现全局变量保护。

实现全局变量保护并不难,不过有时候还是会不小心而为之。一旦用户在脚本中混入了 Lua 全局状态,那么 AOF 持久化和复制(replication)都会无法保证,所以,请不要使用全局变量。

  

 

  解决方法,redis已经提供,

 

void scriptingEnableGlobalsProtection(lua_State *lua) {
    char *s[32];
    sds code = sdsempty();
    int j = 0;

    /* strict.lua from: http://metalua.luaforge.net/src/lib/strict.lua.html.
     * Modified to be adapted to Redis. */
    s[j++]="local dbg=debug\n";
    s[j++]="local mt = {}\n";
    s[j++]="setmetatable(_G, mt)\n";
    s[j++]="mt.__newindex = function (t, n, v)\n";
    s[j++]="  if dbg.getinfo(2) then\n";
    s[j++]="    local w = dbg.getinfo(2, \"S\").what\n";
    s[j++]="    if w ~= \"main\" and w ~= \"C\" then\n";
    s[j++]="      error(\"Script attempted to create global variable '\"..tostring(n)..\"'\", 2)\n";
    s[j++]="    end\n";
    s[j++]="  end\n";
    s[j++]="  rawset(t, n, v)\n";
    s[j++]="end\n";
    s[j++]="mt.__index = function (t, n)\n";
    s[j++]="  if dbg.getinfo(2) and dbg.getinfo(2, \"S\").what ~= \"C\" then\n";
    s[j++]="    error(\"Script attempted to access unexisting global variable '\"..tostring(n)..\"'\", 2)\n";
    s[j++]="  end\n";
    s[j++]="  return rawget(t, n)\n";
    s[j++]="end\n";
    s[j++]="debug = nil\n";
    s[j++]=NULL;

    for (j = 0; s[j] != NULL; j++) code = sdscatlen(code,s[j],strlen(s[j]));
    luaL_loadbuffer(lua,code,sdslen(code),"@enable_strict_lua");
    lua_pcall(lua,0,0,0);
    sdsfree(code);
}

 定义一个table类型的局部变量。

 

然后通过setmeatable 把局部变量与_G进行版本。

可以局部变量,就可以绑定方法了。

 

 

2,返回

      执行方法的返回,是无效的。需要在脚本末端,执行 return “data” ,返回数据。脚本末端没有return

 

3,命令属性。

     因为需要要执行time 命令,结果报错

Write commands not allowed after non deterministic commands. Call redis.replicate_commands() at the start of your script in order to switch to single commands replication mode

   找了好久,重要找到问题在哪里了。redis的命令是分种类的。

#define CMD_WRITE 1                   /* "w" flag */
#define CMD_READONLY 2                /* "r" flag */
#define CMD_DENYOOM 4                 /* "m" flag */
#define CMD_NOT_USED_1 8              /* no longer used flag */
#define CMD_ADMIN 16                  /* "a" flag */
#define CMD_PUBSUB 32                 /* "p" flag */
#define CMD_NOSCRIPT  64              /* "s" flag */
#define CMD_RANDOM 128                /* "R" flag */
#define CMD_SORT_FOR_SCRIPT 256       /* "S" flag */
#define CMD_LOADING 512               /* "l" flag */
#define CMD_STALE 1024                /* "t" flag */
#define CMD_SKIP_MONITOR 2048         /* "M" flag */
#define CMD_ASKING 4096               /* "k" flag */
#define CMD_FAST 8192                 /* "F" flag */

 

详细分类请看,server.h 或

http://blog.csdn.net/wtyvhreal/article/details/43193591

 

REDIS_CMD_NOSCRIPT 类型的命令,是无法执行的。

REDIS_CMD_RANDOM 类型的命令,执行会后无法执行 REDIS_CMD_WRITE命令,这也是一致报错的原因。最后删除了time命令,client传递时间戳。

 

4,KEYS, ARGV, 都是table类型,是可以迭代的。

你可能感兴趣的:(redis之lua脚本系列问题)