Redis是高性能的key-value数据库,在很大程度克服了memcached这类key/value存储的不足,在部分场景下,是对关系数据库的良好补充。得益于超高性能和丰富的数据结构,Redis已成为当前架构设计中的首选key-value存储系统。java
虽然Redis官网上提供了200多个命令,但作程序设计时仍是避免不了为了实现一小步业务逻辑而屡次调用Redis的状况。数据库
以compare and set场景为例。若是使用Redis原生命令,须要从Redis中获取这个key,而后提取其中的值进行比对:若是相等就不作处理;若是不相等或者key不存在则将key设置成目标值。仅仅一个单点的compare and set操做就须要与Redis通信两次。数组
此外,这种分散操做没法利用Redis的原子特性,占用屡次网络IO。缓存
今天咱们就来探讨一下如何优雅地应对上述场景。服务器
在介绍Lua以前,咱们须要先对这个语言有个初步了解。Lua 是一个小巧的脚本语言,几乎能够运行在全部操做系统和平台上。咱们通常不会用Lua处理特别复杂的事务,所以只需了解一些lua的基本语法便可。网络
Redis问世以后,其开发者也意识到了开篇提到的问题,所以Redis从2.6版本开始支持Lua脚本。新版本的Redis还支持Lua Script debug,感兴趣的小伙伴能够去官网的Documentation中找到对应介绍和QuickStart。数据结构
有了Lua脚本以后,使用Redis程序时便可以在如下方面实现显著提高:架构
因此如今流传一句话:要想学好Redis,必会Lua Script。运维
接下来咱们就实现一个简单的compare and set,并经过这个例子感觉一下Lua脚本给Redis使用带来的全新体验。分布式
首先看一下如何让Redis执行Lua脚本。
Redis 127.0.0.1:6379> EVAL script numkeys key [key ...] arg [arg ...]
这里借用一下官网的例子。
上述脚本直接返回了入参。
你们能够简单地将KEYS[1],KEYS[2], ARGV[1],ARGV[2]理解为占位符。
若是只能在命令行中写脚本执行,遇到复杂的脚本程序岂不是会抓狂?
下面咱们来看一下,如何让Redis执行Lua脚本文件,同时也验证一下lua脚本的复用特性(之后咱们不再须要按期批量删除某些符合特定规则的key了)。
Redis 127.0.0.1:6379> SCRIPT LOAD script Redis 127.0.0.1:6379> EVALSHA sha1 numkeys key [key ...] arg [arg ...]
Redis提供了一个SCRIPTLOAD命令,命令后面的script即为Lua脚本。命令将脚本script添加到脚本缓存中,但并不当即执行这个脚本。执行命令后,Redis会返回一个SHA1串,第二个EVALSHA命令便可执行。
须要注意的是,脚本能够在缓存中保留无限长的时间,直到执行完SCRIPT FLUSH。咱们来看一下效果。
Redis还支持直接执行Lua脚本文件。首先编写并存储一个Lua脚本。
而后调用Redis-cli –eval命令
Redis-cli –eval命令语法基本与原eval语法相同。
compareand set的实现逻辑是这样的:首先获取Redis中指定key的value,而后与给定值进行比较:若是相等,则将key设定为目标值并返回一个标识符;若是不相等,则不做任何操做并返回一个标识符。
if Redis.call('get', KEYS[1]) == ARGV[1] then Redis.call('set', KEYS[1], ARGV[2]); return 1 else return 0 end
下面咱们来测试一下这个脚本。
首先向Redis的指定key compareAndSet:key写入一个值value
在Redis中执行lua脚本
能够看到第一次执行返回1,说明修改为功了;再使用原参数执行时返回0,说明没有作任何修改。咱们再查询一下compareAndSet:key这个key
能够看到compareAndSet:key这个key已经被修改成new_value了。
咱们经过lua脚本实现了一个简单的compareAndSet操做。
下面咱们经过这个例子来验证一下开篇提到的特性。
读到这里,但愿你已经对Redis+Lua有了必定的了解,并能使用脚本完成一些简单的复合操做。后续还会继续更新一些基于Lua脚本+java程序实现的分布式数据结构,如延迟队列、可重入锁等,感兴趣的小伙伴能够持续关注。
做者:李崇
原文首发 UAVStack智能运维
来源:宜信技术学院