大数据-50 Redis 分布式锁 乐观锁 Watch SETNX Lua Redisson分布式锁 Java实现分布式锁

本文涉及的产品
云原生大数据计算服务MaxCompute,500CU*H 100GB 3个月
简介: 大数据-50 Redis 分布式锁 乐观锁 Watch SETNX Lua Redisson分布式锁 Java实现分布式锁

点一下关注吧!!!非常感谢!!持续更新!!!

目前已经更新到了:

Hadoop(已更完)

HDFS(已更完)

MapReduce(已更完)

Hive(已更完)

Flume(已更完)

Sqoop(已更完)

Zookeeper(已更完)

HBase(已更完)

Redis (正在更新…)

章节内容

上节我们完成了:


Redis缓存相关的概念

缓存穿透、缓存击穿、数据不一致性等

HotKey、BigKey等问题

针对上述问题提出一些解决方案

乐观锁介绍

乐观锁基于CAS(Compare And Swap)思想,比较和替换,是不具有互斥性,不会产生锁等待而消耗资源,但需要反复的重试,能比较快的响应。

Watch实现

watch介绍

我们可以使用 Redis 来实现乐观锁:


利用 Redis 的 watch 功能,监控 Redis-Key的状态值

获取 RedisKey 的值

创建 Redis 事务

给这个Key的值+1

然后去执行这个事务,如果 key 的值被修改过则修改,key不加1

wacth实现

暂时就先忽略编码规范的内容,就先实现即可。

具体编写逻辑如下:

public class Test02 {

    public static void main(String[] args) {
        String redisKey = "lock";
        ExecutorService executor = Executors.newFixedThreadPool(20);
        try {
            Jedis jedis = new Jedis("h121.wzk.icu", 6379);
            jedis.del(redisKey);
            jedis.set(redisKey, "0");
            jedis.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

        for (int i = 0; i < 300; i ++) {
            executor.execute(() -> {
                Jedis jedis = null;
                try {
                    jedis = new Jedis("h121.wzk.icu", 6379);
                    jedis.watch(redisKey);
                    String redisValue = jedis.get(redisKey);
                    int value = Integer.valueOf(redisValue);
                    String userInfo = UUID.randomUUID().toString();
                    if (value < 20) {
                        Transaction tx = jedis.multi();
                        tx.incr(redisKey);
                        List<Object> list = tx.exec();
                        if (list != null && !list.isEmpty()) {
                            System.out.println("获取锁成功, 用户信息: " + userInfo + " 成功人数: " + (value + 1));
                        }
                    } else {
                        System.out.println("秒杀结束!");
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    if (null != jedis) {
                        jedis.close();
                    }

                }
            });
        }
        executor.shutdown();
    }

}

运行之后,会看到已经在进行争抢了:

获取锁成功, 用户信息: e6e06770-f274-4d89-8369-65babc2e3073 成功人数: 1
获取锁成功, 用户信息: 2cc2803b-085e-47ee-9fe6-4bbe1f694fd5 成功人数: 2
获取锁成功, 用户信息: 525ad22c-abb2-4f94-868a-cca981f9d768 成功人数: 3
获取锁成功, 用户信息: 9af67396-798e-4e09-b524-6ddc5e1673ec 成功人数: 4
获取锁成功, 用户信息: d5aa82f4-7d25-42c1-b8db-01ff7cfaf6c6 成功人数: 5
获取锁成功, 用户信息: 7dcc0646-e7a0-4cc0-bdcc-b96c7e8ba98b 成功人数: 6
获取锁成功, 用户信息: 7c9276d0-eec9-462a-8a8b-87711406375b 成功人数: 8
获取锁成功, 用户信息: c43b0158-b211-4a91-b430-51eb6ef74ded 成功人数: 9
获取锁成功, 用户信息: 9ab9418f-5e52-4d28-9ea5-92bc6b8b7742 成功人数: 7
获取锁成功, 用户信息: 7692d829-f7ef-4e28-90a4-2222a14c45d4 成功人数: 11
获取锁成功, 用户信息: 52695f97-49bf-4a06-bc45-a8ee1abb4524 成功人数: 10
获取锁成功, 用户信息: 196e29cc-b2fe-4356-841c-1f4376e3d5ae 成功人数: 12
获取锁成功, 用户信息: 8bb39e3c-c751-4468-b948-50ccb6aeb533 成功人数: 13
获取锁成功, 用户信息: d9691236-13f0-452b-b765-bc15b094866b 成功人数: 14
获取锁成功, 用户信息: cb1b0291-de78-4779-b4e6-294121393e9f 成功人数: 15
获取锁成功, 用户信息: dc368684-533f-47b0-9847-3fbfbf8fee78 成功人数: 16
获取锁成功, 用户信息: 361d2d66-cb9d-4e79-9c85-19f1b83c136d 成功人数: 17
获取锁成功, 用户信息: bd6fe63f-e48a-48f1-b751-e091d19886a2 成功人数: 19
秒杀结束!
秒杀结束!
秒杀结束!
秒杀结束!
获取锁成功, 用户信息: dba287f8-65f0-4da8-a131-05304164b3aa 成功人数: 18
秒杀结束!
获取锁成功, 用户信息: 05c5c5f9-f9cd-48b3-a266-c4ff3f256814 成功人数: 20
秒杀结束!
秒杀结束!
秒杀结束!

SETNX

setnx介绍

共享资源互斥

共享资源串行化

单应用中使用锁:单进程但是多线程

synchronized、ReentrantLock

分布式应用中的锁:多进程多线程

分布式锁是控制分布式系统之间同步访问共享资源的一种方式

利用Redis的单线程特性对共享资源进行串行化处理

SETNX实现

获取锁方式1 SET

public boolean getLock(String lockKey,String requestId,int expireTime) {
    // NX:保证互斥性
    // hset 原子性操作 只要lockKey有效 则说明有进程在使用分布式锁
    String result = jedis.set(lockKey, requestId, "NX", "EX", expireTime);
    if("OK".equals(result)) {
        return true;
    }
    return false;
}

获取锁方式2 SETNX

public boolean getLock(String lockKey,String requestId,int expireTime) {
    Long result = jedis.setnx(lockKey, requestId);
    if(result == 1) {
        // 成功设置 进程down 永久有效 别的进程就无法获得锁
        jedis.expire(lockKey, expireTime);
        return true;
    }
    return false;
}

释放锁方式1 del

注意,当调用del方法时候,如果这把锁已经不属于当前客户端了,比如已经过期了,而别的人拿到了这把锁,此时删除就会导致释放掉了别人的锁。

public static void releaseLock(String lockKey,String requestId) {
    if (requestId.equals(jedis.get(lockKey))) {
        jedis.del(lockKey);
    }
}


释放锁方式2 lua

public static boolean releaseLock(String lockKey, String requestId) {
    String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return
    redis.call('del', KEYS[1]) else return 0 end";
    Object result = jedis.eval(script, Collections.singletonList(lockKey),
    Collections.singletonList(requestId));
    if (result.equals(1L)) {
        return true;
    }
    return false;
}

Redisson分布式

Redisson介绍

  • Redisson是假设在Redis基础上的Java驻内存数据网格(In-Memory Data Grid)
  • Redisson是基于NIO的Netty框架上,生产环境使用分布式锁。

添加依赖

<dependency>
  <groupId>org.redisson</groupId>
  <artifactId>redisson</artifactId>
  <version>2.7.0</version>
</dependency>

配置Redisson

public class RedissonManager {

    private static final Config CONFIG = new Config();

    private static Redisson redisson = null;

    static {
        CONFIG
                .useClusterServers()
                .setScanInterval(2000)
                .addNodeAddress("redis://h121.wzk.icu:6379")
                .addNodeAddress("redis://h122.wzk.icu:6379")
                .addNodeAddress("redis://h123.wzk.icu:6379");
        redisson = (Redisson) Redisson.create(CONFIG);
    }

    public static Redisson getRedisson() {
        return redisson;
    }

}

获取与释放锁

public class DistributedRedisLock {

    private static Redisson redisson = RedissonManager.getRedisson();

    private static final String LOCK_TITLE = "redisLock_";

    public static boolean acquire(String lockName) {
        String key = LOCK_TITLE  + lockName;
        RLock rLock = redisson.getLock(key);
        rLock.lock(3, TimeUnit.SECONDS);
        return true;
    }

    public static void release(String lockName) {
        String key = LOCK_TITLE  + lockName;
        RLock rLock = redisson.getLock(key);
        rLock.unlock();
    }

}

业务使用

public String discount() throws IOException{
    String key = "lock001";
    // 加锁
    DistributedRedisLock.acquire(key);
    // 执行具体业务逻辑
    dosoming
    // 释放锁
    DistributedRedisLock.release(key);
    // 返回结果
    return soming;
}

实现原理

分布式锁特性

  • 互斥性:任意时刻,只能有一个客户端获取锁,不能同时有两个客户端获取到锁。
  • 同一性:锁只能被持有该锁客户端删除,不能由其他客户端删除
  • 可重入性:持有某个客户端可持续对该锁加锁 实现锁的续租
  • 容错性:超过生命周期会自动进行释放,其他客户端可以获取到锁

常见分布式锁对比

相关实践学习
基于MaxCompute的热门话题分析
Apsara Clouder大数据专项技能认证配套课程:基于MaxCompute的热门话题分析
目录
相关文章
|
6月前
|
NoSQL 调度 Redis
分布式锁—3.Redisson的公平锁
Redisson公平锁(RedissonFairLock)是一种基于Redis实现的分布式锁,确保多个线程按申请顺序获取锁,从而实现公平性。其核心机制是通过队列和有序集合管理线程的排队顺序。加锁时,线程会进入队列并等待,锁释放后,队列中的第一个线程优先获取锁。RedissonFairLock支持可重入加锁,即同一线程多次加锁不会阻塞。新旧版本在排队机制上有所不同,新版本在5分钟后才会重排队列,而旧版本在5秒后就会重排。释放锁时,Redisson会移除队列中等待超时的线程,并通知下一个排队的线程获取锁。通过这种机制,RedissonFairLock确保了锁的公平性和顺序性。
|
4月前
|
NoSQL Java Redis
基于Redisson和自定义注解的分布式锁实现策略。
在实现分布式锁时,保证各个组件配置恰当、异常处理充足、资源清理彻底是至关重要的。这样保障了在分布布局场景下,锁的正确性和高效性,使得系统的稳健性得到增强。通过这种方式,可以有效预防并发环境下的资源冲突问题。
264 29
|
5月前
|
缓存 NoSQL 算法
高并发秒杀系统实战(Redis+Lua分布式锁防超卖与库存扣减优化)
秒杀系统面临瞬时高并发、资源竞争和数据一致性挑战。传统方案如数据库锁或应用层锁存在性能瓶颈或分布式问题,而基于Redis的分布式锁与Lua脚本原子操作成为高效解决方案。通过Redis的`SETNX`实现分布式锁,结合Lua脚本完成库存扣减,确保操作原子性并大幅提升性能(QPS从120提升至8,200)。此外,分段库存策略、多级限流及服务降级机制进一步优化系统稳定性。最佳实践包括分层防控、黄金扣减法则与容灾设计,强调根据业务特性灵活组合技术手段以应对高并发场景。
1547 7
|
6月前
|
NoSQL 调度 Redis
分布式锁—5.Redisson的读写锁
Redisson读写锁(RedissonReadWriteLock)是Redisson提供的一种分布式锁机制,支持读锁和写锁的互斥与并发控制。读锁允许多个线程同时获取,适用于读多写少的场景,而写锁则是独占锁,确保写操作的互斥性。Redisson通过Lua脚本实现锁的获取、释放和重入逻辑,并利用WatchDog机制自动续期锁的过期时间,防止锁因超时被误释放。 读锁的获取逻辑通过Lua脚本实现,支持读读不互斥,即多个线程可以同时获取读锁。写锁的获取逻辑则确保写写互斥和读写互斥,即同一时间只能有一个线程获取写锁,
360 17
|
NoSQL 安全 调度
【📕分布式锁通关指南 10】源码剖析redisson之MultiLock的实现
Redisson 的 MultiLock 是一种分布式锁实现,支持对多个独立的 RLock 同时加锁或解锁。它通过“整锁整放”机制确保所有锁要么全部加锁成功,要么完全回滚,避免状态不一致。适用于跨多个 Redis 实例或节点的场景,如分布式任务调度。其核心逻辑基于遍历加锁列表,失败时自动释放已获取的锁,保证原子性。解锁时亦逐一操作,降低死锁风险。MultiLock 不依赖 Lua 脚本,而是封装多锁协调,满足高一致性需求的业务场景。
236 0
【📕分布式锁通关指南 10】源码剖析redisson之MultiLock的实现
|
6月前
|
算法 NoSQL Redis
分布式锁—4.Redisson的联锁和红锁
Redisson的MultiLock和RedLock机制为分布式锁提供了强大的支持。MultiLock允许一次性锁定多个资源,确保在更新这些资源时不会被其他线程干扰。它通过将多个锁合并为一个大锁,统一进行加锁和释放操作。RedissonMultiLock的实现通过遍历所有锁并尝试加锁,若在超时时间内无法获取所有锁,则释放已获取的锁并重试。 RedLock算法则基于多个Redis节点的加锁机制,确保在大多数节点上加锁成功即可。RedissonRedLock通过重载MultiLock的failedLocksLi
389 10
|
6月前
|
NoSQL Java Redis
分布式锁—6.Redisson的同步器组件
Redisson提供了多种分布式同步工具,包括分布式锁、Semaphore和CountDownLatch。分布式锁包括可重入锁、公平锁、联锁、红锁和读写锁,适用于不同的并发控制场景。Semaphore允许多个线程同时获取锁,适用于资源池管理。CountDownLatch则用于线程间的同步,确保一组线程完成操作后再继续执行。Redisson通过Redis实现这些同步机制,提供了高可用性和高性能的分布式同步解决方案。源码剖析部分详细介绍了这些组件的初始化和操作流程,展示了Redisson如何利用Redis命令和
|
3月前
|
存储 负载均衡 NoSQL
【赵渝强老师】Redis Cluster分布式集群
Redis Cluster是Redis的分布式存储解决方案,通过哈希槽(slot)实现数据分片,支持水平扩展,具备高可用性和负载均衡能力,适用于大规模数据场景。
317 2
|
3月前
|
存储 缓存 NoSQL
【📕分布式锁通关指南 12】源码剖析redisson如何利用Redis数据结构实现Semaphore和CountDownLatch
本文解析 Redisson 如何通过 Redis 实现分布式信号量(RSemaphore)与倒数闩(RCountDownLatch),利用 Lua 脚本与原子操作保障分布式环境下的同步控制,帮助开发者更好地理解其原理与应用。
252 6
下一篇
oss云网关配置