事务使用MULTI 、 EXEC 、 DISCARD 和 WATCH 等命令。很多情况下我们需要一次执行不止一个命令,而且需要其同时成功或者失败。redis对事务的支持也是源自于这部分需求,即支持一次性按顺序执行多个命令的能力,并保证其原子性。
在redis的官网上洋洋洒洒的大概提供了200多个命令(都是原子性的),貌似看起来很多,但是这些都是别人预先给你定义好的,但你却不能按照自己的意图进行定制,所以是不是感觉自己还是有一种被束缚的感觉,有这个感觉就对了。。。
Lua脚本是在事务的基础上,如果我们需要在服务端一次性的执行更复杂的操作(包含一些逻辑判断),则lua就可以派上用场了。它和200多个命令一样都是原子性的。所以说这极大的扩展了redis。要想学好Redis,必会Lua Script。。。感觉这个很像是mysql的存储过程。
使用Lua脚本的好处:
- 减少网络开销。可以将多个请求通过脚本的形式一次发送,减少网络时延。
- 原子操作。redis会将整个脚本作为一个整体执行,中间不会被其他命令插入。因此在编写脚本的过程中无需担心会出现竞态条件,无需使用事务。
- 复用。客户端发送的脚本会永久存在redis中,这样,其他客户端可以复用这一脚本而不需要使用代码完成相同的逻辑。
redis中lua介绍:
- 使用eval来调用lua
EVAL script numkeys key [key ...] arg [arg ...]
首先大家一定要知道eval的语法格式,其中:
<1> script: lua脚本,如果是文本路径,需要加file_get_contens()来获取
<2> numkeys: key的个数
<3> key: redis中各种数据结构的替代符号
<4> arg: 你的自定义参数
在redis cli 模式下。该命令表示key的个数为2,即username和age,相应的username也就是KEY[1],age也就是key[2]。key参数后面的也就是arg参数了,也就是jack和20,相应的jack也就是ARGV[1],20也就是ARGV[2]。
127.0.0.1:6379> eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 username age jack 20
1) "username"
2) "age"
3) "jack"
4) "20"
在php中同样也是使用eval来调用的(predis扩展)
Redis::eval(file_get_contents(storage_path("app/lock.lua")), 1, "1lock", "121313");
- 使用call来调用redis的方法
比如上面的文本lock.lua内容如下
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
- 一个简单例子
批量写入集合
public function mSadd()
{
$lua = <<<LUA
local _key = KEYS[1]
local _ttl = tonumber(ARGV[1])
local _values = ARGV
for idx, v in ipairs(_values) do
if (idx > 1) then
redis.call('SADD', _key, v)
end
end
if (_ttl > 0) then
local tl = redis.call('TTL', _key)
if (tl == -1) then
redis.call('EXPIREAT', _key, _ttl)
end
end
return true
LUA;
$orderNo = [1233243,434434311,4353543434];
$expireAt = '1665478454';
$redisKey = 'order:01409103';
$params = array_unique(array_filter($orderNo));
array_unshift($params, $expireAt);
array_unshift($params, $redisKey);
$redis = \Lta\Redis::getConn();
//* @param string $script
//* @param array $args
//* @param int $numKeys
$res = $redis->eval($lua, $params, 1);
return boolval($res);
}
lua的简单用户大致如上,使用eval调用脚本,使用call来调用redis已有的方法。具体的lua脚本用法参考官网