实现分布式锁的解决方案

article/2025/10/13 4:20:24

目录

  • 1. 分布式锁
    • 1.1 什么是分布式锁
    • 1.2 为什么要使用分布式锁
    • 1.3 分布式锁应具有的特性
  • 2 分布式锁实现方案
    • 2.1 数据库实现分布式锁
    • 2.2 ZooKeeper实现分布式锁
    • 2.3 Redis实现分布式锁
      • 2.3.1 版本一
      • 2.3.2 版本二
      • 2.3.3 版本三
  • 3. Redisson
    • 3.1 Redisson介绍
    • 3.2 Redisson分布式锁使用方式
    • 3.3 Lua脚本
      • 3.3.1 Lua简介
      • 3.3.2 Redis中使用Lua的好处
      • 3.3.3 如何在Redis中使用Lua
  • 4 zk锁和redis锁比较
    • 4.1 setnx + lua脚本
    • 4.2 Redission
    • 4.3 Redis分布式锁的缺点
    • 4.4 ZK分布式锁


1. 分布式锁

1.1 什么是分布式锁

在我们进行单机应用开发涉及并发同步的时候,我们往往采用synchronized或者ReentrantLock的方式来解决多线程间的代码同步问题。但是当我们的应用是在分布式集群工作的情况下,那么就需要一种更加高级的锁机制,来处理种跨机器的进程之间的数据同步问题,这就是分布式锁。

分布式锁,是控制分布式系统之间同步访问共享资源的一种方式。在分布式系统中,常常需要协调他们的动作。如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要互斥来防止彼此干扰来保证一致性,在这种情况下,便需要使用到分布式锁。

分布式锁可以理解为:控制分布式系统有序的去对共享资源进行操作,通过互斥来保证数据的一致性。

可能有同学会问,使用我们以前学习的Java中的锁机制,例如synchronized、ReentrantLock不就能解决问题了吗?为什么还要使用分布式锁?

对于简单的单体项目,即运行时程序在同一个Java虚拟机中,此时使用上面的Java的锁机制确实可以解决多线程并发问题。例如下面程序代码:

public class LockTest implements Runnable {public synchronized void get() {System.out.println("1 线程 -->" + Thread.currentThread().getName());System.out.println("2 线程 -->" + Thread.currentThread().getName());System.out.println("3 线程 -->" + Thread.currentThread().getName());}public void run() {get();}public static void main(String[] args) {LockTest test = new LockTest();for (int i = 0; i < 10; i++) {new Thread(test, "线程-" + i).start();}}}

运行结果如下:

1 线程 -->线程-0 2 线程 -->线程-0 3 线程 -->线程-0 1 线程 -->线程-2 2 线程 -->线程-2 3 线程 -->线程-2 1 线程 -->线程-1 2 线程 -->线程-1 3 线程 -->线程-1 1 线程 -->线程-3 2 线程 -->线程-3 3 线程 -->线程-3 1 线程 -->线程-4 2 线程 -->线程-4 3 线程 -->线程-4

但是在分布式环境中,程序是集群方式部署,如下图:

在这里插入图片描述

上面的集群部署方式依然会产生线程并发问题,因为synchronized、ReentrantLock只是jvm级别的加锁,没有办法控制其他jvm。也就是上面两个tomcat实例还是可以出现并发执行的情况。要解决分布式环境下的并发问题,则必须使用分布式锁。

分布式锁的实现方式有多种,例如:数据库实现方式、ZooKeeper实现方式、Redis实现方式等。

1.2 为什么要使用分布式锁

为了能够说明分布式锁的重要性,下面通过一个电商项目中减库存的案例来演示如果没有使用分布式锁会出现什么问题。代码如下:

第一步:导入坐标

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.2.5.RELEASE</version><relativePath/></parent><groupId>com.itheima</groupId><artifactId>lock-test</artifactId><version>1.0-SNAPSHOT</version><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--集成redis--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><version>1.4.1.RELEASE</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.3</version></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId></dependency></dependencies></project>

第二步:配置application.yml文件

server:port: 8080
spring:redis:host: 68.79.63.42port: 26379password: itheima123

第三步:编写Controller

package com.itheima.controller;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class StockController {@Autowiredprivate StringRedisTemplate redisTemplate;@GetMapping("/stock")public String stock(){int stock = Integer.parseInt(redisTemplate.opsForValue().get("stock"));if(stock > 0){stock --;redisTemplate.opsForValue().set("stock",stock+"");System.out.println("库存扣减成功,剩余库存:" + stock);}else {System.out.println("库存不足!!!");}return "OK";}
}

第四步:编写启动类

@SpringBootApplication
public class MyApplication {public static void main(String[] args) {SpringApplication.run(MyApplication.class,args);}
}

第五步:设置redis

在这里插入图片描述

测试方式:使用jmeter进行压力测试,如下:

在这里插入图片描述

在这里插入图片描述

注:Apache JMeter是Apache组织开发的基于Java的压力测试工具。用于对软件做压力测试,它最初被设计用于Web应用测试,但后来扩展到其他测试领域。

查看控制台输出,发现已经出现了线程并发问题,如下:

在这里插入图片描述

由于当前程序是部署在一个Tomcat中的,即程序运行在一个jvm中,此时可以对减库存的代码进行同步处理,如下:

在这里插入图片描述

再次进行测试(注意恢复redis中的数据),此时已经没有线程并发问题,控制台输出如下:

在这里插入图片描述

这说明如果程序运行在一个jvm中,使用synchronized即可解决线程并发问题。

下面将程序进行集群部署(如下图所示),并通过Nginx进行负载,再进行测试。

在这里插入图片描述

操作过程:

第一步:配置Nginx

    upstream upstream_name{server 127.0.0.1:8001;server 127.0.0.1:8002;}server {listen       8080;server_name  localhost;location / {proxy_pass http://upstream_name;proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;}}

第二步:修改application.yml中端口号改为8001和8002并分别启动程序

第三步:使用jemter再次测试,可以看到又出现了并发问题

1.3 分布式锁应具有的特性

  • 在分布式系统环境下,一个方法在同一时间只能被一个机器的一个线程执行
  • 高可用的获取锁与释放锁
  • 高性能的获取锁与释放锁
  • 具备可重入特性
  • 具备锁失效机制,防止死锁
  • 具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失败

2 分布式锁实现方案

2.1 数据库实现分布式锁

基于数据库实现分布式锁的核心思想是:在数据库中创建一个表,表中包含方法名等字段,并在方法名字段上创建唯一索引。想要执行某个方法,首先需要将这个方法名插入表中,成功插入则获取锁,执行完成后删除对应的行数据释放锁。此种方式就是建立在数据库唯一索引的特性基础上的。

表结构如下:

在这里插入图片描述

具体实现过程如下(在前面lock-test工程基础上进行改造):

第一步:在pom.xml中导入maven坐标

<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.2.0</version>
</dependency>
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId>
</dependency>

第二步:配置文件application.yml中配置mybatis-plus相关配置

server:port: 8002
spring:redis:host: 68.79.63.42port: 26379password: itheima123application:name: lockTestdatasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://127.0.0.1:3306/dlockusername: rootpassword: root
mybatis-plus:configuration:map-underscore-to-camel-case: falseauto-mapping-behavior: full#log-impl: org.apache.ibatis.logging.stdout.StdOutImplmapper-locations: classpath*:mapper/**/*Mapper.xml

第三步:创建实体类

package com.itheima.entity;import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;@TableName("mylock")
public class MyLock implements Serializable {private int id;private String methodName;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getMethodName() {return methodName;}public void setMethodName(String methodName) {this.methodName = methodName;}
}

第四步:创建Mapper接口

package com.itheima.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.entity.MyLock;
import org.apache.ibatis.annotations.Mapper;@Mapper
public interface MyLockMapper extends BaseMapper<MyLock> {public void deleteByMethodName(String methodName);
}

第五步:在resources/mapper目录下创建Mapper映射文件MyLockMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.MyLockMapper"><delete id="deleteByMethodName" parameterType="string">delete from mylock where methodName = #{value}</delete>
</mapper>

第六步:改造StockController

@Autowired
private MyLockMapper myLockMapper;@GetMapping("/stock")
public String stock(){MyLock entity = new MyLock();entity.setMethodName("stock");try {//插入数据,如果不抛异常则表示插入成功,即获得锁myLockMapper.insert(entity);int stock = Integer.parseInt(redisTemplate.opsForValue().get("stock"));if(stock > 0){stock --;redisTemplate.opsForValue().set("stock",stock+"");System.out.println("库存扣减成功,剩余库存:" + stock);}else {System.out.println("库存不足!!!");}//释放锁myLockMapper.deleteByMethodName("stock");}catch (Exception ex){System.out.println("没有获取锁,不能执行减库存操作!!!");}return "OK";
}

通过观察控制台输出可以看到,使用此种方式已经解决了线程并发问题。

注意,虽然使用数据库方式可以实现分布式锁,但是这种实现方式还存在如下一些问题:

1、因为是基于数据库实现的,数据库的可用性和性能将直接影响分布式锁的可用性及性能,所以,数据库需要双机部署、数据同步、主备切换;

2、不具备可重入的特性,因为同一个线程在释放锁之前,行数据一直存在,无法再次成功插入数据,所以,需要在表中新增一列,用于记录当前获取到锁的机器和线程信息,在再次获取锁的时候,先查询表中机器和线程信息是否和当前机器和线程相同,若相同则直接获取锁;

3、没有锁失效机制,因为有可能出现成功插入数据后,服务器宕机了,对应的数据没有被删除,当服务恢复后一直获取不到锁,所以,需要在表中新增一列,用于记录失效时间,并且需要有定时任务清除这些失效的数据;

4、不具备阻塞锁特性,获取不到锁直接返回失败,所以需要优化获取逻辑,循环多次去获取。

5、在实施的过程中会遇到各种不同的问题,为了解决这些问题,实现方式将会越来越复杂;依赖数据库需要一定的资源开销,性能问题需要考虑。

2.2 ZooKeeper实现分布式锁

ZooKeeper实现分布式锁

2.3 Redis实现分布式锁

redis实现分布式锁比较简单,就是调用redis的set命令设置值,能够设置成功则表示加锁成功,即获得锁,通过调用del命令来删除设置的键值,即释放锁。

2.3.1 版本一

加锁命令:set lock_key lock_value NX

解锁命令:del lock_key

Java程序:

@GetMapping("/stock")
public String stock() {try {//尝试加锁Boolean locked = redisTemplate.opsForValue().setIfAbsent("mylock", "mylock");if(locked){//加锁成功int stock = Integer.parseInt(redisTemplate.opsForValue().get("stock"));if(stock > 0){stock --;redisTemplate.opsForValue().set("stock",stock+"");System.out.println("库存扣减成功,剩余库存:" + stock);}else {System.out.println("库存不足!!!");}//释放锁redisTemplate.delete("mylock");}else{System.out.println("没有获取锁,不能执行减库存操作!!!");}}catch (Exception ex){System.out.println("出现异常!!!");}return "OK";
}

2.3.2 版本二

上面版本一的实现中存在一个问题,就是当某个线程获得锁后程序挂掉,此时还没来得及释放锁,这样后面所有的线程都无法获得锁了。为了解决这个问题可以在加锁时设置一个过期时间防止死锁。

加锁命令:set lock_key lock_value NX PX 5000

解锁命令:del lock_key

Java程序:

@GetMapping("/stock")
public String stock() {try {//尝试加锁Boolean locked = redisTemplate.opsForValue().setIfAbsent("mylock", "mylock",5000,TimeUnit.MILLISECONDS);if(locked){//加锁成功int stock = Integer.parseInt(redisTemplate.opsForValue().get("stock"));if(stock > 0){stock --;redisTemplate.opsForValue().set("stock",stock+"");System.out.println("库存扣减成功,剩余库存:" + stock);}else {System.out.println("库存不足!!!");}//释放锁redisTemplate.delete("mylock");}else{System.out.println("没有获取锁,不能执行减库存操作!!!");}}catch (Exception ex){System.out.println("出现异常!!!");}return "OK";
}

2.3.3 版本三

针对前面版本二还有一点需要优化,就是加锁和解锁必须是同一个客户端,所以在加锁时可以设置当前线程id,在释放锁时判断是否为当前线程加的锁,如果是再释放锁即可。

Java程序:

@GetMapping("/stock")
public String stock() {try {String threadId = Thread.currentThread().getId()+"";//尝试加锁Boolean locked = redisTemplate.opsForValue().setIfAbsent("mylock",threadId,5000,TimeUnit.MILLISECONDS);if(locked){//加锁成功int stock = Integer.parseInt(redisTemplate.opsForValue().get("stock"));if(stock > 0){stock --;redisTemplate.opsForValue().set("stock",stock+"");System.out.println("库存扣减成功,剩余库存:" + stock);}else {System.out.println("库存不足!!!");}String myValue = redisTemplate.opsForValue().get("mylock");if(threadId.equals(myValue)){//释放锁redisTemplate.delete("mylock");}}else{System.out.println("没有获取锁,不能执行减库存操作!!!");}}catch (Exception ex){System.out.println("出现异常!!!");}return "OK";
}

3. Redisson

3.1 Redisson介绍

Redisson是架设在Redis基础上的一个Java驻内存数据网格(In-Memory Data Grid)。充分的利用了Redis键值数据库提供的一系列优势,基于Java实用工具包中常用接口,为使用者提供了一系列具有分布式特性的常用工具类。使得原本作为协调单机多线程并发程序的工具包获得了协调分布式多机多线程并发系统的能力,大大降低了设计和研发大规模分布式系统的难度。同时结合各富特色的分布式服务,更进一步简化了分布式环境中程序相互之间的协作。

在这里插入图片描述

Redisson已经内置提供了基于Redis的分布式锁实现,此种方式是我们推荐的分布式锁使用方式。

Redisson的maven坐标如下:

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

3.2 Redisson分布式锁使用方式

第一步:在pom.xml中导入redisson的maven坐标

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

第二步:编写配置类

package com.itheima.config;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class RedissonConfig {@Value("${spring.redis.host}")private String host;@Value("${spring.redis.port}")private String port;@Value("${spring.redis.password}")private String password;@Beanpublic RedissonClient redissonClient(){Config config = new Config();config.useSingleServer().setAddress("redis://" + host + ":" + port);config.useSingleServer().setPassword(password);final RedissonClient client = Redisson.create(config);return client;}
}

第三步:改造Controller

@Autowired
private RedissonClient redissonClient;@GetMapping("/stock")
public String stock() {//获得分布式锁对象,注意,此时还没有加锁成功RLock lock = redissonClient.getLock("mylock");try {//尝试加锁,如果加锁成功则后续程序继续执行,如果加锁不成功则阻塞等待lock.lock(5000,TimeUnit.MILLISECONDS);int stock = Integer.parseInt(redisTemplate.opsForValue().get("stock"));if(stock > 0){stock --;redisTemplate.opsForValue().set("stock",stock+"");System.out.println("库存扣减成功,剩余库存:" + stock);}else {System.out.println("库存不足!!!");}}catch (Exception ex){System.out.println("出现异常!!!");}finally {//解锁lock.unlock();}return "OK";
}

3.3 Lua脚本

3.3.1 Lua简介

Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。

从Redis2.6.0版本开始提供了EVAL 和 EVALSHA 命令,这两个命令可以执行 Lua 脚本。

3.3.2 Redis中使用Lua的好处

Redis中使用Lua的好处:

  • 减少网络开销。可以将多个请求通过脚本的形式一次发送,减少网络时延
  • 原子操作。redis会将整个脚本作为一个整体执行,中间不会被其他命令插入。因此在编写脚本的过程中无需担心会出现竞态条件,无需使用事务
  • 复用。客户端发送的脚本会永久存在redis中,这样,其他客户端可以复用这一脚本而不需要使用代码完成相同的逻辑

3.3.3 如何在Redis中使用Lua

在redis中使用Lua脚本主要有三个命令

  • eval
  • evalsha
  • script load

eval用来直接执行lua脚本,使用方式如下:

EVAL script numkeys key [key ...] arg [arg ...]

key代表要操作的redis key

arg可以传自定义的参数

numkeys用来确定key有几个

script就是你写的lua脚本

lua脚本里面使用KEYS[1]和ARGV[1]来获取传递的第一个key和第一个arg,后面以此类推

举例:

eval "return redis.call('set',KEYS[1],ARGV[1])" 1 city beijing
eval "return redis.call('set','name','xiaoming')" 0
eval "return redis.call('del',KEYS[1])" 1 city
eval "return redis.call('get',KEYS[1])" 1 name
eval "if (redis.call('exists', KEYS[1]) == 0) then redis.call('set', KEYS[2], ARGV[1]); redis.call('expire', KEYS[2], ARGV[2]);return nil;end;" 2 citys city beijing 5000

在用eval命令的时候,可以注意到每次都要把执行的脚本发送过去,这样势必会有一定的网络开销,所以redis对lua脚本做了缓存,通过script load 和 evalsha实现:

SCRIPT LOAD script
EVALSHA sha1 numkeys key [key ...] arg [arg ...]

举例:

script load "return redis.call('get',KEYS[1]);"
evalsha 0e11c9f252fd76115c38403ce6095872b8c70580 1 name

4 zk锁和redis锁比较

4.1 setnx + lua脚本

优点:redis基于[内存],读写性能很高,因此基于redis的分布式锁效率比较高

缺点分布式环境下可能会有节点数据同步问题,可靠性有一定的影响。比如现在有一个3主3丛的Redis集群, 客户端发生的命令写入了机器1的master 节点,数据正准备主丛同步的时候,master 结点挂了,slave 结点没有接收到最新的数据,此时 slave结点竞选为master, 导致之前加的分布式锁失效。

4.2 Redission

优点:解决了Redis集群的同步可用性问题

缺点:网上是说:发布时间短,稳定性和可靠性有待验证。个人觉得,目前市面上已经稳定了,算是比较成熟的比较完善的分布式锁了。

4.3 Redis分布式锁的缺点

如果你对某个redis master实例,写入了myLock这种锁key的value,此时会异步复制给对应的master slave实例。

但是这个过程中一旦发生redis master宕机,主备切换,redis slave变为了redis master。接着就会导致,客户端2来尝试加锁的时候,在新的redis master上完成了加锁,而客户端1也以为自己成功加了锁。

此时就会导致多个客户端对一个分布式锁完成了加锁。这时系统在业务语义上一定会出现问题,导致各种脏数据的产生。

所以这个就是redis cluster,或者是redis master-slave架构的主从异步复制导致的redis分布式锁的最大缺陷:*在redis master实例宕机的时候,可能导致多个客户端同时完成加锁。*

4.4 ZK分布式锁

优点:不存在redis的数据同步(zookeeper是同步完以后才返回)、主从切换(zookeeper主从切换的过程中服务是不可用的)的问题,可靠性很高

缺点:保证了可靠性的同时牺牲了一部分效率(但是依然很高)。性能不如redis。

注:实际开发过程中,可以 curator 工具包封装的API帮助我们实现分布式锁。


http://chatgpt.dhexx.cn/article/otJc2BTC.shtml

相关文章

什么是分布式锁,分布式锁有什么作用?

1 、什么是分布式锁 为了防止分布式系统中的多个进程之间相互干扰&#xff0c;我们需要一种分布式协调技术来对这些进程进行调度。而这个分布式协调技术的核心就是来实现这个分布式锁。 2、为什么要使用分布式锁 成员变量 A 存在 JVM1、JVM2、JVM3 三个 JVM 内存中&#xff1b…

分布式锁以及三种加锁方式

在很多场景中&#xff0c;我们为了保证数据的最终一致性&#xff0c;需要很多的技术方案来支持&#xff0c;比如分布式事务、分布式锁等。那具体什么是分布式锁&#xff0c;分布式锁应用在哪些业务场景、如何来实现分布式锁呢&#xff1f; 一 为什么要使用分布式锁 我们在开发…

分布式架构 --- 分布式锁

分布式锁 1. 研究背景及其意义2. 分布式锁的介绍2.1 分布式锁2.2 为什么需要分布式锁2.3 分布式锁的基本要求 3. 分布式锁的实现3.1 基于数据库的分布式锁3.1.1选用数据库实现分布式锁的原因3.1.2 基于数据库实现分布式锁的缺点3.1.3分布式锁的实现 3.2 基于Redis的分布式锁3.2…

分布式锁的区别

分布式锁&#xff0c;是一种思想&#xff0c;它的实现方式有很多。比如&#xff0c;我们将沙滩当做分布式锁的组件&#xff0c;那么它看起来应该是这样的 加锁 在沙滩上踩一脚&#xff0c;留下自己的脚印&#xff0c;就对应了加锁操作。其他进程或者线程&#xff0c;看到沙滩上…

分布式锁的实现方式

背景 分布式场景中的数据一致性问题一直是一个比较重要的话题。分布式的CAP理论告诉我们“任何一个分布式系统都无法同时满足一致性&#xff08;Consistency&#xff09;、可用性&#xff08;Availability&#xff09;和分区容错性&#xff08;Partition tolerance&#xff09…

分布式锁-Redisson

分布式锁 1、分布式锁1.1 本地锁的局限性1.1.1 测试代码1.1.2 使用ab工具测试(单节点)1.1.3 本地锁问题演示(集群情况) 1.2 分布式锁实现的解决方案1.3 使用Redis实现分布式锁(了解即可)1.3.1 编写代码1.3.2 压测 1.4 使用Redisson解决分布式锁1.4.1 实现代码1.4.1 压测1.4.2 可…

Redis 分布式锁

文章目录 一、分布式锁概念二、使用setnx实现锁三、编写代码测试分布式锁1. 使用Java代码测试分布式锁2. 优化之设置锁的过期时间 四、优化之给lock设置UUID防误删五、使用LUA脚本保证删除的原子性 一、分布式锁概念 随着业务发展的需要&#xff0c;原单机部署的系统被演化成分…

关于分布式锁

先别说了别的&#xff0c;先来一个总结。 synchronized 单机版可以&#xff0c;但是上了分布式就不行了。 nginx 分布式服务单机锁就不行 取消单机锁&#xff0c;上redis分布式锁setnx 注意的问题&#xff1a; 如果只加了锁&#xff0c;没有释放锁&#xff0c;出现异常的话…

Redisson分布式锁详解

概述 setnx分布式锁的问题 重入问题 重入问题是指获得锁的线程可以再次进入到相同的锁的代码块中&#xff0c;可重入锁的意义在于防止死锁&#xff0c;比如HashTable这样的代码中&#xff0c;它的方法都是使用synchronized修饰的&#xff0c;假如它在一个方法内&#xff0c;…

redission实现分布式锁

在开始提到Redis分布式锁之前&#xff0c;先说一下redis中的两个命令。 SETNX key valuesetnx 是SET if Not eXists(如果不存在&#xff0c;则 SET)的简写。 用法如图&#xff0c;如果不存在set成功返回int的1&#xff0c;这个key存在了返回0。 SETEX key seconds value上面…

Java分布式锁

文章目录 1.什么是锁&#xff1f;2.什么是分布式&#xff1f;分布式场景 3.什么是分布式锁&#xff1f;4.我们应该怎么设计分布式锁&#xff1f;5.基于数据库的分布锁5.1 基于表主键唯一做分布式锁5.2 基于表字段版本号做分布式锁 6.基于 Redis 做分布式锁6.1 基于 REDIS 的 SE…

Redis分布式锁

概述 日常开发中&#xff0c;秒杀下单、抢红包等等业务场景&#xff0c;都需要用到分布式锁。而Redis非常适合作为分布式锁使用。本文将分七个方案展开&#xff0c;跟大家探讨Redis分布式锁的正确使用方式。如果有不正确的地方&#xff0c;欢迎大家指出哈&#xff0c;一起学习一…

Zookeeper分布式锁

实现一把分布式锁通常有很多方法&#xff0c;比较常见的有 Redis 和 Zookeeper。 Redis分布式锁可参考之前的文章&#xff1a; Redisson 分布式锁原理分析&#xff1a;https://blog.csdn.net/qq_42402854/article/details/123342331 Zookeeper能实现分布式锁&#xff0c;是因…

分布式锁

分布式锁实践 在不同进程需要互斥地访问共享资源时&#xff0c;分布式锁是一种非常有用的技术手段。有很多三方库和文章描述如何用Redis实现一个分布式锁管理器&#xff0c;但是这些库实现的方式差别很大&#xff0c;而且很多简单的实现其实只需采用稍微增加一点复杂的设计就可…

分布式系列之分布式锁几种实现机制

在分布式系统中&#xff0c;分布式锁用来解决分布式系统中多线程、多进程在不同机器上共享资源访问的问题。本文简要介绍分布式锁的四种实现机制&#xff0c;包括数据库、Redis缓存、Zookeeper和Etcd&#xff0c;以加深了解。 1、分布式锁介绍 在单体应用中&#xff0c;通过锁…

三种分布式锁

----------本文为学习记录如有错误帮忙指正 一、什么是分布式锁&#xff1f; 在单机系统下&#xff0c;如果多个线程同时访问一个变量或者代码片段就会产生多线程问题。&#xff08;被访问的变量或者代码片段被称之为临界区域&#xff09;这时我们就需要让所有线程按顺序一个一…

Redis实现分布式锁

目录 一、前言 为什么需要分布式锁&#xff1f; 二、基于redis实现分布式锁 为什么redis可以实现分布式锁&#xff1f; 如何实现&#xff1f; 锁的获取 锁的释放 三、如何避免死锁&#xff1f;锁的过期时间如何设置&#xff1f; 避免死锁 锁过期处理 释放其他服务的锁…

什么是分布式锁?几种分布式锁分别是怎么实现的?

一、什么是分布式锁&#xff1a; 1、什么是分布式锁&#xff1a; 分布式锁&#xff0c;即分布式系统中的锁。在单体应用中我们通过锁解决的是控制共享资源访问的问题&#xff0c;而分布式锁&#xff0c;就是解决了分布式系统中控制共享资源访问的问题。与单体应用不同的是&am…

软件需求最佳实践笔记(一)

1.软件需求最佳实践笔记 | 需求框架 前言&#xff1a;SERU是一套系统全面的需求方法论&#xff0c;可指导我们日常的软件需求工作。曾参加过徐峰老师软件需求最佳实践课程的培训&#xff0c;收益颇多&#xff0c;现通过笔记形式整理出来&#xff0c;以期与具有同样需求的读者共…

声音信号基音提取算法基频和谐波处理分析

1、内容简介 略 293-可以交流、咨询、答疑 2、内容说明 略 一、 实验原理&#xff1a; 傅里叶变换建立了信号频谱的概念。所谓傅里叶分析即分析信号的频谱(频率构成)、频带宽度等。要想合成出一段音乐,就要了解该段音乐的基波频率、谐波构成等。因此,必须采用傅里叶变换这…