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类型,是可以迭代的。