我已经开始研究和玩lua了一下,并且发现在想要获取按键范围时它很棒。例如:
business:5:visits:2013-11-12
business:5:visits:2013-11-13
etc
使用lua我只需要向redis发送一个命令而不是完整的日期范围。
现在我正在考虑转换更多逻辑并将其移至Redis上。
采取我们的信息存储流程,目前看起来像这样:
// create a new unique id
redisClient.incr(Config.messageId, function(err, reply) {
var messageId = reply.toString();
var timestmp = Date.now();
// push message
redisClient.zadd(Config.history + ':' + obj.uid + ':' + obj.channel.replace(/\s+/g, ''), timestmp, messageId);
// store the message data by messageId
redisClient.hmset(Config.messageHash + ':' + messageId, {
'user_id': obj.uid,
'text_body': "some text",
'text_date': timestmp,
});
// set expires
redisClient.expire(Config.history + ':' + obj.uid + ':' + obj.channel.replace(/\s+/g, ''), Config.messageExpire);
redisClient.expire(Config.messageHash + ':' + messageId, Config.messageExpire);
// add to mysql-sync queue
redisClient.RPUSH(Config.messageMySQLSyncQueue, Config.messageHash + ':' + messageId);
});
以上内容很容易转换为lua,但是它的性能是否值得?
在Lua中写这个并且只需要向Redis发出1个命令会更快吗?是否会导致阻止其他命令出现问题?
答案 0 :(得分:7)
Lua脚本旨在像MULTI
命令一样工作。实际上,您使用Redis客户端的MULTI
命令开发的大多数命令都可以在Lua中实现。也就是说,您可以在脚本中封装一些复杂的操作,您的数据层将执行原子写操作,而不必担心Redis上的数据建模策略。
此外,当您想要执行快速但复杂的读取操作时,我发现它们非常有用。例如,您可能希望按顺序获取对象。对象存储在哈希键中,而顺序由有序集密钥定义。您将获得一系列所谓的有序集,并使用hmget
以哈希方式获取对象。
最重要的一点是Lua脚本应该实现尽可能快地执行的事情,因为Redis将在Lua脚本运行时阻止其他操作。也就是说,您需要执行快速中断,否则您的整体Redis性能会下降很多。
我认为你应该在真正需要它们的时候使用它们。通常,客户端是使用C#,Java,JavaScript,Ruby等高级编程语言开发的......它们提供了更好的开发体验:良好的调试器,IDE,代码完成......
摘要:如果您将域逻辑的某些部分转换为Redis Lua脚本,那么您应该使用它们来证明实际的好处(性能)。
答案 1 :(得分:4)
Redis的Lua脚本还有一个缺点:即使它们位于同一物理服务器上,您也无法向其他Redis实例发出操作。在一台服务器上运行多个Redis实例以利用所有CPU内核是一种常见做法。因此,您必须从客户端控制许多Redis实例。
总结上述所有内容:
简而言之,Redis不是应用程序服务器。所以你不能轻易地在Lua上写下你想要的任何逻辑,并确保一切正常。
如果您需要NoSQL数据库中的真实应用程序服务器,请尝试使用Tarantool。它也有Lua脚本,但区别在于它们不会相互阻塞,它们仍然作为一个ACID事务执行(如果你不做外部请求),它们可能会产生你想要的任何副作用,甚至问题请求外部数据库,如Tarantool或Redis或执行任何http请求。这是通过在单独的光纤中执行每个Lua脚本以及通过Lua脚本完成的所有更改的基于行的复制来实现的。
答案 2 :(得分:3)
TL; DR:不要使用Lua脚本(为此)
稍微长一点:Redis' Lua脚本语义调用不通过代码生成密钥名称,并声明脚本使用的任何密钥都应作为参数提供(使用KEYS
数组)。
更长时间:请参阅http://redis.io/commands/eval
的引用必须在执行前分析所有Redis命令,以确定命令将在哪些键上运行。为了使EVAL成为现实,必须明确传递密钥。这在许多方面都很有用,但尤其要确保Redis Cluster可以将您的请求转发到适当的群集节点
答案 3 :(得分:2)
Lua很好,我们在某些情况下使用它 - 特别是当我们想要发生一些原子操作时,但在你的情况下,你将在将上面的代码转换为Lua脚本并运行到Redis时遇到问题。这就是因为这一行:
var timestmp = Date.now();
在这样的一行之后你不能再执行SET操作了,主要是因为在Redis中如何处理主从复制。看看这里:http://redis.io/commands/eval#scripts-as-pure-functions
这就像反对使用Lua脚本的另一个论点。
答案 4 :(得分:2)
Lua脚本非常强大。 正如您所描述的那样,它允许限制redis服务器和客户端之间的网络往返。此外,您不会一直以字符串形式发送脚本,只有SHA1应该在第一次调用后发送,这非常小。
Lua有一些最佳实践:如果你要分片数据或使用复制,请务必分开只读"只读"从实际在集群中写入的那些(它们必须在master上执行)。
在你的lua电话之前计算,redis中你需要的所有密钥,你也不能在lua(嵌入在redis中)本地访问与时间相关的变量,这意味着必须计算与时间相关的值卢外面。通常,最好在lua之外完成大部分工作,仅使用它来对redis进行批量操作并限制网络活动。
最后,要非常小心lua超时(你可以用redis覆盖它)。由于lua执行在redis实例中是阻塞的,所以它是一个Stop-The-World操作,因此执行它不能太长(以这种方式设计你的算法" Divide and Conquer")或者一切都会得到慢下来。
还可能出现阈值问题:考虑在lua脚本中清除redis密钥。如果它需要太长时间(处理的键/操作太多),它将因lua超时而失败。然后,由于活动,您的数据以redis增长。下次你尝试使用lua进行清理时,为了清理redis,它必须处理更多的键,因此它成功的机会更少!由于lua超时,你可能会失去记忆......