首页 > 数据库 >Redis——分布式锁

Redis——分布式锁

时间:2023-10-08 17:14:36浏览次数:41  
标签:Redisson Redis 获取 线程 config 分布式

基本原理

synchronized是利用JVM内部的锁监视器控制线程,但是只能在一个JVM中生效。如果有多个JVM的时候,就会有多个线程获取到锁,就无法实现多JVM进程之间的互斥了。

因此不能使用JVM内部的锁监视器了,必须使用JVM外部的锁监视器,就能保证只有一个线程获取到锁,就能实现多进程之间的互斥了。

分布式锁是什么

是满足分布式系统或集群模式下多进程可见且互斥的锁。分布式锁可以是悲观锁或乐观锁,具体取决于实现方式。悲观锁是通过独占资源的方式实现的,悲观锁的具体实现如Redis分布式锁、MySQL行锁和表锁。

分布式锁的基本特性

1、多进程可见。

2、互斥(只能被一个线程拿到)。

3、高可用(大多数情况下线程来获取锁都是成功的)。

4、高并发(加锁之后,线程变成串行执行了,性能会下降,所以获取锁的动作要很快)。

5、安全性(避免死锁)。

不同实现方式对比

MySQL支持主从复制;Redis支持主从复制、集群;

基于Redis的分布式锁

基于setnx ex实现

存在许多缺点:

1、不可重入:同一线程无法多次获取同一把锁。

2、不可重试:获取锁没有重试机制。

3、超时释放:锁超时时间长短存在矛盾。

4、主从同步延迟(极端情况):获取锁是SET操作(写操作),主节点写入后未同步给从结点,就可能出现多个线程拿到锁的情况。

Redisson框架实现

Redisson是在Redis的基础上实现的分布式工具的集合。提供了分布式系统下各种各样的工具,包括分布式锁。

推荐自己配置Redisson,不要使用Redisson与SpringBoot的整合方式。

Redisson的使用

1、引入redisson依赖

2、配置Redis(配置文件或配置类)

@Configuration
public class RedisConfig {
    @Bean
    public RedissonClient redissonClient() {
        //配置类
        Config config = new Config();
        //添加redis地址,也可以使用config.useclusterServers()添加集群地址
        config.useSingleServer().setAddress
            ("redis://192.168.150.101:6379").setPassword("123321");
        //创建客户端
        return Redisson.create(config);
    }
}

3、使用Redisson的分布式锁

@Resource
private RedissonClient redissonClient;

@Test  
void testRedisson() throws InterruptedException {
    //1、创建锁对象(可重入),指定锁的名称
    RLock lock = redissonclient.getLock("lock:order:" + id);
    //2、尝试获取锁,参数为:
    //重试获取锁的最大等待时间(期间会重试获取锁),1秒结束,返回false
    //锁超时释放时间(避免宕机造成死锁)
    //时间单位
    boolean isLock = lock.tryLock(1, 10, TimeUnit.SECONDS);
    //判断尝试获取锁是否成功
    if(!isLock) {
        //获取锁失败
        return Result.fail("不允许重复下单");
    }
    try {
        System.out.println("执行业务");
    } finally {
        //3、释放锁
        lock.unlock();
    }
}

并发测试

可以使用Fiddler,Jmeter等软件发送请求。

Redisson原理

Redisson可重入锁原理

解决setnx ex锁的不可重入缺点。

在一个线程里连续多次获取锁,这就是锁的重入。所以锁的数据结构不能使用String(简单的key-value),需要使用能在一个key中存储两个数据的数据结构。Redisson使用Hash存储锁,key记录锁名,field记录线程标识,value记录锁的重入次数(与JDK中ReentrantLock原理相同)。

释放锁的时候将重入次数-1,直至所有的业务都执行完,最外层的方法结束释放锁时才能删除锁。因为尝试获取锁和释放锁是成对出现的,所以当value为0时就能判断出最外层的方法已经释放锁,就可以把锁删除。(代码需要lua脚本实现)

Redisson的锁重试和WatchDog机制

解决setnx ex锁的不可重试和超时释放时间长短矛盾的缺点。

/**
 * waitTime 等待时间
 * leaseTime 超时释放时间
 * unit 时间单位
 */
boolean tryLock(long waitTime, long leaseTime, TimeUnit unit);

重试机制

基于信号量和PubSub发布订阅(等待、唤醒,不占用CPU)

线程获取锁失败之后,进行等待,等待释放锁的消息(PubSub机制),得到消息后,去重新尝试获取锁。

线程获取锁成功,在释放锁的时候会发布消息。

超时释放机制

基于WatchDog机制,延续锁的剩余时间。

线程获取锁成功之后,开启一个定时任务,该任务每隔一段时间重置锁的超时时间(EXPIRE),确保锁在非服务宕机的情况下不会因为自动超时而释放。

Redisson的主从同步multiLock联锁原理

解决setnx ex锁的主从同步延迟的缺点。

主从节点中的所有节点都变成独立的Redis结点,都可以读写,此时获取锁的方式改变为依次向所有独立的Redis节点获取锁,即使一个节点宕机,仍然还有其他节点存活,节点越多可用性越高。

也可以给上述Redis节点建立主从关系,进一步提高可用性。一个主节点宕机,且未完成主从同步的情况下,其他线程也是不能获取到锁的。

该方案称为multiLock(联锁)。

使用

为每个节点(Redisson客户端)都新建一个配置。

	@Bean
    public RedissonClient redissonClient2() {
        //配置
        Config config = new Config();
        config.useSingleServer().setAddress
            ("redis://192.168.150.101:6380");
        //创建客户端
        return Redisson.create(config);
    }

	@Bean
    public RedissonClient redissonClient3() {
        //配置
        Config config = new Config();
        config.useSingleServer().setAddress
            ("redis://192.168.150.101:6381");
        //创建客户端
        return Redisson.create(config);
    }
//在需要使用的地方注入多个RedissonClient
@Resource
private RedissonClient redissonClient;
@Resource
private RedissonClient redissonClient2;
@Resource
private RedissonClient redissonClient3;

private RLock lock;

@BeforeEach
void setUp() {
    RLock lock1 = redissonclient.getLock("order");
    RLock lock2 = redissonclient2.getLock("order");
    RLock lock3 = redissonclient3.getLock("order");
    //创建联锁multiLock
    lock = redissonclient.getMultiLock(lockl, lock2, lock3);
}

void func() {
    boolean isLock = lock.tryLock();
}

总结

三类Redis分布式锁:

1、不可重入的Redis分布式锁

2、可重入的Redis分布式锁

3、Redisson的multiLock联锁

标签:Redisson,Redis,获取,线程,config,分布式
From: https://www.cnblogs.com/fallorange/p/17749622.html

相关文章

  • Redis——基本使用
    五种数据类型Redis是一个基于内存的数据库。是一个key-value的数据库,key一般是String类型,value的类型多种多样。字符串StringSETnamezhangxiancheng//redis中默认都是使用字符串来存储数据的DELkey//删除EXISTSkey//是否存在KEYS*//所有键redis中的键和值都是以二进......
  • redis-cluster nodes命令信息说明
     集群定义1.1每个字段的含义如下:1.id:节点ID,一个40字节的随机字符串,节点创建时生成,且不会变化(除非使用CLUSTERRESETHARD命令)。2.ip:port:客户端访问的地址。3.flags:逗号分隔的标记位,可能值有:myself,master,slave,fail?,fail,handshake,noaddr,noflags......
  • 分布式操作系统市场规模有万亿吗?你怎么看?
    昨天参加一个饭局,席间和一位做ICT基础设施投资的朋友聊起我们团队研发的LAXCUS分布式操作系统,请他分析一下分布式操作系统和连带的产业市场规模价值,我给出的分析依据是:1.LAXCUS面向TOB市场,有广泛应用前景,包括AI、大数据、HPC的支持2.LAXCUS属于多机操作系统,和Windows、Linux单......
  • 为什么redis使用单线程——简单说下
    redis使用单线程主要原因第一个,每条命令都是原子操作,单线程能够保证原子性。第二个原因,如果设计为多线程,肯定存在锁的竞争导致锁的获取释放开销,线程切换的开销,这与我们使用redis是相违背的。尽管redis设计为单线程,但是他的性能很高,主要原因是基于内存,以及pipeline机制都能保证redi......
  • Spring、Redis相关知识查漏补缺
    动态web页面不具有动态性×静态web页面不具有交互性√事务隔离级别是数据库自带的与Spring无关√Spring自己实现了—套与数据库无关的事务机制×软件框架是面向某个领域的、可复用的半成品软件√使用软件框架的优势是开发的灵活性和扩展性更好×拦截器......
  • Kafka的分布式架构与高可用性
    导语一开始我们就说过Kafka是一款开源的高吞吐、分布式的消息队列系统,那么今天我们就来说下它的分布式架构和高可用性以及双/多中心部署。Kafka体系架构简介以下是Kafka的软件架构,整个Kafka体系结构由Producer、Consumer、Broker、ZooKeeper组成。Broker又由Topic、......
  • Redis分布式锁
    简述利用Redis的Setnx命令,来实现一个分布式的加锁方案。利用注解,在拥有该注解的方法上,进行切面处理,在方法执行前,进行加锁,执行结束后,根据是否自动释放锁,进行解锁。将该注解用在定时任务的方法上,即可实现分布式定时任务,即获取到锁的方法,才会执行。1redis命令1.1setnx命令Re......
  • redis服务配置文件详解
    bind0.0.0.0#监听地址,可以用空格隔开后多个监听IPprotected-modeyes#redis3.2之后加入的新特性,在没有设置bindIP和密码的时候,redis只允许访问127.0.0.1:6379,可以远程连接,但当访问将提示警告信息并拒绝远程访问port6379#监听端口,默认6379/tcptcp-backlog511#三次......
  • Redis Cluster扩、缩容
    1、RedisCluster扩容(Redis5为例) 因公司业务发展迅猛,现有的三主三从的rediscluster架构可能无法满足现有业务的并发写入需求,因此公司紧急采购两台服务器10.0.0.68,10.0.0.78,需要将其动态添加到集群当中,但不能影响业务使用和数据丢失。 注意:生产环境一般建议master节点为奇数......
  • Mysql 分布式序列算法
    接上文Mysql分库分表1.分布式序列简介在分布式系统下,怎么保证ID的生成满足以上需求?ShardingJDBC支持以上两种算法自动生成ID。这里,使用ShardingJDBC让主键ID以雪花算法进行生成,首先配置数据库,因为默认的注解id是int类型,装不下64位,需要进行修改:#在本地和远端服务器数据......