分布式锁-Redis红锁解决方案

一 分布式锁的概念

1:概念

分布式锁(多服务共享锁) 在分布式的部署环境下,通过锁机制来让多客户端互斥的对共享资源进行访问控制分布式系统不同进程共同访问共享资源的一种锁的实现。如果不同的系统或同一个系统的不同主机之间共享了某个临界资源,往往需要互斥来防止彼此干扰,以保证一致性。

2:锁/分布式锁/事务区别

  • 锁 单进程的系统中,存在多线程同时操作一个公共变量,此时需要加锁对变量进行同步操作,保证多线程的操作线性执行消除并发修改。解决的是单进程中的多线程并发问题。
  • 分布式锁 只要的应用场景是在集群模式的多个相同服务,可能会部署在不同机器上,解决进程间安全问题,防止多进程同时操作一个变量或者数据库解决的是多进程的并发问题 事务 解决一个会话过程中,上下文的修改对所有数据库表的操作要么全部成功,要不全部失败。所以应用在service层。解决的是一个会话中的操作的数据一致性。
  • 分布式事务 解决一个联动操作,比如一个商品的买卖分为添加商品到购物车、修改商品库存,此时购物车服务和商品库存服务可能部署在两台电脑,这时候需要保证对两个服务的操作都全部成功或者全部回退。解决的是组合服务的数据操作的一致性问题

3:reddison的公平锁

reddison公平锁枷锁

默认的加锁逻辑是非公平的。在加锁失败时,线程会进入 while 循环,一直尝试获得锁,这时候是多线程进行竞争。就是说谁抢到就是谁的。Redisson 提供了公平锁机制,使用方式如下

RLock fairLock = redisson.getFairLock("anyLock");
// 最常见的使用方法
fairLock.lock();

看门狗机制是在 RedissonBaseLock#scheduleExpirationRenewal 方法中,这块公平锁和非公平锁并无区别。前文已经了解到,公平锁加锁失败之后,会将当前放到等待队列中,通过 Java 代码中的循环不断尝试获得锁。

reddison公平锁释放

公平锁的释放同样分为主动释放和超时释放。

  • 主动释放,即自己调用释放锁。
  • 超时删除,则分为两种,一种是持锁线程超时删除,这种和非公平锁没有任何区别,因为这个锁也是含有超时时间+看门狗续租的。另一种则是等待队列中的超时删除,是在每次获取锁之前,判断第一个等待线程的时间戳是否超时,从而移除锁。

二 、使用的案例场景

需求
当在打车软件中,乘客下了订单。多个司机抢单,此时因为单子只有一个,多个司机对此共享资源进行抢,此处应该使用分布式锁;后台服务部署在多台服务器上;
controller层代码

 @GetMapping("/do/{orderId}")public String grab(@PathVariable("orderId") int orderId, int driverId){System.out.println("order:"+orderId+",driverId:"+driverId);//此处调用锁控制层代码grabService.grabOrder(orderId,driverId);return "";}

锁控制层代码(使用synchronized 不成功)
使用synchronized 不能保证多台服务器只有一个抢成功;因为synchronized 只能锁本服务的资源;多台服务的资源是锁不住的;

@AutowiredOrderService orderService;@Overridepublic String grabOrder(int orderId, int driverId) {String lock = (orderId+"");synchronized (lock.intern()) {try {System.out.println("司机:"+driverId+" 执行抢单逻辑");//此处调用订单业务代码boolean b = orderService.grab(orderId, driverId);if(b) {System.out.println("司机:"+driverId+" 抢单成功");}else {System.out.println("司机:"+driverId+" 抢单失败");}} finally {}}return null;}

调用的订单业务代码
这一层就是写的伪代码,后续并不关注他
在这里插入图片描述

三、Redis解决方案-红锁

介绍

红锁本质上就是使用多个Redis做锁。例如有5个Redis,一次锁的获取,会对每个请求都获取一遍,如果获取锁成功的数量超过一半(2.5),则获取锁成功,反之失败;
释放锁也需要对每个Redis释放

红锁原理

  1. 在Redis的分布式环境中,我们假设有5个Redis master。这些节点完全互相独立,没有主从关系
  2. 线程1向这5个redis加锁,当加到第三个的时候,4和5加不上了;但是符合红锁的n/2+1原则,所以线程1获取到了锁;
  3. 当redis3挂了,此时线程1获取到了锁,正在顺序执行,
  4. 线程2来到了redis抢占锁,因为3挂了,1,2有锁,只有4和5可以加锁,因为我们注册的时候是5台,4和5这两台不满足n/2+1原则,抢占锁失败;

当锁遇到故障转移

单实例肯定不是很可靠吧?加锁成功之后,结果 Redis 服务宕机了,这不就凉凉~
这时候会提出来将 Redis 主从部署。即使是主从,也是存在巧合的!
主从结构中存在明显的竞态:

客户端 A 从 master 获取到锁在 master 将锁同步到 slave 之前,master 宕掉了。
slave 节点被晋级为 master 节点客户端 B 取得了同一个资源被客户端 A 已经获取到的另外一个锁。安全失效!有时候程序就是这么巧,比如说正好一个节点挂掉的时候,多个客户端同时取到了锁。如果你可以接受这种小概率错误,那用这个基于复制的方案就完全没有问题。
那我使用集群呢?如果还记得前面的内容,应该是知道对集群进行加锁的时候,其实是通过 CRC16 的 hash 函数来对 key 进行取模,将结果路由到预先分配过 slot 的相应节点上。
发现其实还是发到单个节点上的
! 这时候 Redis 作者提出了 RedLock 的概念

在这里插入图片描述
总结一下就是对集群的每个节点进行加锁,如果大多数(N/2+1)加锁成功了,则认为获取锁成功。

RedLock 的问题
看着 RedLock 好像是解决问题了:

客户端 A 锁住了集群的大多数(一半以上);
客户端 B 也要锁住大多数;
这里肯定会冲突,所以 客户端 B 加锁失败。
那实际解决问题了么?

加锁 key 的问题

有一个很大的疑问,我加锁 lock1、lock2、lock3,但是 RedissonRedLock 是如何保证这三个 key 是在归属于 Redis 集群中不同的 master 呢?因为按照 RedLock 的理论,是需要在半数以上的 master 节点加锁成功。阅读完源码之后,发现 RedissonRedLock 完全是 RedissonMultiLock 的子类,只是重写了 failedLocksLimit 方法,保证半数以上加锁成功即可。所以这三个 key,是需要用户来保证分散在不同的节点上的。

红锁的争议

那我使用 5 个单节点的客户端,然后再使用红锁,听着好像是可以的,并且 RedissonRedLock 可以这样使用。但是那和 Redis 集群还有啥关系啊!所以依然没有解决我的问题,在 redis 集群下 针对master节点集群,,还是需要用户自己来“手工定位锁”,使锁的节点分散到不同的master 集群节点下。 手工定位锁,这个…… 我考虑了下,还是不用 RedLock 吧!如果master节点变动则锁也存在问题,master集群同步等等锁的同步和锁失效也是需要考虑的问题;
Redisson 的开发者认为 Redis 的红锁也存在争议(前文介绍的那个争议),但是为了保证可用性,RLock 对象执行的每个 Redis 命令执行都通过 Redis 3.0 中引入的 WAIT 命令进行同步。

Redisson 的开发者认为 Redis 的红锁也存在争议(前文介绍的那个争议),但是为了保证可用性,RLock 对象执行的每个
Redis 命令执行都通过 Redis 3.0 中引入的 WAIT 命令进行同步。

在这里插入图片描述
源码在这一部分。
在这里插入图片描述
看源码,同时发送了一个 WAIT 1 1000 到 Redis。

结论:Redisson RedLock 是基于联锁 MultiLock 实现的,但是使用过程中需要自己判断 key 落在哪个节点上,对使用者不是很友好。

红锁使用说明-官网介绍

基于Redis的Redisson红锁RedissonRedLock对象实现了Redlock介绍的加锁算法。该对象也可以用来将多个RLock对象关联为一个红锁,每个RLock对象实例可以来自于不同的Redisson实例。

RLock lock1 = redissonInstance1.getLock("lock1");
RLock lock2 = redissonInstance2.getLock("lock2");
RLock lock3 = redissonInstance3.getLock("lock3");RedissonRedLock lock = new RedissonRedLock(lock1, lock2, lock3);
// 同时加锁:lock1 lock2 lock3
// 红锁在大部分节点上加锁成功就算成功。
lock.lock();
...
lock.unlock();

大家都知道,如果负责储存某些分布式锁的某些Redis节点宕机以后,而且这些锁正好处于锁住的状态时,这些锁会出现锁死的状态。为了避免这种情况的发生,Redisson内部提供了一个监控锁的看门狗,它的作用是在Redisson实例被关闭前,不断的延长锁的有效期。默认情况下,看门狗的检查锁的超时时间是30秒钟,也可以通过修改Config.lockWatchdogTimeout来另行指定。

另外Redisson还通过加锁的方法提供了leaseTime的参数来指定加锁的时间。超过这个时间后锁便自动解开了。

RedissonRedLock lock = new RedissonRedLock(lock1, lock2, lock3);
// 给lock1,lock2,lock3加锁,如果没有手动解开的话,10秒钟后将会自动解开
lock.lock(10, TimeUnit.SECONDS);// 为加锁等待100秒时间,并在加锁成功10秒钟后自动解开
boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
...
lock.unlock();

红锁实战

1.注册红锁的RedissonClient

@Component
public class RedisConfig {@Bean(name = "redissonRed1")@Primarypublic RedissonClient redissonRed1(){Config config = new Config();config.useSingleServer().setAddress("127.0.0.1:6379").setDatabase(0);return Redisson.create(config);}@Bean(name = "redissonRed2")public RedissonClient redissonRed2(){Config config = new Config();config.useSingleServer().setAddress("127.0.0.1:6380").setDatabase(0);return Redisson.create(config);}@Bean(name = "redissonRed3")public RedissonClient redissonRed3(){Config config = new Config();config.useSingleServer().setAddress("127.0.0.1:6381").setDatabase(0);return Redisson.create(config);}@Bean(name = "redissonRed4")public RedissonClient redissonRed4(){Config config = new Config();config.useSingleServer().setAddress("127.0.0.1:6382").setDatabase(0);return Redisson.create(config);}@Bean(name = "redissonRed5")public RedissonClient redissonRed5(){Config config = new Config();config.useSingleServer().setAddress("127.0.0.1:6383").setDatabase(0);return Redisson.create(config);}}

配置方式2
基于 Redis 的 Redisson 分布式联锁 RedissonMultiLock 对象可以将多个 RLock 对象关联为一个联锁,每个 RLock 对象实例可以来自于不同的 Redisson 实例
在这里插入图片描述
按照官方文档的说法,这里 Redisson 客户端可以不是同一个。当然,一般工作中也不会说不用一个客户端吧,可以看出 遍历所有的锁,依次加锁。加锁逻辑就和可重入锁加锁并无区别了。所以 Lua 脚本就不进行分析了

2. 红锁使用

package com.online.taxi.order.service.impl;import com.online.taxi.order.constant.RedisKeyConstant;
import com.online.taxi.order.service.GrabService;
import com.online.taxi.order.service.OrderService;import org.redisson.Redisson;
import org.redisson.RedissonRedLock;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;import java.util.concurrent.TimeUnit;@Service
public class GrabRedisRedissonRedLockLockServiceImpl implements GrabService {// 红锁@Autowired@Qualifier("redissonRed1")private RedissonClient redissonRed1;@Autowired@Qualifier("redissonRed2")private RedissonClient redissonRed2;@Autowired@Qualifier("redissonRed3")private RedissonClient redissonRed3;@Autowired@Qualifier("redissonRed4")private RedissonClient redissonRed4;@Autowired@Qualifier("redissonRed5")private RedissonClient redissonRed5;@AutowiredOrderService orderService;@Overridepublic String grabOrder(int orderId , int driverId){System.out.println("红锁实现类");//生成keyString lockKey = ("" + orderId).intern();//redisson锁 单节点
//        RLock rLock = redissonRed1.getLock(lockKey);//红锁 redis sonRLock rLock1 = redissonRed1.getLock(lockKey);RLock rLock2 = redissonRed2.getLock(lockKey);RLock rLock3 = redissonRed3.getLock(lockKey);RLock rLock4 = redissonRed4.getLock(lockKey);RLock rLock5 = redissonRed5.getLock(lockKey);RedissonRedLock rLock = new RedissonRedLock(rLock1,rLock2,rLock3,rLock4,rLock5);try {/**红锁* waitTimeout 尝试获取锁的最大等待时间,超过这个值,则认为获取锁失败* leaseTime   锁的持有时间,超过这个时间锁会自动失效(值应设置为大于业务处理的时间,确保在锁有效期内业务能处理完)*/boolean b1 = rLock.tryLock((long)waitTimeout, (long)leaseTime, TimeUnit.SECONDS);if (b1){System.out.println("加锁成功");// 此代码默认 设置key 超时时间30秒,过10秒,再延时System.out.println("司机:"+driverId+" 执行抢单逻辑");boolean b = orderService.grab(orderId, driverId);if(b) {System.out.println("司机:"+driverId+" 抢单成功");}else {System.out.println("司机:"+driverId+" 抢单失败");}System.out.println("加锁成功");}else {System.out.println("加锁失败");}} finally {rLock.unlock();}return null;}}

四、单机版redission 使用

 <dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId><version>3.18.0</version></dependency>

1.配置

    @Value("${spring.redis.host}")private String url;@Value("${spring.redis.port}")private Integer port;@Value("${spring.redis.password}")private String password;@Beanpublic RedissonClient redissonClient(){// 配置Config config = new Config();log.info("Redisson 初始化 {}",String.format("redis://%s:%d",url,port));config.useSingleServer().setAddress(String.format("redis://%s:%d",url,port) ).setPassword(password);// 创建RedissonClient对象return Redisson.create(config);}

2. 注入分布式锁

此处枷锁后在释放锁是解决了锁的读写机制问题

@Autowired
private RedissonClient redissonClient;public List<? extends Object> getCachedConfigList(String ode) {List<StructureVo> configuration =null;String key = getCacheKey(orgCode, STR_CONFIG_LIST);List<Object> cacheConfig = redisCache.getCacheList(key);if (!CollectionUtils.isEmpty(cacheConfig)) {return cacheConfig;}RLock lock = redissonClient.getLock(getReddisonEbomCacheKey());try {lock.lock(DEFAULT_EXPIRE_SECOND,TimeUnit.SECONDS);List<Object> secondCacheConfig = redisCache.getCacheList(key);if (!CollectionUtils.isEmpty(secondCacheConfig)) {log.info("second lock reddison 查询到缓存释放锁!");reddisnUnlock(getReddisonEbomCacheKey());return cacheConfig;}log.info("reddison枷锁成功!存放缓存资源!");configuration = this.queryAllConfiguration();redisCache.setCacheList(key, configuration);redisCache.expire(key, DEFAULT_EXPIRE_HOURS, TimeUnit.HOURS);} catch (Exception e) {log.error("Happen Exception: " + e.getMessage(),e);}finally {reddisnUnlock(getReddisonEbomCacheKey());}return configuration;}

释放锁方法

    private void reddisnUnlock(String key) {log.info(" reddison key {}锁释放!",key);try {RLock unlock = redissonClient.getLock(key);if (unlock != null && unlock.isHeldByCurrentThread()) {unlock.unlock();}} catch (IllegalMonitorStateException e) {log.info("reddison锁释放 Exception: " + e.getMessage(),e);}}

Redis 单实例版本锁 NX

单实例加锁

SET resource_name my_random_value NX PX 30000

对于单实例 Redis 只需要使用这个命令即可。

  • NX:仅在不存在 key 的时候才能被执行成功;
  • PX:失效时间,传入 30000,就是 30s 后自动释放锁;
  • my_random_value:就是随机值,可以是线程号之类的。主要是为了更安全的释放锁,释放锁的时候使用脚本告诉 Redis: 只有 key 存在并且存储的值和我指定的值一样才能删除成功。

简单描述即为

  1. 指定一个 key 作为锁标记,存入 Redis 中,指定一个 唯一的用户标识作为 value。
  2. 当 key 不存在时才能设置值,确保同一时间只有一个客户端进程获得锁,满足互斥性特性。
  3. 设置一个过期时间,防止因系统异常导致没能删除这个 key,满足防死锁特性。
  4. 当处理完业务之后需要清除这个 key 来释放锁,清除 key 时需要校验 value 值,需要满足只有加锁的人才能释放锁

可以通过以下 Lua 脚本实现锁释放:

if redis.call("get",KEYS[1]) == ARGV[1] thenreturn redis.call("del",KEYS[1])
elsereturn 0
end

为什么要设置随机值?

主要是为了防止锁被其他客户端删除。有这么一种情况:

  1. 客户端 A 获得了锁,还没有执行结束,但是锁超时自动释放了;
  2. 客户端 B 此时过来,是可以获得锁的,加锁成功;
  3. 此时,客户端 A 执行结束了,要去释放锁,如果不对比随机值,就会把客户端 B 的锁给释放了。
    当然前面看过 Redisson 的处理,这个 my_random_value 存放的是 UUID:ThreadId 组合成的一个类似 931573de-903e-42fd-baa7-428ebb7eda80:1 的字符串。

为什么要用lua脚本操作redis数据库?

1.减少开销–减少向redis服务器的请求次数
2.原子操作–redis将lua脚本作为一个原子执行
3.可复用–其他客户端可以使用已经执行过的lua脚本
4.增加redis灵活性–lua脚本可以帮助redis做更多的事情

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/176019.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用 Authing 快速实现一套类似 OpenAI 的认证、API Key 商业权益授权机制

如果你有经常使用 OpenAI 或者 HuggingFace 这一类面向开发者的 SaaS 服务&#xff0c;对于 API Key 肯定不会陌生。我们在使用这些服务时&#xff0c;通常都会在其平台上面创建一套 API Key&#xff0c;之后我们才能在代码中通过这一串 API key 访问其服务&#xff1b;同时&am…

处理SAP资产折旧AFAB 过账报错:“科目 8019010100 要求一个成本会计分配”

会计在进行资产折旧AFAB时 报错如下所示&#xff1a; 原因分析&#xff1a; 折旧时没有把资产设置得成本中心带到过账凭证的成本中心字段中去。而资产中已经维护了成本中心了。 所以要在资产过账的科目分配中设置一下路径如下&#xff1a; 或者TCODE&#xff1a;ACSET科目设置这…

Jmeter(二十一):jmeter导入和导出接口的处理(超详细)

JMeter测试导入接口 利用Jmeter测试上传文件&#xff0c;首先可根据接口文档或者fiddler抓包分析文件上传的接口&#xff1b;如下图&#xff1a; 以下是我通过fiddler所截取的文件上传的接口 1、填写导入接口的信息 查看文件上传栏下的填写信息&#xff1a; 文件名称&#x…

RT-Thread 7. RT-Thread Studio ENV修改MCU型号

1. 修改MCU型号 2.在ENV界面输入 scons -c scons --dist3. dist下为更新后完整源代码 4.导入RT-Thread Studio 发现GD32F330已经生效了。 5. 自己编写startup_gd32f3x0.S&#xff0c;准确性待验证 ;/* ; * Copyright (c) 2006-2021, RT-Thread Development Team ; * ; * SPD…

MySQL主从复制原理

1、MySQL主从复制的三个步骤及其原理图 slave会从master读取binlog来进行数据同步 MySQL复制过程分成三步&#xff1a; 1、master将改变记录到二进制日志&#xff08;binary log&#xff09;。这些记录过程叫做二进制日志事件&#xff0c;binary log events。 2、slave将ma…

WebService与RESTful两种接口风格示例

下面我将分别用WebService&#xff08;SOAP&#xff09;和RESTful API的例子来说明它们是如何工作的。 1. WebService (SOAP) 示例&#xff1a; 假设有一个在线计算器服务&#xff0c;它提供了一个加法操作的SOAP WebService。 SOAP请求&#xff08;客户端到服务器&#xff…

UG\NX二次开发 球坐标到直角坐标的转换

文章作者:里海 来源网站:《里海NX二次开发3000例专栏》 感谢粉丝订阅 感谢 JiaLiHuiNaoGui 订阅本专栏,非常感谢。 简介 已知角度θ和ϕ,距离d,求P点坐标。 "> 代码 #include "me.hpp" using namespace NXOpen; using namespace std;void GetP<

MBox肯定会因为回购和销毁将起飞

近期&#xff0c;币安链上的GameFi和元宇宙平台MOBOX宣布推出代币回购和销毁计划&#xff0c;在加密货币市场还处于熊市的现在&#xff0c;消息引起了不少链游打金爱好者和玩家的关注&#xff0c;MOBOX的讨论量也在快速上升。 在近一年多的熊市之中&#xff0c;很多GameFi项目从…

LeetCode:2003. 每棵子树内缺失的最小基因值(C++)

目录 2003. 每棵子树内缺失的最小基因值 题目描述&#xff1a; 实现代码与解析&#xff1a; dfs 启发式合并 原理思路&#xff1a; 2003. 每棵子树内缺失的最小基因值 题目描述&#xff1a; 有一棵根节点为 0 的 家族树 &#xff0c;总共包含 n 个节点&#xff0c;节点编…

【Linux】虚拟机部署与发布J2EE项目(Linux版本)

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是Java方文山&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的专栏《微信小程序开发实战》。&#x1f3af;&#x1f3a…

【Vue3-Flask-BS架构Web应用】实践笔记1-使用一个bat脚本自动化完整部署环境

前言 近年来&#xff0c;Web开发已经成为计算机科学领域中最热门和多产的领域之一。Python和Vue.js是两个备受欢迎的工具&#xff0c;用于构建现代Web应用程序。在本教程中&#xff0c;我们将探索如何使用这两个工具来创建一个完整的Web项目。我们将完成从安装Python和Vue.js到…

从0到1之微信小程序快速入门(02)

目录 页面导航 - 声明式导航 1. 导航到 tabBar 页面 2. 导航到非 tabBar 页面 3. 后退导航 ​编辑 页面导航 - 编程式导航 页面导航 - 导航传参 页面事件 - 下拉刷新事件 监听下拉刷新事件 停止下拉刷新的效果 页面事件 - 上拉触底事件 监听页面的上拉触底事件 配置…

项目部署之OpenResty

项目部署之OpenResty 1. OpenResty介绍 OpenResty 是一个基于Nginx的高性能Web平台&#xff0c;用于方便地搭建能够处理超高并发、扩展性极高的动态Web应用、Web服务和动态网关。具备下列特点&#xff1a; 具备Nginx的完整功能基于Lua语言进行扩展&#xff0c;集成了大量精良…

cmake多目录构建初步成功

目录和代码和 首次cmake 多目录构建失败 此文一样&#xff1b; 只有一个CMakeLists.txt&#xff1b; cmake_minimum_required(VERSION 3.10) project(mytest3 VERSION 1.0) include_directories("${PROJECT_SOURCE_DIR}/include") add_executable(mytest3 src/main…

vue2:路由前置守卫无法获取到this.$store.state.xxx

在获取到vuex的数据时候&#xff0c;想在router目录下的index.js文件去获取到vuex仓库中声明的全局变量&#xff0c;但是通过this.$store.stote.xxx去获取的时候&#xff0c;报错提示&#xff1a;$store未定义 一、store/index.js const store new Vuex.Store({state: {// 属…

WPS中图的自动编号及引用

WPS中图的自动编号及引用 图的自动编号图编号的引用图编号及引用的更新 图的自动编号 将光标放置在需要插入编号的位置点击“引用”→“题注”&#xff1a; 点击“引用”→“题注”&#xff1a; 点击“编号”&#xff0c;设置图的编号格式&#xff0c;可勾选“包含章节编号”&…

铁轨(Rails, ACM/ICPC CERC 1997, UVa 514)rust解法

有一个火车站&#xff0c;铁轨铺设如图6-1所示。有n节车厢从A方向驶入车站&#xff0c;按进站顺序编号为1&#xff5e;n。你的任务是判断是否能让它们按照某种特定的顺序进入B方向的铁轨并驶出车站。例如&#xff0c;出栈顺序(5 4 1 2 3)是不可能的&#xff0c;但(5 4 3 2 1)是…

【错误解决方案】ModuleNotFoundError: No module named ‘xgboost‘

1. 错误提示 在尝试导入名为xgboost的模块时出现了ModuleNotFoundError。 错误提示&#xff1a;ModuleNotFoundError: No module named xgboost 这个错误通常意味着Python环境中没有安装你试图导入的模块。 2. 解决方案 安装xgboost模块即可解决上述问题。 可以通过Python…

对音频文件的处理:音频信息,读取内容,获取时长,切割音频,pcm与wav互转

音频处理发现的比较简单的代码&#xff0c;原作者代码在github&#xff1a;GitHub - silencesmile/python_wav: 对音频文件的处理&#xff1a;音频信息&#xff0c;读取内容&#xff0c;获取时长&#xff0c;切割音频&#xff0c;pcm与wav互转 可以按给定的开始和结束时间调用代…

软件安利——火绒安全

近年来&#xff0c;以优化、驱动、管理为目标所打造的软件屡见不鲜&#xff0c;大同小异的电脑管家相继走入了公众的视野。然而&#xff0c;在这日益急功近利的社会氛围驱动之下&#xff0c;真正坚持初心、优先考虑用户体验的电脑管家逐渐湮没在了浪潮之中。无论是鲁大师&#…