基于Redis的分布式锁实现

284 阅读2分钟

随着业务越来越复杂。应用服务都会朝着分布式、集群方式部署。分布式CAP原则告诉我们,Consistency(一致性) Availability (可用性)、partition tolerance(分区容错性),三者不可得兼。 很多场景中,需要使用分布式事务,分布式锁等技术保证数据最终一致性。有时候,我们需要保证某一个方法同一时刻只能被一个线程执行。 在单机环境中,java提供了很多并发相关API、在多机环境中无能为力了 对于分布式锁,最好能满足以下几点

  1. 可以保证在分布式部署的应用集群中,同一个方法在同一时间只能被一台机器上一个线程执行
  2. 这把锁是一个可重入锁(避免死锁)
  3. 这把锁最好是阻塞锁
  4. 有高可用获取锁释放锁功能
  5. 获取锁释放锁性能摇号 针对分布式锁,目前有一下几种方案 www.hollischuang.com/archives/17…
  6. 基于数据库实现分布式锁
  7. 基于缓存实现分布式锁
  8. 基于zookeeper实现分布式锁

分布式同步锁实现 实现思路 锁的实现主要基于redis的 SETNX命令。 我们来看SET NX解释。 SETNX key value 将key的值设为value 当且仅当key不存在 若给定的key存在,则setnx不作任何动作 setnx 是setif not exists 如果不存在 则set简写 返回值 设置成功 返回1 设置失败 返回0

  1. 使用SETNX 命令获取锁。若返回0 key存在锁存在 则获取失败 繁殖获取成功
  2. 2 为了防止获取锁程序异常 导致其他线程 进程 调用SETNX命令 总是返回0 进入需要为改key设置一个合理的过期时间
  3. 释放锁 使用DEL命令将锁删除

实现过程 创建同步锁实现类

/**
 * 同步锁
 *
 * @property key Redis key
 * @property stringRedisTemplate RedisTemplate
 * @property expire Redis TTL/秒
 * @property safetyTime 安全时间/秒
 */
class SyncLock(
        private val key: String,
        private val stringRedisTemplate: StringRedisTemplate,
        private val expire: Long,
        private val safetyTime: Long
)

key reids中的key,对应java api synchronized的对象 expire reids中key的过期时间 safetyTime 下文介绍其作用 实现锁的获取功能

private val value: String get() = Thread.currentThread().name

/**
 * 尝试获取锁(立即返回)
 *
 * @return 是否获取成功
 */
fun tryLock(): Boolean {
    val locked = stringRedisTemplate.opsForValue().setIfAbsent(key, value) ?: false
    if (locked) {
        stringRedisTemplate.expire(key, expire, TimeUnit.SECONDS)
    }
    return locked
}

参考 segmentfault.com/a/119000001…