1、传统锁回顾(Jvm本地锁,MySQL悲观锁、乐观锁)
目录
- 1.1 从减库存聊起
- 1.2 环境准备
- 1.3 简单实现减库存
- 1.4 演示超卖现象
- 1.5 jvm锁
- 1.6 三种情况导致Jvm本地锁失效
- 1、多例模式下,Jvm本地锁失效
- 2、Spring的事务导致Jvm本地锁失效
- 3、集群部署导致Jvm本地锁失效
- 1.7 mysql锁演示
- 1.7.1、一个sql
- 1.7.2、悲观锁
- 1.7.3、乐观锁
- 1.7.4、mysql锁总结
- 1.8 redis乐观锁
- 1.8.1 引入redis
- 1.8.2 redis乐观锁原理
- 1.8.3 redis乐观锁解决超卖问题
- 1.8.4 redis乐观锁的缺点
1.1 从减库存聊起
多线程并发安全问题最典型的代表就是超卖现象
库存在并发量较大情况下很容易发生超卖现象,一旦发生超卖现象,就会出现多成交了订单而发不了货的情况。
场景:商品S库存余量为5时,用户A和B同时来购买一个商品,此时查询库存数都为5,库存充足则开始减库存
用户A:update db_stock set stock = stock - 1 where id = 1
用户B:update db_stock set stock = stock - 1 where id = 1
并发情况下,更新后的结果可能是4,而实际的最终库存量应该是3才对 !!
1.2 环境准备
建表语句:
CREATE TABLE `db_stock` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`product_code` varchar(255) DEFAULT NULL COMMENT '商品编号',`stock_code` varchar(255) DEFAULT NULL COMMENT '仓库编号',`count` int(11) DEFAULT NULL COMMENT '库存量',PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
表中数据如下:

创建分布式锁demo工程:
目录结构

pom.xml
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.46</version></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.3.4</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies>
application.yml配置文件:
server.port=10010
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://192.168.239.11:3306/atguigu_distributed_lock
spring.datasource.username=root
spring.datasource.password=houchen
DistributedLockApplication启动类:
@SpringBootApplication
@MapperScan("com.atguigu.distributed.lock.mapper")
public class DistributedLockApplication {public static void main(String[] args) {SpringApplication.run(DistributedLockApplication.class, args);}}
Stock实体类:
@Data
@TableName("db_stock")
public class Stock {@TableIdprivate Long id;private String productCode;private String stockCode;private Integer count;
}
StockMapper接口:
public interface StockMapper extends BaseMapper<Stock> {
}
1.3 简单实现减库存

@RestController
public class StockController {@Autowiredprivate StockService stockService;@GetMapping("stock/deduct")public String deduct(){this.stockService.deduct();return "hello stock deduct!!";}}@Service
public class StockService {@Autowiredprivate StockMapper stockMapper;public void deduct(){// 先查询库存是否充足Stock stock = this.stockMapper.selectById(1L);// 再减库存if (stock != null && stock.getCount() > 0){stock.setCount(stock.getCount() - 1);this.stockMapper.updateById(stock);}}
}
测试:

查看数据库:

在浏览器中一个一个访问时,每访问一次,库存量减1,没有任何问题。
1.4 演示超卖现象
使用jmeter压力测试工具,高并发下压测一下,添加线程组:并发100循环50次,即5000次请求。


启动测试,查看压力测试报告:

- Label 取样器别名,如果勾选Include group name ,则会添加线程组的名称作为前缀
- # Samples 取样器运行次数
- Average 请求(事务)的平均响应时间
- Median 中位数
- 90% Line 90%用户响应时间
- 95% Line 90%用户响应时间
- 99% Line 90%用户响应时间
- Min 最小响应时间
- Max 最大响应时间
- Error 错误率
- Throughput 吞吐率
- Received KB/sec 每秒收到的千字节
- Sent KB/sec 每秒收到的千字节
查看mysql数据库剩余库存数:还有4818

1.5 jvm锁
使用jvm锁(synchronized关键字或者ReetrantLock)试试:
/*** 使用jvm锁来解决超卖问题*/public synchronized void deduct() {// 先查询库存是否充足Stock stock = this.stockMapper.selectById(1L);// 再减库存if (stock != null && stock.getCount() > 0) {stock.setCount(stock.getCount() - 1);this.stockMapper.updateById(stock);}}
重启tomcat服务,再次使用jmeter压力测试,效果如下:

可以看到,加锁之后,吞吐量减少了一倍多!
查看mysql数据库:

并没有发生超卖现象,完美解决。
原理
添加synchronized关键字之后,同一时刻只有一个请求能够获取到锁,并减库存。此时,所有请求只会one-by-one执行下去,也就不会发生超卖现象

1.6 三种情况导致Jvm本地锁失效
1、多例模式下,Jvm本地锁失效
原理:StockService有多个对象,不同的对象持有不同的锁,所以还是会有多个线程进入到 临界区 中
演示:
@Service
@Scope(value = "prototype",proxyMode = ScopedProxyMode.TARGET_CLASS)
public class StockService {@Autowiredprivate StockMapper stockMapper;/*** 使用jvm锁来解决超卖问题*/public synchronized void deduct() {// 先查询库存是否充足Stock stock = this.stockMapper.selectById(1L);// 再减库存if (stock != null && stock.getCount() > 0) {stock.setCount(stock.getCount() - 1);this.stockMapper.updateById(stock);}}
}
重启tomcat服务,再次使用jmeter压力测试,查看数据库,发现库存确实没有减到 0 ,发生超卖

2、Spring的事务导致Jvm本地锁失效
在加锁的地方加上 @Transactional 注解
@Transactionalpublic synchronized void deduct() {// 先查询库存是否充足Stock stock = this.stockMapper.selectById(1L);// 再减库存if (stock != null && stock.getCount() > 0) {stock.setCount(stock.getCount() - 1);this.stockMapper.updateById(stock);}}
重启tomcat服务,再次使用jmeter压力测试,查看数据库,发现库存确实没有减到 0 ,发生超卖

造成超卖的原因:
Spring事务默认的隔离级别是可重复读

解决办法
扩大锁的范围,将开启事务,提交事务也包括在锁的代码块中
@GetMapping("stock/deduct")public String deduct(){synchronized (this) {this.stockService.deduct();}return "hello stock deduct!!";}
3、集群部署导致Jvm本地锁失效
使用jvm锁在单工程单服务情况下确实没有问题,但是在集群情况下会怎样?
接下启动多个服务并使用nginx负载均衡
1)启动两个服务(端口号分别10010 10086),如下:

2)配置nginx 负载均衡
#user nobody;
worker_processes 1;#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;#pid logs/nginx.pid;events {worker_connections 1024;
}http {include mime.types;default_type application/octet-stream;sendfile on;upstream distributed {server localhost:10010;server localhost:10086;}server {listen 80;server_name localhost;location / {proxy_pass http://distributed;}}
}
3)在post中测试:http://localhost/stock/deduct (其中80是nginx的监听端口)

请求正常,说明nginx负载均衡起作用了
4) Jmeter压力测试
注意
- 先把数据库库存量还原到5000
- 重新配置访问路径 http://localhost:80/stock/deduct

两台机器时,吞吐量明显大于单个机器
查看数据库,库存不为0,表示多服务时,Jvm锁失效

5) 原因
每个服务都有自己的本地锁,所以无法锁住临界区,导致多线程的安全问题
1.7 mysql锁演示
除了使用jvm锁之外,还可以使用mysql自带的锁:悲观锁 或者 乐观锁
1.7.1、一个sql
update db_stock set count = count - 1 where product_code = '1001' and count >= #{count}
public void deduct() {this.stockMapper.updateStock("1001", 1);}public interface StockMapper extends BaseMapper<Stock> {@Update("update db_stock set count = count - #{count} where product_code = #{productCode} and count >= #{count}")int updateStock(@Param("productCode") String productCode, @Param("count") Integer count);
}
这种方式可以解决上述Jvm锁失效的三个问题
缺点:
1、确定好锁范围
当使用的是表锁时,会导致系统的吞吐量直线下降
什么情况下会使用行级锁
1)锁的查询或者更新条件必须是索引字段
2) 查询或者更新条件必须是具体值
2、一件商品多个仓库问题无法处理
3、无法记录仓库变化前后的状态
1.7.2、悲观锁
SELECT ... FOR UPDATE (悲观锁)
代码实现
改造StockService: 添加事务注解,去掉synchronized关键词
@Transactionalpublic void deduct() {Stock stocks = this.stockMapper.queryStockForUpdate("1001");if (stocks != null && stocks.getCount() > 0) {stocks.setCount(stocks.getCount() - 1);this.stockMapper.updateById(stocks);}}
在StockeMapper中定义selectStockForUpdate方法:
public interface StockMapper extends BaseMapper<Stock> {@Update("update db_stock set count = count - #{count} where product_code = #{productCode} and count >= #{count}")int updateStock(@Param("productCode") String productCode, @Param("count") Integer count);@Select("select * from db_stock where product_code = #{productCode} for update")Stock queryStockForUpdate(@Param("productCode") String productCode);
}
压力测试
注意:测试之前,需要把库存量改成5000。压测数据如下:比jvm锁性能高很多

mysql数据库存:

【注意】使用MySQL乐观锁时,也需要注意锁的粒度,尽量使用行级锁,否则系统吞吐量会降低
1.7.3、乐观锁
乐观锁是相对悲观锁而言,乐观锁假设认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则重试。
使用数据版本(Version)记录机制实现,这是乐观锁最常用的实现 方式。一般是通过为数据库表增加一个数字类型的 “version” 字段来实现。当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值加一。当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的version值进行比对,如果数据库表当前版本号与第一次取出来的version值相等,则予以更新。
给db_stock表添加version字段:

改造 StockService
/*** 使用MySQL乐观锁来解决库存超卖问题*/public void deduct() {// 先查询库存是否充足Stock stock = this.stockMapper.selectById(1L);// 再减库存if (stock != null && stock.getCount() > 0){// 获取版本号Long version = stock.getVersion();stock.setCount(stock.getCount() - 1);// 每次更新 版本号 + 1stock.setVersion(stock.getVersion() + 1);// 更新之前先判断是否是之前查询的那个版本,如果不是重试if (this.stockMapper.update(stock, new UpdateWrapper<Stock>().eq("id", stock.getId()).eq("version", version)) == 0) {deduct();}}}
重启后使用jmeter压力测试工具结果如下:


并发度比较低,说明乐观锁在并发量越大的情况下,性能越低(因为需要大量的重试);并发量越小,性能越高。
乐观锁存在的问题
- 高并发情况下,性能较低
- ABA问题
- 读写分离的情况下,可能会导致乐观锁不可靠
1.7.4、mysql锁总结
性能:一个sql > 悲观锁 > jvm锁 > 乐观锁
- 如果追求极致性能、业务场景简单并且不需要记录数据前后变化的情况下。
优先选择:一个sql
-
如果写并发量较低(多读),争抢不是很激烈的情况下优先选择:乐观锁
-
如果写并发量较高,一般会经常冲突,此时选择乐观锁的话,会导致业务代码不间断的重试。
优先选择:mysql悲观锁
- 不推荐jvm本地锁。
1.8 redis乐观锁
1.8.1 引入redis
见我的博客 https://blog.csdn.net/hc1285653662/article/details/127564372 中的SpringDataRedis客户端
改造StockService
/*** 为了提高请求响应的速度,将库存放在redis中进行操作*/public void deduct() {// 先查询库存是否充足String stockStr = redisTemplate.opsForValue().get("stock:" + "1001");Long stock = Long.parseLong(stockStr);if (stock != null && stock > 0) {redisTemplate.opsForValue().set("stock:" + "1001", String.valueOf(stock - 1));}}
演示redis库存超卖
设置redis库存为 5000

jmeter启动测试,可以看到并发比无锁时候的mysql库存要高

查询redis库存,发现剩余库存不为0,所以发生超卖现象

1.8.2 redis乐观锁原理
使用watch命令监视某个key,如果在监视的过程中该key被某个客户端修改后,那么自身对于key的修改将会失败

1.8.3 redis乐观锁解决超卖问题
改造StockService
/*** 为了提高请求响应的速度,将库存放在redis中进行操作*/public void deduct() {// 监听 stock:1001redisTemplate.execute(new SessionCallback<Object>() {@Overridepublic Object execute(RedisOperations operations) throws DataAccessException {operations.watch("stock:" + "1001");String stockStr = (String) operations.opsForValue().get("stock:" + "1001");Long stock = Long.parseLong(stockStr);if (stock != null && stock > 0) {operations.multi();operations.opsForValue().set("stock:" + "1001", String.valueOf(stock - 1));List exec = operations.exec();// 如果减库存失败,代表key别其他客户端修改了,则进行重试if (exec == null || exec.size() == 0) {try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}deduct();}return exec;}return null;}});}
查看测试结果:发现并发很低(可能因为我redis部署在阿里云上的docker里,网络开销导致并发很低),但是确实解决超卖问题


1.8.4 redis乐观锁的缺点
- 性能问题
相关文章:
1、传统锁回顾(Jvm本地锁,MySQL悲观锁、乐观锁)
目录 1.1 从减库存聊起1.2 环境准备1.3 简单实现减库存1.4 演示超卖现象1.5 jvm锁1.6 三种情况导致Jvm本地锁失效1、多例模式下,Jvm本地锁失效2、Spring的事务导致Jvm本地锁失效3、集群部署导致Jvm本地锁失效 1.7 mysql锁演示1.7.1、一个sql1.7.2、悲观锁1.7.3、乐观…...
【Java||牛客】DFS应用迷宫问题
step by step. 题目: 描述 定义一个二维数组 N*M ,如 5 5 数组下所示: int maze[5][5] { 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, }; 它表示一个迷宫,其中的1表示墙壁,0表示可…...
【vue】Vue中class样式的动态绑定
简介:Vue中class样式的绑定 1、字符串写法 使用场景:样式的类型不确定 写法: <div :class"xd_bg">测试账号</div> 手动触发样式改变 注意:字符串使用的是vue实例data中已有的属性 2、对象写法 使…...
机器学习深度学习——随机梯度下降算法(及其优化)
在我们没有办法得到解析解的时候,我们可以用过梯度下降来进行优化,这种方法几乎可以所有深度学习模型。 关于优化的东西,我自己曾经研究过智能排班算法和优化,所以关于如何找局部最小值,以及如何跳出局部最小值的一些基…...
【MTK平台】【wpa_supplicant】关于wpa_supplicant_8/src/p2p/p2p.c文件的介绍
本文主要介绍external/wpa_supplicant_8/src/p2p/p2p.c文件 先看下p2p_find 这个方法 P2P_find 主要用于 P2P(点对点)网络中查找其他对等方的功能。另外可以看到设置P2P模块的状态为 P2P_SEARCH int p2p_find(struct p2p_data *p2p, unsigned int tim…...
华为数通HCIP-流量过滤与转发路径控制
流量控制 分类:流量过滤、流量转发路径控制; 特点:1、作用于数据层面/转发层面; 2、不会影响路由表,针对转发流量生效; 实现步骤: 1、通过流量匹配工具匹配流量(ACL…...
SpringBoot中定时任务开启多线程避免多任务堵塞
场景 SpringBoot中定时任务与异步定时任务的实现: SpringBoot中定时任务与异步定时任务的实现_霸道流氓气质的博客-CSDN博客 使用SpringBoot原生方式实现定时任务,已经开启多线程支持,以上是方式之一。 除此之外还可通过如下方式。 为什…...
回归预测 | MATLAB实现SO-CNN-BiLSTM蛇群算法优化卷积双向长短期记忆神经网络多输入单输出回归预测
回归预测 | MATLAB实现SO-CNN-BiLSTM蛇群算法优化卷积双向长短期记忆神经网络多输入单输出回归预测 目录 回归预测 | MATLAB实现SO-CNN-BiLSTM蛇群算法优化卷积双向长短期记忆神经网络多输入单输出回归预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 Matlab实…...
入侵检测——IDS概述、签名技术
1. 什么是IDS? IDS(intrusion detection system)入侵检测系统,是一种对网络传输进行即时监视,在发现可疑传输时发出警报或者采取主动反应措施的网络安全设备。它会对系统的运行状态进行监视,发现各种攻击企…...
golang 标准库json Marshal 序列化与反序列化
标准库代码 func Marshal(v any) ([]byte, error) {e : newEncodeState()defer encodeStatePool.Put(e)err : e.marshal(v, encOpts{escapeHTML: true})if err ! nil {return nil, err}buf : append([]byte(nil), e.Bytes()...)return buf, nil }func Unmarshal(data []byte, …...
【【51单片机AD/DA的分析】】
51单片机AD/DA的分析 看似单片机实验,其实是要学好数电 模数转换 与 数模转换 运算放大器 DA的转换就是利用运算放大器实现的 输出电压v0-(D7~D0)/256 x (VrefxRfb)/R D7~D0 就是我们控制的按键看输入多少 然后再划分256份 Vref是我们设置的一个基准电压 PWM 这种…...
在docker中安装使用达梦数据库
关于在docker中安装达梦数据库,达梦官方网站其实是有提供安装使用方法的,但可能还是有朋友不会,这里将在原文基础上简单扩充下。 注意:docker容器中,数据库安装后没有创建服务的脚本,只有bin、bin2、conf、…...
Leetcode-每日一题【剑指 Offer II 010. 和为 k 的子数组】
题目 给定一个整数数组和一个整数 k ,请找到该数组中和为 k 的连续子数组的个数。 示例 1: 输入:nums [1,1,1], k 2输出: 2解释: 此题 [1,1] 与 [1,1] 为两种不同的情况 示例 2: 输入:nums [1,2,3], k 3输出: 2 提示: 1 < nums.leng…...
【JavaScript】使用Promise来处理异步调用,方法传入参数为接口,并回调接口的方法
例如我们在下面这个方法传入一个接口,并将方法的执行过程用传入的接口进行回调 connect() {wx.connectSocket({url: this.url,success: () > {console.log(WebSocket 连接创建成功);},fail: (err) > {console.error(WebSocket 连接创建失败, err);}});wx.onS…...
grid map学习笔记1之Ubuntu18.04+ROS-melodic编译安装grid_map栅格地图及示例运行
文章目录 0 引言1 安装依赖和编译1.1 安装依赖1.2 下载编译 2 运行示例2.1 simple_demo2.2 tutorial_demo2.3 iterators_demo2.4 image_to_gridmap_demo2.5 grid_map_to_image_demo2.6 opencv_demo2.7 resolution_change_demo2.8 filters_demo2.9 interpolation_demo 0 引言 苏…...
postgres wal2json插件jsonb字段数据丢失问题解决
使用pgwal2jsondebezium进行数据同步时,发现偶尔会有jsonb字段数据丢失的问题 进行测试时发现: 1、发生数据丢失的jsonb字段长度都比较大(超过toast阈值,使用toast表存储) 2、针对发生jsonb字段丢失的数据,jsonb字段本身未发生修…...
华为eNSP:路由引入
一、拓扑图 二、路由器的配置 1、配置路由器的IP AR1: [Huawei]int g0/0/0 [Huawei-GigabitEthernet0/0/0]ip add 1.1.1.1 24 [Huawei-GigabitEthernet0/0/0]qu AR2: [Huawei]int g0/0/0 [Huawei-GigabitEthernet0/0/0]ip add 1.1.1.2 24 [Huaw…...
Retrospectives on the Embodied AI Workshop(嵌入式人工智能研讨会回顾) 论文阅读
论文信息 题目:Retrospectives on the Embodied AI Workshop 作者:Matt Deitke, Dhruv Batra, Yonatan Bisk 来源:arXiv 论文地址:https://arxiv.org/pdf/2210.06849 Abstract 我们的分析重点关注 CVPR Embodied AI Workshop 上…...
「JVM」Full GC和Minor GC、Major GC
Full GC和Minor GC、Major GC 一、Full GC1、什么是Full GC?2、什么情况下会触发full gc? 二、Minor GC1、什么是Minor GC?2、什么情况下会触发Minor GC? 三、Major GC1、什么是Major GC?2、什么情况下会触发Major GC?…...
Asp.Net MVC 使用Log4Net
Asp.Net MVC 使用Log4Net 在 ASP.NET MVC 中使用 Log4net 需要进行一些配置和代码集成。下面是在 ASP.NET MVC 中使用 Log4net 的步骤: 1. 安装 Log4net NuGet 包 打开 NuGet 包管理器控制台,并运行以下命令来安装 Log4net: Install-Pack…...
XML Group端口详解
在XML数据映射过程中,经常需要对数据进行分组聚合操作。例如,当处理包含多个物料明细的XML文件时,可能需要将相同物料号的明细归为一组,或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码,增加了开…...
第19节 Node.js Express 框架
Express 是一个为Node.js设计的web开发框架,它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用,和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...
19c补丁后oracle属主变化,导致不能识别磁盘组
补丁后服务器重启,数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后,存在与用户组权限相关的问题。具体表现为,Oracle 实例的运行用户(oracle)和集…...
超短脉冲激光自聚焦效应
前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应,这是一种非线性光学现象,主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场,对材料产生非线性响应,可能…...
【入坑系列】TiDB 强制索引在不同库下不生效问题
文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...
23-Oracle 23 ai 区块链表(Blockchain Table)
小伙伴有没有在金融强合规的领域中遇见,必须要保持数据不可变,管理员都无法修改和留痕的要求。比如医疗的电子病历中,影像检查检验结果不可篡改行的,药品追溯过程中数据只可插入无法删除的特性需求;登录日志、修改日志…...
条件运算符
C中的三目运算符(也称条件运算符,英文:ternary operator)是一种简洁的条件选择语句,语法如下: 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true,则整个表达式的结果为“表达式1”…...
React Native在HarmonyOS 5.0阅读类应用开发中的实践
一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强,React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 (1)使用React Native…...
最新SpringBoot+SpringCloud+Nacos微服务框架分享
文章目录 前言一、服务规划二、架构核心1.cloud的pom2.gateway的异常handler3.gateway的filter4、admin的pom5、admin的登录核心 三、code-helper分享总结 前言 最近有个活蛮赶的,根据Excel列的需求预估的工时直接打骨折,不要问我为什么,主要…...
九天毕昇深度学习平台 | 如何安装库?
pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子: 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...
