最近遇到一个比较奇怪的问题,在秒杀的时候,redis的分布式锁竟然没有锁住,在并发的时候,事务开启的太长,没有拿到相应的数据,从而进行的脏读,以及脏写。我在 为了解决这个问题,运用lua 脚本去操作锁
好了,现在开始 上代码:
1.设置 redis 锁
Boolean setResult =false;
//redis 锁
String key = "lock:exchange"+userId+"commodity"+shopCommodityId;
//uuId 作为 请求id
String requestId = UUID.randomUUID().toString() ;
//进行锁 操作
setResult = distributedLock.lock(key, requestId,60000);
log.info("用户:"+userId+"--锁--返回结果:"+setResult);
2.redis 锁工具类
public interface DistributedLock {
/**
* 获取锁
* @param requestId 请求id uuid
* @param key 锁key
* @param expiresTime 失效时间 ms
* @return 获取锁成功 true
*/
boolean lock(String key,String requestId,int expiresTime);
/**
* 释放锁
* @param key 锁key
* @param requestId 请求id uuid
* @return 成功释放 true
*/
boolean releaseLock(String key,String requestId);
}
public class DistributedLockImpl implements DistributedLock{
public DistributedLockImpl(StringRedisTemplate redisTemplate){
this.redisTemplate = redisTemplate;
}
private StringRedisTemplate redisTemplate;
//lua 脚本编写
private static String lockScript = "if redis.call('setnx',KEYS[1],ARGV[1]) == 1 then return redis.call('expire',KEYS[1],ARGV[2]) else return 0 end";
private static String releaseLockScript = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
@Override
public boolean lock( String key,String requestId, int expiresTime) {
DefaultRedisScript<Long> longDefaultRedisScript = new DefaultRedisScript<>(lockScript, Long.class);
Long result = redisTemplate.execute(longDefaultRedisScript, Collections.singletonList(key), requestId,String.valueOf(expiresTime));
return result == 1;
}
@Override
public boolean releaseLock(String key, String requestId) {
DefaultRedisScript<Long> longDefaultRedisScript = new DefaultRedisScript<>(releaseLockScript, Long.class);
Long result = redisTemplate.execute(longDefaultRedisScript, Collections.singletonList(key), requestId);
return result == 1;
}
}
3.根据返回锁的结果 进行操作;
4.最后进行锁的释放,我建议在 finally 里面进行操作,无论如何都会进行锁的释放。