redis的lua

Redis在2.6之后增加了基于lua语言的解析模块,它允许用户端通过简单的lua语言实现简单的事务和逻辑运算。个人觉得对于一个Key-value服务器来说,这么做的确有些剑走偏锋的意思,但我相信在现实中确实有这样的需求。这些天,通过简单的脚本,测试了一下redis的lua脚本。

测试的脚本语言是python,需要说明的是,由于之前我的python-redis支持库比较旧,在调用eval函数时,系统会抛出没有对应的方法的异常:

Python 2.6.6 (r266:84292, Sep 11 2012, 08:34:23)
[GCC 4.4.6 20120305 (Red Hat 4.4.6-4)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import redis
>>> r=redis.Redis('localhost')
>>> r.eval()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Redis' object has no attribute 'eval'
>>> dir(r)
['RESPONSE_CALLBACKS', 'SUBSCRIPTION_COMMANDS', '__class__', '__contains__', '__delattr__', '__delitem__', '__dict__', '__doc__', '__format__', '__getattribute__', '__getitem__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_execute_command', '_get_db', '_get_host', '_get_port', '_parse_response', '_setup_connection', '_zaggregate', 'append', 'bgrewriteaof', 'bgsave', 'blpop', 'brpop', 'connection', 'connection_pool', 'db', 'dbsize', 'decr', 'delete', 'encode', 'encoding', 'errors', 'execute_command', 'exists', 'expire', 'expireat', 'flush', 'flushall', 'flushdb', 'get', 'get_connection', 'getset', 'hdel', 'hexists', 'hget', 'hgetall', 'hincrby', 'hkeys', 'hlen', 'hmget', 'hmset', 'host', 'hset', 'hsetnx', 'hvals', 'incr', 'info', 'keys', 'lastsave', 'lindex', 'listen', 'llen', 'lock', 'lpop', 'lpush', 'lrange', 'lrem', 'lset', 'ltrim', 'mget', 'move', 'mset', 'msetnx', 'parse_response', 'ping', 'pipeline', 'pop', 'port', 'psubscribe', 'publish', 'punsubscribe', 'push', 'randomkey', 'rename', 'renamenx', 'rpop', 'rpoplpush', 'rpush', 'sadd', 'save', 'scard', 'sdiff', 'sdiffstore', 'select', 'set', 'setex', 'setnx', 'sinter', 'sinterstore', 'sismember', 'smembers', 'smove', 'sort', 'spop', 'srandmember', 'srem', 'subscribe', 'subscribed', 'substr', 'sunion', 'sunionstore', 'ttl', 'type', 'unsubscribe', 'zadd', 'zcard', 'zincr', 'zincrby', 'zinter', 'zinterstore', 'zrange', 'zrangebyscore', 'zrank', 'zrem', 'zremrangebyrank', 'zremrangebyscore', 'zrevrange', 'zrevrank', 'zscore', 'zunion', 'zunionstore']

这里只需要到https://github.com/andymccurdy/redis-py下载最新的redis for python驱动包即可。

代码如下:

import redis, time
r = redis.Redis('localhost', db=1)

script1 = '''
local i=0
local b=0
local res
local limit = tonumber(KEYS[1])
while (i <= limit) do
    res = redis.call('set', i, b)
    i = i+1
    b = b+i
end
return {KEYS[1]}'''
#start = time.time()
#print r.eval(script, 1, 20000)
#print time.time() - start

script2 = '''
local list=redis.call('keys', '*')
for x in pairs(list) do
    redis.call('del', x)
end
return {1}
'''
print r.eval(script2, 0)

eval的传入方式为eval(脚本,入参数量,入参1,入参2…)

script1是一个简单的累加数列,并依次存储到redis; script2则有点实用性:通过keys将所有key删除,当然可以通过稍加修改这个脚本,使它支持删除某一类型的key。

脚本一的执行时间在0.05秒左右,虚拟机的测试结果,这个结果明显的比通过python每次set来的强很多。

几个问题:

  1. 在执行这个脚本的过程中,redis事实上处在一个整体锁定的状态,甚至于做info, monitor操作也不行,系统会报错:(error) BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE. 这个过程会直到脚本完成后才可解除。所以lua脚本的模式并不适合redis高并发的环境,个人觉得这个问题将会在之后的某个版本完善,现阶段合适的应用环境还是数据统计和后台维护。
  2. 一旦出现错误的lua,导致死循环,可能无法直接关闭脚本,只能重启redis服务
  3. 并不是每次redis.call都会将数据写入redis。不过由于问题1的存在,数据的一致性反而可以得到保证。
推荐阅读:
之前接触到的基于LAMP平台的
对于网站来说,琐碎文件是一个很
作为MySQL对于Web应用的
碰到一个悲催的事情:一台Red

“redis的lua”的一个回复

发表评论

电子邮件地址不会被公开。 必填项已用*标注

请补全下列算式: *

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据