redis对事务的支持目前还比较简单。redis只能保证一个client发起的事务中的命令可以连续的执行,而中间不会插入其他client的命令。redis中提供了四个命令来实现事务,MULTI:类似于mysql中的BEGIN;EXEC:类似于COMMIT;DISCARD类似于ROLLBACK;WATCH则是用于来实现mysql中类似锁的功能。redis事务的实现原理是把事务中的命令先放入队列中,当client提交了exec命令后,redis会把队列中的每一条命令按序执行一遍。如果在执行exec之前事务中断了,那么所有的命令都不会执行;如果执行了exec命令之后,那么所有的命令都会按序执行。但如果在事务执行期间redis被强制关闭,那么则需要使用redis-check-aof 工具对redis进行修复,删除那些部分执行的命令。虽然可以用事务+watch实现原子操作,但是不免有点太僵硬,很明显这是一个if……else语句。正是因为这个局限,使得lua脚本派上了大的用场。
在redis中可以通过redis.call()和redis.pcall()直接调用。redis确保正一条script脚本执行期间,其它任何脚本或者命令都无法执行,这个样子就可以变相的实现redis的事务。使用脚本的好处:
1.减少网络开销。可以将多个请求通过脚本的形式一次发送,减少网络时延
2.原子操作。redis会将整个脚本作为一个整体执行,中间不会被其他命令插入。因此在编写脚本的过程中无需担心会出现竞态条件,无需使用事务。
3.复用。客户端发送的脚步会永久存在redis中,这样,其他客户端可以复用这一脚本而不需要使用代码完成相同的逻辑。
调用Lua脚本的语法:
redis-cli –eval path/to/redis.lua KEYS[1] KEYS[2] , ARGV[1] ARGV[2] …
–eval,告诉redis-cli读取并运行后面的lua脚本
path/to/redis.lua,是lua脚本的位置
KEYS[1] KEYS[2],是要操作的键,可以指定多个,在lua脚本中通过KEYS[1], KEYS[2]获取
ARGV[1] ARGV[2],参数,在lua脚本中通过ARGV[1], ARGV[2]获取。
调用方法:
1.进入redis客户端:
eval “return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}” 2 key1 key2 first second
2.直接运行:
script.lua
local str = ''
for k,v in ipairs(KEYS) do
str = str .. v
end
return str
redis-cli --eval script.lua key1 key2 , val1 val2
3.在PHP中调用
script.lua
local str = ''
for k,v in ipairs(KEYS) do
str = str .. v
end
return str
test.php
<?php
$rds_client = new \Redis();
$rds_client->connect("127.0.0.1", "6379");
$script = file_get_contents("/path/script.lua");
// 2表示传入数据的个数
$res = $rds_client->eval($script, ["key1", 'val1', "key2", "val2"], 2);
参考文献:
http://www.redis.cn/commands/eval.html
https://github.com/phpredis/phpredis/blob/develop/redis_commands.c
http://blog.csdn.net/wzzfeitian/article/details/42081837
http://blog.csdn.net/zhouzme/article/details/53046606