分布式锁的 redis 实现

#spring-boot#redis

2019-06-03 10:19:06

分布式锁最经典的例子应该是分布式的电商系统里对商品的库存进行加锁了,举个简单的例子,单机时代,我们可能就一台机器,这时候也可能需要锁,为啥呢,多线程啊,所以还是有锁,比如最简单的 synchronize 到了分布式系统,synchronize 已经不够用了。因为不只有一台机器啊,即使 服务器 A 加锁了,服务器 B 可能依旧跑进去了,比如两个人连接到了两台服务器 A、B,然后买同一件商品,即使有 synchronize 第一个人完全可以在 A 下单,第二个人在 B 下单,synchronize 只能保证当前进程,当有多个进程时,就废了。

基于 redis 的 分布式锁

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
private boolean success(Boolean value) {
    return value != null && value;
}

/**
 * 给 key 加锁
 * @param key 要加锁的 key
 * @return true 如果成功 否则 false
 */
public boolean lock(String key, int time) {
    return success(redisTemplate.opsForValue().setIfAbsent(key, "lock", time, TimeUnit.SECONDS));
}

/**
 * 解锁 key
 * @param key 要解锁的 key
 */
public void unlock(String key) {
    if (key == null || key.isEmpty()) return;
    redisTemplate.opsForValue().getOperations().delete(key);
}

其中 setIfAbsent 是在 spring-data-redis 2.1 版本之后新增的,而 Redis 自 2.6.12 版本开始支持 set 方法设置过期时间,如果 redis 版本低于 2.6.12 或者 spring-data-redis 版本低于 2.1,而又不想更新版本,可以使用下面的代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
private boolean success(Boolean value) {
    return value != null && value;
}

public Boolean lock(String key, long timeout) {
    return redisTemplate.execute((RedisCallback<Boolean>) connection -> {
        JedisCommands commands = (JedisCommands)connection.getNativeConnection();
        String result = commands.set(key, "lock", "NX", "PX",
                TimeUnit.SECONDS.toMillis(timeout));
        return "OK".equals(result);
    });
}

public long unlock(String key) {
    if (key == null || key.isEmpty()) return 0;
    return redisTemplate.execute((RedisCallback<Long>) connection -> connection.del(key.getBytes()));
}
最后更新于