参考:
https://www.ogeek.net/article/212010.htm
https://www.ogeek.net/article/179946.htm
https://www.jianshu.com/p/76bc0e963172
https://www.letianbiji.com/redis/redis-lua.html
https://www.ogeek.net/article/148833.htm
Redis从2.6版本开始引入对Lua脚本的支持,通过在服务器中嵌入Lua环境,Redis客户端可以使用Lua脚本,直接在服务端原子的执行多个Redis命令。
其中,使用EVAL命令可以直接对输入的脚本进行求值:
1
2
|
redis>EVAL "return 'hello world'" 0
"hello world"
|
使用脚本的好处如下:
1.减少网络开销:本来5次网络请求的操作,可以用一个请求完成,原先5次请求的逻辑放在redis服务器上完成。使用脚本,减少了网络往返时延。
2.原子操作:Redis会将整个脚本作为一个整体执行,中间不会被其他命令插入。
3.复用:客户端发送的脚本会永久存储在Redis中,意味着其他客户端可以复用这一脚本而不需要使用代码完成同样的逻辑。
第一步:redis配置
import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.fasterxml.jackson.module.paramnames.ParameterNamesModule; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration @EnableCaching //引入缓存 public class RedisConfig {
/** * RedisTemplate配置 * @param redisConnectionFactory * @return */ @Primary @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { // 设置序列化 Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>( Object.class); ObjectMapper om = new ObjectMapper() .registerModule(new ParameterNamesModule()) .registerModule(new Jdk8Module()) .registerModule(new JavaTimeModule()); // new module, NOT JSR310Module;; om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); // 配置redisTemplate RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>(); redisTemplate.setConnectionFactory(redisConnectionFactory); RedisSerializer<?> stringSerializer = new StringRedisSerializer(); // key序列化 redisTemplate.setKeySerializer(stringSerializer); // value序列化 redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); // Hash key序列化 redisTemplate.setHashKeySerializer(stringSerializer); // Hash value序列化 redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer); redisTemplate.afterPropertiesSet(); return redisTemplate; }
}
第二步:写lua脚本
redis_lock4.lua的内容:
if redis.call('setnx',KEYS[1],ARGV[1]) == 1 then return redis.call('expire',KEYS[1],ARGV[2]) else return -1000 end
解释 :setnx命令, 设置 KEYS[1]的值为ARGV[1], 并设置KEYS[1]的过期时间为ARGV[2]。 如设置prize_stock的值为100,并设置prize_stock的过期时间为200秒。
第三步:代码
Long result = null;
try { //调用lua脚本并执行 DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(); redisScript.setResultType(Long.class);//返回类型是Long //lua文件存放在resources目录下的redis文件夹内 redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("redis/redis_lock4.lua"))); result = redisTemplate.execute(redisScript, Arrays.asList("prize_stock"), 100, 300); System.out.println("lock==" + result); } catch (Exception e) { e.printStackTrace(); }
解释:
Arrays.asList("prize_stock") 为KEYS, lua脚本对应 KEYS[1]
100, 300为参数,lua脚本对应ARGV[1] 和 ARGV[2]。
--------------------------------------------------------- 仅记录,没用: 加锁:
if redis.call('setnx',KEYS[1],ARGV[1]) == 1 then return redis.call('expire',KEYS[1],ARGV[2]) else return -1000 end
解锁:
if redis.call("exists",KEYS[1]) == 0 then return 100 end
if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 200 end 扣库存:
if (redis.call('exists', KEYS[1]) == 1) then local stock = tonumber(redis.call('get', KEYS[1])) local num = tonumber(ARGV[1]) if (stock >= num) then return redis.call('incrby', KEYS[1], 0 - num) end end return -100
|
请发表评论