127.0.0.1:6379> sadd lottery u1 u2 u3 u4 u5 u6 u7(integer) 7127.0.0.1:6379> sadd lottery u1(integer) 0

如果没有lottey这个key就新建该key,有就直接添加并返回成功添加的元素个数。
同时你会创造如果凑集中存在了添加的元素是无法被再次添加的。

查询凑集中的元素

查询所有元素通过SMEMBERS命令。

127.0.0.1:6379> smembers lottery1) "u2"2) "u7"3) "u6"4) "u4"5) "u1"6) "u3"7) "u5"随机抽取N个元素

SET凑集有两个命令都能知足随机抽取N个元素,分别是SPOP和SRANDMEMBER,它们的差异在于SPOP会将选中的元素从原来的凑集中剔除,而SRANDMEMBER不会。
我们分别来利用这两个命令来随机从lottery中抽取2个元向来看看。

phpredis高并发抽奖Redis联合Lua剧本实现抽奖逻辑 HTML

127.0.0.1:6379> srandmember lottery 21) "u2"2) "u4"127.0.0.1:6379> smembers lottery1) "u2"2) "u7"3) "u6"4) "u4"5) "u1"6) "u3"7) "u5"127.0.0.1:6379> spop lottery 21) "u3"2) "u5"127.0.0.1:6379> smembers lottery1) "u2"2) "u7"3) "u6"4) "u4"5) "u1"

就lottery来说,如果你的奖池人数一次性添加的不再增加利用SPOP;如果动态添加,为了担保中奖的人不再次进入奖池该当利用SRANDMEMBER。

抽奖脚本

接下来便是抽奖脚本,我们从lottery中抽出特定的人放入中奖名单,其余一个凑集chosen中。

按道理Redis抽奖脚本在Lua中该当是这样的:

function draw(KEYS,ARGV) -- 抽奖逻辑 函数体 end

但是我们只须要编写抽奖逻辑的函数体,然后把函数体写入.lua文件中,在Maven项目中放入META-INF/scripts文件夹中,如图所示:

约定lua脚本所在的目录

draw.lua的逻辑为:

--- 大略抽奖脚本 return 结果终极通报给Java 运用-- 奖池的keylocal lottery_key = KEYS[1]-- 中奖名单的keylocal chosen_key = KEYS[2]-- 预定抽奖的人数local lottery_count = ARGV[1]-- 如果预定抽奖的人数大于0才开始抽奖if tonumber(lottery_count) > 0 then -- 奖池中抽奖 返回的是 被抽中的人组成的数组 local chosen_list = redis.call('SRANDMEMBER', lottery_key, lottery_count); -- 将抽中的人添加到中奖名单中 返回中奖的人数 if chosen_list then return redis.call('SADD', chosen_key, unpack(chosen_list)) else return 0 endelse return 0end

这里的逻辑仅仅为了演示用,实际上要根据你的业务进行编写,lua干系的语法请参考上一文。

3. 对应的 Java 代码

Spring Data Redis中的RedisTemplate供应了execute方法来实行Lua脚本,这里我选择利用下面的方法:

@Overridepublic <T> T execute(RedisScript<T> script, List<K> keys, Object... args) { return scriptExecutor.execute(script, keys, args);}RedisScript Redis脚本的抽象,用来加载脚本。
keys对应Lua脚本中的KEYS,用来传入Redis的KEY,在Lua脚本中可以通过 KEYS[索引]来取值,例如取第一个值KEYS[1]。
args用来向Lua脚本通报其它的参数,在Lua脚本中可以通过ARGV[索引]来取值。

我们利用draw.lua脚本从Redis的lottery凑集中抽取5名幸运者并把他们添加到中奖名单chosen凑集中:

RedisScript<Long> redisScript = RedisScript.of(new ClassPathResource("META-INF/scripts/draw.lua"), Long.class);Long chosenCount = stringRedisTemplate.execute(redisScript, Arrays.asList("lottery", "chosen"), Collections.singletonList("5"));

布局RedisScript工具时务必指定返回值工具以担保Lua脚本工具和Java的返回值能对应上,否则将涌现非常。
拜会org.springframework.data.redis.connection.ReturnType列举。

4. 总结

到此Redis利用Lua脚本进行抽奖的整套逻辑就完成了。
Lua脚本在Redis中常日是为了担保高并发下的原子性,当你考虑是否须要利用它时该当充分考虑你的业务和架构是否适宜利用它,而非为了“炫技”。
好了本日的分享就到这里,我是:码农小胖哥 多多关注,更多干货分享。

点击理解更多获取更多编程干货