理论系列-分布式锁-简单了解

理论系列-分布式锁-简单了解

1. 是什么

锁,在多线程环境中控制对资源的并发访问,比如synchronized、Lock等

分布式锁,synchronized和Lock不只能本地加锁,或者说单机部署环境下的锁,在(单个服务)集群部署或者多服务完成一个接口时,需要用到分布式锁。

2. 应用场景

  • 互联网秒杀

  • 抢优惠券

3. 常见实现技术

MySQL

使用这么一个表:

作用
id
resource_name锁定的资源名
node_info机器信息
count重入次数
desc
create_time
update_time

上锁:resource_name,排它锁for update

解锁:resource_name查询到记录,然后机器信息验证通过,count–,等于0则删除

Zookeeper

Curator已经封装好了分布式锁的功能。

关键点:临时(有序)节点、watch(无需处理超时,机器宕机则自动删除节点)

Redis

setNx

setNx+Ex

Redisson已经封装好了分布式锁的功能。

关键点:原子命令

对比

-MySQLZKRedis

4. 关键

  • 互斥性:只有一个线程获取锁
  • 有效性:避免机器宕机(获取锁的时间,大于过期时间,需更新过期时间,防止业务未完成被释放,常见于Redis)
  • 重入性
  • 释放锁:上锁线程才有权释放锁
  • 可选:阻塞、公平

5. 不安全

  • GC的STW
  • 时钟跳跃
  • 长时间网络IO

6. Redis demo

场景:秒杀。提供一个接口,成功则库存-1,库存数保存在redis

1. 无锁

用户获取当前库存数,够则扣除1,否则结束

问题:多个用户访问?见2. 本地锁

@GetMapping("stock1")
@ResponseBody
public String stock() {
    int curStock = Integer.parseInt(redisTemplate.opsForValue().get(KEY));
    if (curStock > 0) {
        int newStock = curStock - 1;
        redisTemplate.opsForValue().set(KEY, String.valueOf(newStock));
        System.out.println("扣除库存成功,剩余库存:" + newStock);
    } else {
        System.out.println("库存不足");
    }

    return "end";
}

2. 本地锁

多个用户访问,一个一个秒杀成功,直至没有库存数

问题:集群部署,用户A访问实例1,用户B访问实例1,如果剩余1个库存,可能AB都能秒杀成功

@GetMapping("stock2")
@ResponseBody
public String stock() {
    synchronized (this) {
        int curStock = Integer.parseInt(redisTemplate.opsForValue().get(KEY));
        if (curStock > 0) {
            int newStock = curStock - 1;
            redisTemplate.opsForValue().set(KEY, String.valueOf(newStock));
            System.out.println("扣除库存成功,剩余库存:" + newStock);
        } else {
            System.out.println("库存不足");
        }
    }

    return "end";
}

3. Redis原生锁

前提:原子API,加锁并设置过期时间

第一点:用户获取锁,宕机。超时时间

第二点:业务时间 > 超时时间。定时器,定期更新

第三点:上锁和解锁为同一个线程

// 原子,同时完成加锁和过期时间设置
Boolean setIfAbsent(K key, V value, long timeout, TimeUnit unit);
// 定时器,开个线程完成即可
// 上解锁,添加一个唯一标示,一般UUID即可

实现略。

4. Redisson分布式锁

@GetMapping("stock4")
@ResponseBody
public String stock4() {
    RLock rLock = null;
    try {
        rLock = redisson.getLock(LOCK);
        rLock.lock();
        // 如果使用lock(int, int),不会自动更新超时时间
        //            try {
        //                Thread.sleep(35*1000);
        //            } catch (Exception e) {
        //                e.printStackTrace();
        //            }

        int curStock = Integer.parseInt(redisTemplate.opsForValue().get(KEY));
        if (curStock > 0) {
            int newStock = curStock - 1;
            redisTemplate.opsForValue().set(KEY, String.valueOf(newStock));
            System.out.println("扣除库存成功,剩余库存:" + newStock);
        } else {
            System.out.println("库存不足");
        }
    } finally {
        if (rLock != null) {
            rLock.unlock();
        }
    }

    return "end";
}

参考

https://juejin.im/post/5bbb0d8df265da0abd3533a5

https://www.bilibili.com/video/BV1d4411y79Y?from=search&seid=4094813974568007126

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值