IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> 孤尽训练营打卡日记day27--分布式锁 -> 正文阅读

[大数据]孤尽训练营打卡日记day27--分布式锁

前言

???? ? ? 随着公司业务的扩展,单实例逐渐都演变成的多实例,在多实例的情况下,怎么处理并发呢?

? ??? ? ?单实例加锁:synchronized关键字、Semaphore、ReentrantLock,或者我们也可以基于AQS定制化锁

???? ? ? 多实例加锁:?

什么是分布式锁

???? ????单机部署的情况下,锁是在多线程之间共享的,但是分布式部署的情况下,是多台机器,应用级锁。也就是说锁是多进程之间共享的。那么分布式锁要保证锁资源的唯一性,可以在多进程之间共享。

分布式锁特性(互斥、可重入)

  • 保证同一方法在某一时刻只能在一台机器里的一个进程中一个线程执行
  • 保证锁可重入(避免死锁)
  • 保证获取锁和释放锁的高可用

实现分布式锁的三种方式

数据库

???? ? ? 乐观锁:在表结构中,设置一列版本号,每次更新数据,都会校验版本号,不一致则更新失败

???? ? ? 场景:写比较少的场景,多读? ? ? ? ?

???? ? ? 悲观锁 :select ....... for update ,? 每次拿数据的时候都会加锁,其他人只能读,不能写? ?

? ? ? ? ?场景:多写场景? ?

zookeeper

???? ????实现分布式锁的原理就是多个节点同时在一个指定的节点下面创建临时会话顺序节点,谁创建的节点序号最小,谁就获得了锁,并且其他节点就会监听序号比自己小的节点,一旦序号比自己小的节点被删除了,其他节点就会得到相应的事件,然后查看自己是否为序号最小的节点,如果是,则获取锁。

缺点:

???? ????zookeeper分布式锁保证了锁的容错性、一致性。但是会产生空闲节点(/lock-path)。高并发的业务场景下使用zookeeper分布式锁时,会留下很多的空节点。

redis

???? ????获取锁时,使用redis的命令setnx、pexpire(提供基于毫秒的过期时间,expire提供基于秒的过期时间)+ lua脚本(保证脚本中的命令被一起执行,不间断)来实现分布式锁。删除锁时,先执行get,如果获取的值是自己设置的,则执行del操作,同时,这两个操作也放在lua脚本中执行,来保证原子性。

RedisLockRegistry

????????是spring-integration-redis中提供redis分布式锁实现类。主要是通过redis锁+本地锁双重锁的方式实现的一个比较好的锁。

????????获取锁 的过程也很简单,首先通过本地锁(localLock,对应的是ReentrantLock实例)获取锁,然后通过RedisTemplate获取redis锁。

@Override

public void lock() {
    // 获取本地锁
    Lock localLock = RedisLockRegistry.this.localRegistry.obtain(this.lockKey); 
    localLock.lock();
    while (true) {
    try {
        while (!this.obtainLock()) { // 自旋获取 redis
            Thread.sleep(100); //NOSONAR

        }
        break;

    } catch (InterruptedException e) {

    /*
     * This method must be uninterruptible so catch and ignore
     * interrupts and only break out of the while loop when
     * we get the lock.
     */
    } catch (Exception e) {
        localLock.unlock();
        rethrowAsLockException(e);
    }

}

为什么使用本地锁:首先是为了锁的可重入,其次是减轻redis服务压力。

释放锁

@Override
public void unlock() {
    if (!Thread.currentThread().equals(this.thread)) {
        if (this.thread == null) {
            throw new IllegalStateException("Lock is not locked; " + this);
        }
        throw new IllegalStateException("Lock is owned by " + this.thread.getName() + ";" + this);
    }
    try {
        if (this.reLock-- <= 0) {
            try {
                this.assertLockInRedisIsUnchanged();
                RedisLockRegistry.this.redisTemplate.delete(constructLockKey());
                if (logger.isDebugEnabled()) {
                    logger.debug("Released lock; " + this);
                }
            } finally {
                this.thread = null;
                this.reLock = 0;
                toWeakThreadStorage(this);
            }
        }
    } finally {
        Lock localLock = RedisLockRegistry.this.localRegistry.obtain(this.lockKey);
        localLock.unlock();
    }
}

????????如果当前线程持有锁的计数 > 1,说明本地锁被当前线程多次获取,这时只释放本地锁(释放之后当前线程持有锁的计数-1)

????????如果当前线程持有锁的计数 = 1,释放本地锁和redis锁

使用

@Autowired
private RedisLockRegistry redisLockRegistry;

public void testLock(){
    Lock obtain = redisLockRegistry.obtain("Key");
    if (obtain.tryLock()) {
        try {
            // todo 加锁操作
        } finally {
            try {
                obtain.unlock();
            } catch (Exception e) {
???? ???? ???// 释放锁异常
            }
        }
    } else {
        // 获取锁失败
    }
}

注入 RedisLockRegistry? ?bean

@Configuration
public class RedisLockConfiguration {
    @Bean
    @ConditionalOnMissingBean(RedisLockConfig.class)
    public RedisLockConfig redisLockConfig() {
        return new RedisLockConfig();
    }

    @Bean
    public RedisLockRegistry redisLockRegistry(RedisConnectionFactory  redisConnectionFactory, RedisLockConfig redisLockConfig) {
        return new RedisLockRegistry(redisConnectionFactory, redisLockConfig.getRegistryKey());
    }
}
@ConfigurationProperties(prefix = "redis.lock", ignoreUnknownFields = true)
public class RedisLockConfig {

    private String registryKey;

    public String getRegistryKey() {
        return registryKey;
    }

    public void setRegistryKey(String registryKey) {
        this.registryKey = registryKey;
    }
}

Redisson

???? ???是架设在Redis基础上的一个Java驻内存数据网格(In-Memory Data Grid)。 充分的利用了Redis键值数据库提供的一系列优势,基于Java实用工具包中常用接口,为使用者提供了一系列具有分布式特性的常用工具类。

RedisLockRegistry 和 Redisson 区别

  • RedisLockRegistry通过本地锁(ReentrantLock)和redis锁,双重锁实现,Redission通过Netty Future机制、Semaphore (jdk信号量)、redis锁实现。

  • RedisLockRegistry和Redssion都是实现的可重入锁。

  • RedisLockRegistry对锁的刷新没有处理,Redisson通过Netty的TimerTask、Timeout 工具完成锁的定期刷新任务。

  • RedisLockRegistry仅仅是实现了分布式锁,而Redisson处理分布式锁,还提供了了队列、集合、列表等丰富的API。

少年易学老难成,一寸光阴不可轻。? ? ? --朱熹

  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2021-11-22 12:24:30  更:2021-11-22 12:25:55 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 6:16:09-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码