【Redis实战】分布式锁

分布式锁

synchronized只能保证单个JVM内部的线程互斥,不能保证集群模式下的多个JVM的线程互斥。

分布式锁原理

每个JVM内部都有自己的锁监视器,但是跨JVM,就会有多个锁监视器,就会有多个线程获取到锁,不能实现多JVM进程之间的互斥。
我们不能使用JVM内部的锁监视器,我们必须让多个JVM去使用同一个锁监视器,所以肯定是一个独立于JVM内部的,多个JVM都可以看到的监视器。
image.png
过程
image.png

特性

image.png

多进程可见

多个JVM都可以看到,比如Redis,MySQL等。JVM外部的基本都可以实现。

互斥

只能有一个人拿到锁

高可用

大多数情况下,获取锁都是成功的,而不是频繁失败

高并发/高性能

加锁本身就会影响性能,会变成串行执行,如果加锁本身也很慢,就不行了。

安全性

异常情况下,比如,获取锁完毕之后,锁无法释放,服务宕机了。
死锁问题等等。

功能性特性

比如是否可重入,阻塞还是非阻塞的,公平还是非公平锁

不同的分布式锁区别

image.png

MySQL

  • 互斥:通过事务的互斥锁来实现,事务提交锁释放,异常事务回滚
  • 高可用:依赖MySQL本身的高可用
  • 高性能:受限于MySQL的性能
  • 安全性:通过事务获取锁,断开链接的时候,锁会自动释放

Redis

  • 互斥:通过setnx互斥命令来实现互斥
  • 高可用:Redis本身可以实现主从和集群模式,可用性高
  • 高性能:较高
  • 安全性:服务出现故障,锁无法释放,死锁,可以利用key的过期机制来实现

Zookeeper

  • 互斥:利用内部节点的唯一性和有序性来实现,每个节点的id都是自增的,删除节点,另外一个节点就说最小的了
  • 高可用:支持集群
  • 高性能:保证强一致性,主从之间数据同步会消耗一定时间
  • 安全性:创建的是临时节点,服务宕机,锁会自动释放

Redis实现分布式锁

分布式锁需要实现两个最基本的方法

获取锁

互斥

确保只能有一个线程执行成功。通过redis的setnx命令来实现,同时执行时,只有1个能执行成功,实现互斥。

#获取锁
setnx key value

image.png

  • 添加锁的过期时间,避免服务宕机引起死锁。过期时间需要注意,业务还没处理完但是锁过期的问题
#设置过期时间
expire key 10

image.png
为了避免出现,setnx后,expire之前,服务宕机的问题,我们将两条命令合并为一条,保证原子性

#添加锁 nx是互斥,ex是过期时间
set key value ex 10 nx
#或者
set key value nx ex 10

image.png

非/阻塞式获取锁

获取锁成功返回ok,失败返回nil,如果失败了,有两种解决方案,jdk中,有两种方案:一直阻塞式等待,另一种,获取锁失败即刻返回。
非阻塞式获取锁,尝试一次,成功返回true,失败返回false!

释放锁

手动释放

手动删除即可

#释放锁
del key

image.png

超时释放

获取锁时,添加一个超时时间,避免出现服务宕机,锁无法被释放

流程

image.png

分布式锁初级版

执行流程

image.png

分布式锁代码

接口

/**
 * 分布式锁
 *
 * @author zhangzengxiu
 * @date 2023/10/9
 */
public interface ILock {
   
   

    /**
     * 尝试去获取锁
     *
     * @param timeoutSc 过期时间,过期锁自动释放
     * @return 获取成功返回true,失败返回false
     */
    boolean tryLock(long timeoutSc);

    /**
     * 释放锁
     */
    void unlock();
}

实现

import org.springframework.data.redis.core.StringRedisTemplate;

import java.util.concurrent.TimeUnit;

/**
 * @author zhangzengxiu
 * @date 2023/10/9
 */
public class SimpleRedisLock implements ILock {
   
   

    private StringRedisTemplate stringRedisTemplate;

    /**
     * 锁统一前缀
     */
    public static final String KEY_PRE = "lock:";

    /**
     * 业务名称
     */
    private String name;

    public SimpleRedisLock(StringRedisTemplate stringRedisTemplate, String name) {
   
   
        this.stringRedisTemplate = stringRedisTemplate;
        this.name = name;
    }

    @Override
    public boolean tryLock(long timeoutSc) {
   
   
        //获取线程标识
        long threadId = Thread.currentThread().getId();
        String key = KEY_PRE + name;
        String value = String.valueOf(threadId);
        Boolean res = stringRedisTemplate.opsForValue().setIfAbsent(key,
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值