实例代码地址,请前往:https://gitee.com/GuoqingLee/distributed-seckill
redis官方文档地址,请前往:http://www.redis.cn/topics/distlock.html
前言
关于分布式锁的实现,目前主流方案有以下三类:
1、基于数据库的乐观锁;
2、基于redis实现的锁服务;
3、基于zookeeper的实现;
本文注释基于redis实现分布式锁,一种是最普通的实现方式,另外一种是redis官网推荐的实现方式redission实现,接下看看实现代码
一、普通实现方式
普通实现方式是基于以下两个命令
#使用redis中的setnx命令创建一个key,成功创建就是加锁
SET resource_name my_random_value NX PX 30000
#删除锁的时候,找到 key 对应的 value,跟自己传过去的 value 做比较,如果是一样的才删除。
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
依赖引入
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
代码实现
/**
* @author 2019/7/7 15:49
*/
public class RedisLocker {
@Autowired
private JedisPool jedisPool;
/**
* 获取锁
*
* @param key
* @param value
* @param leaseTime 过期时间
* @return
*/
public boolean tryLock(String key, String value, long leaseTime) {
try (Jedis jedis = jedisPool.getResource()) {
if (jedis == null) {
return false;
}
String result = jedis.set(key, value, "NX", "PX", leaseTime);
return "OK".equalsIgnoreCase(result);
} catch (Exception e) {
return false;
}
}
/**
* 释放锁
* @param key
* @param value
* @return
*/
public int unlock(String key, String value) {
try (Jedis jedis = jedisPool.getResource()) {
if (jedis == null) {
return 0;
}
StringBuilder builder = new StringBuilder();
builder.append("if redis.call('get','")
.append(key)
.append("')")
.append("=='")
.append(" then ")
.append(" return redis.call('del','")
.append(key)
.append("')")
.append(" else ")
.append(" return 0")
.append(" end");
return Integer.valueOf(jedis.eval(builder.toString()).toString());
}catch (Exception e){
return 0;
}
}
}
结果请自行测试
二、基于redission实现
依赖引入
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.10.1</version>
</dependency>
配置文件
#redis config
spring.redis.database=
spring.redis.host=
spring.redis.port=
spring.redis.password=
spring.redis.ssl=
spring.redis.timeout=
spring.redis.cluster.nodes=
spring.redis.sentinel.master=
spring.redis.sentinel.nodes=
代码实现
/**
* 基于redission的分布式实现
*
* @author 2019/7/7 15:20
*/
@Component
public class RedissonDistributedLocker {
@Autowired
private RedissonClient redissonClient;
/**
* 加锁
*
* @param lockKey
* @return
*/
public RLock lock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock();
return lock;
}
/**
* 加锁,过期自动释放
*
* @param lockKey
* @param leaseTime
* @return
*/
public RLock lock(String lockKey, long leaseTime) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock(leaseTime, TimeUnit.SECONDS);
return lock;
}
/**
* 加锁,过期自动释放,传入时间单位
*
* @param lockKey
* @param leaseTime
* @param unit
* @return
*/
public RLock lock(String lockKey, long leaseTime, TimeUnit unit) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock(leaseTime, unit);
return lock;
}
/**
* 尝试获取锁,传入时间单位
*
* @param lockKey
* @param unit
* @param waitTime
* @param leaseTime
* @return
*/
public boolean tryLock(String lockKey, TimeUnit unit, long waitTime, long leaseTime) {
RLock lock = redissonClient.getLock(lockKey);
try {
return lock.tryLock(waitTime, leaseTime, unit);
} catch (InterruptedException e) {
return false;
}
}
/**
* 尝试获取锁
*
* @param lockKey
* @param waitTime
* @param leaseTime
* @return
*/
public boolean tryLock(String lockKey, long waitTime, long leaseTime) {
RLock lock = redissonClient.getLock(lockKey);
try {
return lock.tryLock(waitTime, leaseTime, TimeUnit.SECONDS);
} catch (InterruptedException e) {
return false;
}
}
/**
* 释放锁,传入key
*
* @param lockKey
*/
public void unlock(String lockKey) {
redissonClient.getLock(lockKey).unlock();
}
/**
* 释放锁,传入lock
*
* @param lock
*/
public void unlock(RLock lock) {
lock.unlock();
}
}
结果测试
#方式一
@Autowired
private RedissonDistributedLocker redissonDistributedLocker;
String lockKey = "BM_MARKET_SECKILL_" + stallActivityId;
try {
//超过2S自动释放锁
redissonDistributedLocker.lock(lockKey, 2L);
//业务处理
} finally {
redissonDistributedLocker.unlock(lockKey); //释放锁
}
#方式二
@Autowired
private RedissonDistributedLocker redissonDistributedLocker;
public void test() throws InterruptedException {
final int[] counter = {0};
for (int i= 0; i < 100; i++){
new Thread(new Runnable() {
@Override
public void run() {
boolean isGetLock = redissonDistributedLocker.tryLock("test0001", 3L, 1L);
if(isGetLock) {
try {
int a = counter[0];
counter[0] = a + 1;
logger.info(a + "");
} finally {
redissonDistributedLocker.unlock("test0001");
}
}
}
}).start();
}
// 主线程休眠,等待结果
Thread.sleep(5000);
System.out.println(counter[0]);
logger.info(counter[0] + "");
}
三、redis 分布式锁和 zk 分布式锁的对比
- redis 分布式锁,其实需要自己不断去尝试获取锁,比较消耗性能。
- zk 分布式锁,获取不到锁,注册个监听器即可,不需要不断主动尝试获取锁,性能开销较小。
另外一点就是,如果是 redis 获取锁的那个客户端 出现 bug 挂了,那么只能等待超时时间之后才能释放锁;而 zk 的话,因为创建的是临时 znode,只要客户端挂了,znode 就没了,此时就自动释放锁。
redis 分布式锁大家没发现好麻烦吗?遍历上锁,计算时间等等......zk 的分布式锁语义清晰实现简单。