内容简介:1、Redis命令行执行Lua脚本01 、redis.call() 和 redis.pcall() 两个函数的参数可以是任意的 Redis 命令
01 、redis.call() 和 redis.pcall() 两个函数的参数可以是任意的 Redis 命令
127.0.0.1:6379> EVAL "return redis.call('SET','Name','Tinywan')" 0 OK 127.0.0.1:6379> get Name "Tinywan"
说明: EVAL 和 EVALSHA 命令是从 Redis 2.6.0 版本开始的,使用内置的 Lua 解释器,可以对 Lua 脚本进行求值。
EVAL的第一个参数是一段 Lua 5.1 脚本程序。 这段Lua脚本不需要(也不应该)定义函数。它运行在 Redis 服务器中。
EVAL的第二个参数是参数的个数,后面的参数(从第三个参数),表示在脚本中所用到的那些 Redis 键(key),这些键名参数可以在 Lua 中通过全局变量 KEYS 数组,用 1 为基址的形式访问( KEYS[1] , KEYS[2] ,以此类推)。
在命令的最后,那些不是键名参数的附加参数 arg [arg …] ,可以在 Lua 中通过全局变量 ARGV 数组访问,访问的形式和 KEYS 变量类似( ARGV[1] 、 ARGV[2] ,诸如此类)。
02、使用KEYS和ARGV
127.0.0.1:6379> EVAL "return redis.call('SET',KEYS[2],ARGV[3])" 3 name01 name02 name03 Tinywan01 Tinywan02 Tinywan03 OK 127.0.0.1:6379> keys * 1) "name02" 127.0.0.1:6379> get name02 "Tinywan03"
说明:返回结果是Redis multi bulk replies的Lua数组,这是一个Redis的返回类型,您的客户端库可能会将他们转换成数组类型。
03 、redis.call() 和 redis.pcall() 的区别
redis.call() 执行一个不存在的Redis命令: SETNGX
127.0.0.1:6379> EVAL "redis.call('SETNGX',KEYS[1],ARGV[1]);redis.call('SET',KEYS[3],ARGV[1])" 3 name01 name02 name03 Tinywan01 Tinywan02 Tinywan03 (error) ERR Error running script (call to f_0adfcdb3f740b2aabfe19f0e80de7cda7ce6262f): @user_script:1: @user_script: 1: Unknown Redis command called from Lua script 127.0.0.1:6379> keys * (empty list or set) 127.0.0.1:6379>
说明:当 redis.call() 在执行命令的过程中发生错误时,脚本会停止执行,并返回一个脚本错误,错误的输出信息会说明错误造成的原因。由于第一个执行错误,导致后面的也没有执行,设置不成功。
redis.pcall() 执行一个不存在的Redis命令: SETNGX
127.0.0.1:6379> EVAL "redis.pcall('SETNGX',KEYS[1],ARGV[1]);redis.call('SET',KEYS[3],ARGV[1])" 3 name01 name02 name03 Tinywan01 Tinywan02 Tinywan03 (nil) 127.0.0.1:6379> keys * 1) "name03" 127.0.0.1:6379> get name03 "Tinywan01" 127.0.0.1:6379>
说明: redis.pcall() 出错时并不引发(raise)错误,而是返回一个 nil,后面的命令任然可以执行成功。
redis.call() 与 redis.pcall()很类似, 他们唯一的区别是当redis命令执行结果返回错误时, redis.call()将返回给调用者一个错误,而redis.pcall()会将捕获的错误以Lua表的形式返回。
redis.call() 和 redis.pcall() 两个函数的参数可以是任意的 Redis 命令
2、Redis中Lua脚本命令介绍
01、SCRIPT 命令
命令用于将脚本 script 添加到脚本缓存中,但并不立即执行这个脚本。
127.0.0.1:6379> SCRIPT LOAD "return redis.call('set',KEYS[1],ARGV[1])" "c686f316aaf1eb01d5a4de1b0b63cd233010e63d"
02、EVALSHA 命令
根据给定的 SHA1 校验码(也就是 SCRIPT LOAD 执行脚本生成的哈希值),对缓存在服务器中的脚本进行求值。 将脚本缓存到服务器的操作可以通过 SCRIPT LOAD 命令进行。
127.0.0.1:6379> EVALSHA c686f316aaf1eb01d5a4de1b0b63cd233010e63d 2 Github Blog github.tinywan blog.tinywan OK 127.0.0.1:6379> keys * 1) "Github" 2) "name03" 127.0.0.1:6379> get Github "github.tinywan"
03、 SCRIPT FLUSH 命令
清空Lua脚本缓存 Flush the Lua scripts cache.
127.0.0.1:6379> SCRIPT FLUSH OK 127.0.0.1:6379> EVALSHA c686f316aaf1eb01d5a4de1b0b63cd233010e63d 2 Github Blog github.tinywan blog.tinywan (error) NOSCRIPT No matching script. Please use EVAL. 127.0.0.1:6379> SCRIPT LOAD "return redis.call('set',KEYS[1],ARGV[1])" "c686f316aaf1eb01d5a4de1b0b63cd233010e63d" 127.0.0.1:6379> EVALSHA c686f316aaf1eb01d5a4de1b0b63cd233010e63d 2 Github Blog github.tinywan blog.tinywan OK 127.0.0.1:6379>
04、SCRIPT EXISTS
命令用于校验指定的脚本是否已经被保存在缓存当中
127.0.0.1:6379> SCRIPT EXISTS c686f316aaf1eb01d5a4de1b0b63cd233010e63d 1) (integer) 1 127.0.0.1:6379> SCRIPT FLUSH OK 127.0.0.1:6379> SCRIPT EXISTS c686f316aaf1eb01d5a4de1b0b63cd233010e63d 1) (integer) 0 127.0.0.1:6379>
05、SCRIPT KILL
杀死当前正在运行的 Lua 脚本
3、调试
script.lua脚本
local foo = redis.call("ping") return foo
执行脚本
$ redis-cli --eval script.lua PONG
loop.lua脚本
local i = 0 while true do i = i + 1 redis.debug(i) end return "OK"
进入调试模式
$ redis-cli --ldb --eval loop.lua set set , wet set Lua debugging session started, please use: quit -- End the session. restart -- Restart the script in debug mode again. help -- Show Lua script debugging commands. * Stopped at 1, stop reason = step over -> 1 local i = 0 ^C
打开另外一个 shell 窗口
www@iZ2zec3dge6rwz2uw4tveuZ:~$ redis-cli 127.0.0.1:6379> keys * (error) BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE. 127.0.0.1:6379> keys * (error) BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE. 127.0.0.1:6379> keys * (error) BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE. 127.0.0.1:6379> SCRIPT KILL OK 127.0.0.1:6379> keys * 1) "REDIS_CACHE:RESTY_VOD_DETAIL:55"
4、实现分布式锁
使用 PHP 实现分布式锁
<?php /**.------------------------------------------------------------------------------------------------------------------- * | Github: https://github.com/Tinywan * | Blog: http://www.cnblogs.com/Tinywan * |-------------------------------------------------------------------------------------------------------------------- * | Author: Tinywan(ShaoBo Wan) * | DateTime: 2018/9/13 22:28 * | Mail: 756684177@qq.com * | Desc: 使用Redis实现分布式锁 * '------------------------------------------------------------------------------------------------------------------*/ class RedisLock { /** * 获取锁 * @param string $lock_name 锁名 * @param int $acquire_time 重复请求次数 * @param int $lock_timeout 请求超时时间 * @return bool|string */ public static function acquireLock($lock_name, $acquire_time = 3, $lock_timeout = 120) { $identifier = md5($_SERVER['REQUEST_TIME'] . mt_rand(1, 10000000)); $lock_name = 'LOCK:' . $lock_name; $lock_timeout = intval(ceil($lock_timeout)); $end_time = time() + $acquire_time; while (time() < $end_time) { $script = <<<luascript local result = redis.call('setnx',KEYS[1],ARGV[1]); if result == 1 then redis.call('expire',KEYS[1],ARGV[2]) return 1 elseif redis.call('ttl',KEYS[1]) == -1 then redis.call('expire',KEYS[1],ARGV[2]) return 0 end return 0 luascript; $result = location_redis()->evaluate($script, array($lock_name, $identifier, $lock_timeout), 1); if ($result == '1') { return $identifier; } usleep(100000); // 函数延迟代码执行若干微秒 } return false; } /** * 释放锁 * @param string $lock_name 锁名 * @param string $identifier 获取锁返回的标识 * @return bool */ public static function releaseLock($lock_name, $identifier) { $lock_name = 'LOCK:' . $lock_name; while (true) { $script = <<<luascript local result = redis.call('get',KEYS[1]); if result == ARGV[1] then if redis.call('del',KEYS[1]) == 1 then return 1; end end return 0 luascript; $result = location_redis()->evaluate($script, array($lock_name, $identifier), 1); if ($result == 1) { return true; } break; } //进程已经失去了锁 return false; } }
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 【分布式锁】07-Zookeeper实现分布式锁:Semaphore、读写锁实现原理
- 原 荐 分布式锁与实现(二)基于ZooKeeper实现
- 分布式实现原理
- 实现分布式锁
- SOFAJRaft 实现原理:SOFAJRaft-RheaKV 分布式锁实现剖析
- RedLock 实现分布式锁
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Java并发编程实战
Brian Goetz、Tim Peierls、Joshua Bloch、Joseph Bowbeer、David Holmes、Doug Lea / 童云兰 / 机械工业出版社华章公司 / 2012-2 / 69.00元
本书深入浅出地介绍了Java线程和并发,是一本完美的Java并发参考手册。书中从并发性和线程安全性的基本概念出发,介绍了如何使用类库提供的基本并发构建块,用于避免并发危险、构造线程安全的类及验证线程安全的规则,如何将小的线程安全类组合成更大的线程安全类,如何利用线程来提高并发应用程序的吞吐量,如何识别可并行执行的任务,如何提高单线程子系统的响应性,如何确保并发程序执行预期任务,如何提高并发代码的性......一起来看看 《Java并发编程实战》 这本书的介绍吧!