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…...
实现弹窗随键盘上移居中
实现弹窗随键盘上移的核心思路 在Android中,可以通过监听键盘的显示和隐藏事件,动态调整弹窗的位置。关键点在于获取键盘高度,并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...

2025季度云服务器排行榜
在全球云服务器市场,各厂商的排名和地位并非一成不变,而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势,对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析: 一、全球“三巨头”…...

RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)
RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发,后来由Pivotal Software Inc.(现为VMware子公司)接管。RabbitMQ 是一个开源的消息代理和队列服务器,用 Erlang 语言编写。广泛应用于各种分布…...

MySQL的pymysql操作
本章是MySQL的最后一章,MySQL到此完结,下一站Hadoop!!! 这章很简单,完整代码在最后,详细讲解之前python课程里面也有,感兴趣的可以往前找一下 一、查询操作 我们需要打开pycharm …...
API网关Kong的鉴权与限流:高并发场景下的核心实践
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 引言 在微服务架构中,API网关承担着流量调度、安全防护和协议转换的核心职责。作为云原生时代的代表性网关,Kong凭借其插件化架构…...

解析两阶段提交与三阶段提交的核心差异及MySQL实现方案
引言 在分布式系统的事务处理中,如何保障跨节点数据操作的一致性始终是核心挑战。经典的两阶段提交协议(2PC)通过准备阶段与提交阶段的协调机制,以同步决策模式确保事务原子性。其改进版本三阶段提交协议(3PC…...
用鸿蒙HarmonyOS5实现国际象棋小游戏的过程
下面是一个基于鸿蒙OS (HarmonyOS) 的国际象棋小游戏的完整实现代码,使用Java语言和鸿蒙的Ability框架。 1. 项目结构 /src/main/java/com/example/chess/├── MainAbilitySlice.java // 主界面逻辑├── ChessView.java // 游戏视图和逻辑├── …...

Pandas 可视化集成:数据科学家的高效绘图指南
为什么选择 Pandas 进行数据可视化? 在数据科学和分析领域,可视化是理解数据、发现模式和传达见解的关键步骤。Python 生态系统提供了多种可视化工具,如 Matplotlib、Seaborn、Plotly 等,但 Pandas 内置的可视化功能因其与数据结…...
MeanFlow:何凯明新作,单步去噪图像生成新SOTA
1.简介 这篇文章介绍了一种名为MeanFlow的新型生成模型框架,旨在通过单步生成过程高效地将先验分布转换为数据分布。文章的核心创新在于引入了平均速度的概念,这一概念的引入使得模型能够通过单次函数评估完成从先验分布到数据分布的转换,显…...