当前位置: 首页 > news >正文

Redisson常用方法

Redisson

参考: 原文链接

定义:Redisson 是一个用于与 Redis 进行交互的 Java 客户端库

优点:很多

1. 入门

1.1 安装

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

1.2 配置

@Configuration
public class RedissonConfig {@Beanpublic RedissonClient redissonClient(){// 配置Config config = new Config();config.useSingleServer().setAddress("redis://192.168.133.136:6379").setPassword("111111");// 创建RedissonClient对象return Redisson.create(config);}
}
---------------------------------使用yml配置-----------------------------------------
redisson:singleServerConfig:address: "redis://192.168.133.136:6379"  # Redis 服务器地址password: "111111"  # 如果有密码,填入密码connectionMinimumIdleSize: 10  # 最小空闲连接数connectionPoolSize: 64  # 连接池大小idleConnectionTimeout: 10000  # 空闲连接最大存活时间connectTimeout: 10000  # 连接超时timeout: 10000  # 请求超时retryAttempts: 3  # 重试次数retryInterval: 1500  # 重试间隔(毫秒)

1.3 使用

@Autowired
private RedissonClient redissonClient;@Test
void testRedisson() throws Exception {//获取锁(可重入),指定锁的名称RLock lock = redissonClient.getLock("sanjin");//尝试获取锁,参数分别是:获取锁的最大等待时间(期间会重试),锁自动释放时间,时间单位boolean isLock = lock.tryLock(1, 10, TimeUnit.SECONDS);//判断获取锁成功if (isLock) {try {System.out.println("执行业务");} finally {//释放锁lock.unlock();}}
}
----------------结果------------------
执行业务

2. 可重入锁原理

2.1 加锁

这是可重入锁接口,

public interface RLock extends Lock, RLockAsync {String getName();void lockInterruptibly(long var1, TimeUnit var3) throws InterruptedException;// 有等待时间boolean tryLock(long var1, long var3, TimeUnit var5) throws InterruptedException;// 无等待时间void lock(long var1, TimeUnit var3);boolean forceUnlock();boolean isLocked();boolean isHeldByThread(long var1);boolean isHeldByCurrentThread();int getHoldCount();long remainTimeToLive();
}

看一下lock实现方法, 明白大体逻辑即可

  1. 如果成功,立即返回,如果失败,订阅锁的释放事件
  2. 在锁释放时,重新尝试获取锁,如果仍未成功(又被抢了),根据 TTL 再次等待,直到获取锁成功
  3. 在方法退出前,取消对锁释放事件的订阅,避免资源浪费
private void lock(long leaseTime, TimeUnit unit, boolean interruptibly) throws InterruptedException {// 保证只有锁的持有线程可以释放锁long threadId = Thread.currentThread().getId();// -1通常表示锁的等待时间设置为无限制(立即尝试获取锁)// 如果返回 null,表示成功获取到锁, ttl表示锁的剩余过期时间Long ttl = this.tryAcquire(-1L, leaseTime, unit, threadId);if (ttl != null) {// 如果未获取到锁,订阅锁的释放事件CompletableFuture<RedissonLockEntry> future = this.subscribe(threadId);// 设置订阅超时,防止由于网络或其他问题导致的长时间等待this.pubSub.timeout(future);RedissonLockEntry entry;// 阻塞等待订阅结果if (interruptibly) {entry = (RedissonLockEntry)this.commandExecutor.getInterrupted(future);} else {entry = (RedissonLockEntry)this.commandExecutor.get(future);}try {// 进入一个无限循环,不断尝试重新获取锁,直到成功为止while(true) {// 再次尝试获取锁ttl = this.tryAcquire(-1L, leaseTime, unit, threadId);// 如果返回 null,表示成功获取锁,直接退出方法if (ttl == null) {return;}// 如果返回一个非 null 的值 ttl,表示锁仍被占用,需要根据剩余时间等待if (ttl >= 0L) {try {// 使用计数器(CountDownLatch 的一种实现)来等待锁的释放通知entry.getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);} catch (InterruptedException var14) {if (interruptibly) {throw var14;}entry.getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);}// 如果 TTL 为负数(可能表示锁的持有者未设置自动过期时间),线程将等待一个释放通知} else if (interruptibly) {entry.getLatch().acquire();} else {entry.getLatch().acquireUninterruptibly();}}} finally {// 无论锁是否成功获取,最终都会释放订阅,以避免资源泄漏this.unsubscribe(entry, threadId);}}
}

具体加锁逻辑tryAcquire

  • KEYS[1]:锁的 Redis 键,通常为锁的唯一标识
  • ARGV[1]:锁的过期时间(毫秒)
  • ARGV[2]:当前线程的唯一标识(value)
<T> RFuture<T> tryLockInnerAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {// 异步执行 Redis 的 EVAL 命令return this.evalWriteAsync(this.getRawName(), LongCodec.INSTANCE, command,// 判断锁是否已存在,如果锁不存在,创建锁并设置过期时间"if (redis.call('exists', KEYS[1]) == 0) then " +// 使用 HINCRBY 创建锁并设置当前线程的持有次数为 1,利用hash结构"redis.call('hincrby', KEYS[1], ARGV[2], 1); " +// 设置锁的过期时间,确保锁在持有者崩溃后释放"redis.call('pexpire', KEYS[1], ARGV[1]); " +// 返回 nil 表示锁已成功获取"return nil; " +"end; " +// 锁已被当前线程持有的情况, 判断锁是否被当前线程持有"if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +// 增加持有次数(支持可重入)"redis.call('hincrby', KEYS[1], ARGV[2], 1); " +// 更新锁的过期时间"redis.call('pexpire', KEYS[1], ARGV[1]); " +"return nil; " +"end; " +// 返回锁的剩余过期时间(毫秒),用于通知调用者等待或重试"return redis.call('pttl', KEYS[1]);",Collections.singletonList(this.getRawName()), new Object[]{unit.toMillis(leaseTime), this.getLockName(threadId)});}

具体加锁逻辑如下图

2.2 续锁

主要就是根据leaseTime判断如何操作

指定了leaseTime:设置过期时间为leaseTime,不启用看门狗

不指定leaseTime:设置默认过期时间(30s),并且启用看门狗

指不指定是看 lock.lock() 是否传入了值

private <T> RFuture<Long> tryAcquireAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId) {RFuture ttlRemainingFuture;// 尝试获取锁if (leaseTime > 0L) {// 使用指定的过期时间尝试获取锁, 适合短期锁场景,过期后无需续期ttlRemainingFuture = this.tryLockInnerAsync(waitTime, leaseTime, unit, threadId, RedisCommands.EVAL_LONG);} else {// 使用内部默认的锁租约时间 internalLockLeaseTime(30秒), 适合长期锁场景,通常需要续期机制ttlRemainingFuture = this.tryLockInnerAsync(waitTime, this.internalLockLeaseTime, TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG);}// 处理锁获取结果CompletionStage<Long> f = ttlRemainingFuture.thenApply((ttlRemaining) -> {// 如果 ttlRemaining == null,说明锁成功获取if (ttlRemaining == null) {if (leaseTime > 0L) {// 将 internalLockLeaseTime 更新为指定的租约时间,后续 Redis 锁命令会使用该值设置锁的过期时间this.internalLockLeaseTime = unit.toMillis(leaseTime);} else {// 看门狗机制续租// 调用 scheduleExpirationRenewal 方法,开启后台续期任务,确保锁不会因过期时间耗尽而释放this.scheduleExpirationRenewal(threadId);}}// 表示锁已被其他线程持有,返回锁的剩余有效时间return ttlRemaining;});return new CompletableFutureWrapper(f);
}

2.3 解锁

最外层的解锁方法

public void unlock() {try {// 异步调用解锁方法,并等待其执行完成this.get(this.unlockAsync(Thread.currentThread().getId()));} catch (RedisException var2) {if (var2.getCause() instanceof IllegalMonitorStateException) {throw (IllegalMonitorStateException)var2.getCause();} else {throw var2;}}
}

解锁并处理解锁后的步骤(取消看门狗机制…)

public RFuture<Void> unlockAsync(long threadId) {// 异步调用解锁方法RFuture<Boolean> future = this.unlockInnerAsync(threadId);// 使用 handle() 方法处理解锁结果和异常CompletionStage<Void> f = future.handle((opStatus, e) -> {// 取消锁的续期this.cancelExpirationRenewal(threadId);// 如果异步操作抛出异常,包装并抛出 CompletionExceptionif (e != null) {throw new CompletionException(e);} else if (opStatus == null) {// 如果解锁操作状态为 null,说明当前线程未持有锁,抛出 IllegalMonitorStateExceptionIllegalMonitorStateException cause = new IllegalMonitorStateException("attempt to unlock lock, not locked by current thread by node id: " + this.id + " thread-id: " + threadId);throw new CompletionException(cause);} else {// 如果操作成功,则返回 nullreturn null;}});return new CompletableFutureWrapper(f);
}

具体解锁的redis执行步骤

参数:

  • KEYS[1]:锁的键名。
  • KEYS[2]:用于发布解锁事件的频道名。
  • ARGV[1]:解锁事件消息。
  • ARGV[2]:锁的过期时间。
  • ARGV[3]:锁的名称(用于计数器)。

步骤:

  1. 检查当前线程是否持有锁

  2. 减少锁计数器

  3. 如果计数器值大于零,更新锁的过期时间

  4. 如果计数器值为零,删除锁并发布解锁事件

protected RFuture<Boolean> unlockInnerAsync(long threadId) {return this.evalWriteAsync(this.getRawName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,// 检查指定的锁是否存在。如果不存在(0),返回 nil,表示当前线程没有持有锁。"if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then " +"return nil;" +"end;" +// 将锁的计数器减一。如果当前线程持有锁,这个操作会减少锁的计数器值"local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1);" +// 如果锁计数器值仍大于零,说明有其他线程持有锁,需要更新锁的过期时间"if (counter > 0) then " +// 更新锁的过期时间"redis.call('pexpire', KEYS[1], ARGV[2]); " +// 表示解锁操作成功但锁仍被其他线程持有"return 0; " +"else " +// 如果计数器值为零,说明当前线程是最后一个持有锁的线程// 删除锁"redis.call('del', KEYS[1]); " +// 发布解锁事件通知其他等待的线程"redis.call('publish', KEYS[2], ARGV[1]); " +// 表示解锁成功且锁已被完全释放"return 1; " +"end; " +// 如果条件不满足,返回 nil"return nil;",Arrays.asList(this.getRawName(), this.getChannelName()),new Object[]{LockPubSub.UNLOCK_MESSAGE, this.internalLockLeaseTime, this.getLockName(threadId)});
}

图解:

3. 其他锁

3.1 红锁和多锁的区别

RedLock 是一种更为复杂的分布式锁实现,保证了分布式环境中的高可用性和容错性,但需要多个 Redis 实例进行协调

多锁 的实现简单,但可靠性差,容易受到单点故障的影响,不适合对安全性和可靠性要求较高的应用

特点RedLock多锁(Multiple Locks)
实现方式使用多个独立的 Redis 实例,保证多数节点成功每个 Redis 实例独立设置锁
容错性高,支持在大多数节点上获取锁低,不能保证一致性和容错性
锁的获取需要在大多数实例中成功获取在任意一个实例上获取锁即可
安全性提供了更高的安全性和可靠性相对简单,但不适用于复杂场景
网络分区容忍性可以容忍部分节点失败,但不是所有不适合面对网络分区或节点故障的场景

3.2 简单演示

public static void main(String[] args) {String lockKey = "myLock";Config config = new Config();config.useSingleServer().setPassword("123456").setAddress("redis://127.0.0.1:6379");Config config2 = new Config();config.useSingleServer().setPassword("123456").setAddress("redis://127.0.0.1:6380");Config config3 = new Config();config.useSingleServer().setPassword("123456").setAddress("redis://127.0.0.1:6381");RLock lock = Redisson.create(config).getLock(lockKey);RLock lock2 = Redisson.create(config2).getLock(lockKey);RLock lock3 = Redisson.create(config3).getLock(lockKey);RedissonRedLock redLock = new RedissonRedLock(lock, lock2, lock3);try {redLock.lock();} finally {redLock.unlock();}
}

3.3 CAP之间的取舍

CAP 原则又称 CAP 定理, 指的是在一个分布式系统中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性), 三者不可得兼

一致性© : 在分布式系统中的所有数据备份, 在同一时刻是否同样的值(等同于所有节点访问同一份最新的数据副本)
可用性(A): 在集群中一部分节点故障后, 集群整体是否还能响应客户端的读写请求(对数据更新具备高可用性)
分区容忍性§: 以实际效果而言, 分区相当于对通信的时限要求. 系统如果不能在时限内达成数据一致性, 就意味着发生了分区的情况, 必须就当前操作在 C 和 A 之间做出选择

4. Redisson的限流功能

常见的限流功能:固定窗口算法、滑动窗口算法、漏桶算法、令牌桶算法

利用Redisson的令牌桶限流

@Test
void testLimiter() {// 创建一个限流器RRateLimiter rateLimiter = redissonClient.getRateLimiter("sanjin");// 初始化最大流速为每秒10个令牌rateLimiter.trySetRate(RateType.OVERALL, 10, 1, RateIntervalUnit.SECONDS);for (int i = 0; i < 20; i++) {// 尝试获取一个令牌boolean b = rateLimiter.tryAcquire();if (b) {System.out.println("成功获取第"+ i +"个令牌");} else {System.out.println("被第" + i + "次限流了");}}
}
---------------------------结果-----------------------------------
成功获取第0个令牌
成功获取第1个令牌
成功获取第2个令牌
成功获取第3个令牌
成功获取第4个令牌
成功获取第5个令牌
成功获取第6个令牌
成功获取第7个令牌
成功获取第8个令牌
成功获取第9个令牌
被第10次限流了
被第11次限流了
被第12次限流了
被第13次限流了
被第14次限流了
被第15次限流了
被第16次限流了
被第17次限流了
被第18次限流了
被第19次限流了

相关文章:

Redisson常用方法

Redisson 参考: 原文链接 定义&#xff1a;Redisson 是一个用于与 Redis 进行交互的 Java 客户端库 优点&#xff1a;很多 1. 入门 1.1 安装 <!--redission--> <dependency><groupId>org.redisson</groupId><artifactId>redisson</artifa…...

html自带的input年月日(date) /时间(datetime-local)/星期(week)/月份(month)/时间(time)控件

年月日期控件 type"date" <input type"date" id"StartDate" valueDateTime.Now.ToString("yyyy-MM-dd") /> //设置值 $("#StartDate").val("2024-12-12"); //获取值 var StartDate$("#StartDate&quo…...

CSS系列(12)-- 响应式设计详解

前端技术探索系列&#xff1a;CSS 响应式设计详解 &#x1f4f1; 致读者&#xff1a;掌握响应式设计的艺术 &#x1f44b; 前端开发者们&#xff0c; 今天我们将深入探讨 CSS 响应式设计&#xff0c;学习如何创建适应各种设备的网页布局。 响应式基础 &#x1f680; 视口设…...

filecoin boost GraphQL API 查询

查询示例 查询失败交易 curl -X POST \ -H "Content-Type: application/json" \ -d {"query":"query { deals(limit: 10, query: \"failed to get size of imported\") { deals { ID CreatedAt Message } } }"} \ http://localhost:…...

SAS - Subtractive Port

在SAS&#xff08;串行连接SCSI&#xff0c;Serial Attached SCSI&#xff09;协议中&#xff0c;subtractive port 是一种特殊类型的端口&#xff0c;主要用于设备间的路由功能。它的作用是在路径选择过程中充当默认路径&#xff0c;以处理未明确指定路径的请求。以下是它的定…...

TCP客户端模拟链接websocket服务端

因一些特殊原因研究了下TCP模拟链接websocket。原理上可以连接但具体怎么连接怎么操作就不知道了&#xff0c;需要研究下&#xff0c;以下是个人研究的方案。 用线上和本地地址来做例子&#xff1a; 线上wss地址&#xff1a;wss://server.cs.com/cs/vido/1 本地地址ws://127…...

TypeScript 的崛起:全面解析与深度洞察

一、背景与起源 &#xff08;一&#xff09;JavaScript 的局限性 类型系统缺失 难以在编码阶段发现类型相关错误&#xff0c;导致运行时错误频发。例如&#xff0c;将字符串误当作数字进行数学运算&#xff0c;可能在运行时才暴露问题。函数参数类型不明确&#xff0c;容易传入…...

c#笔记2024

Ctrl r e自动添加get和set CompositeCurve3d 复合曲线 List<Entity> entS listline.Cast<Entity>().ToList();//list类型强转 前面拼上\u0003&#xff0c;就可以实现&#xff0c;不管有没有命令都能打断当前命令的效果 取消其他命令&#xff1a;Z.doc.SendStri…...

Hadoop一课一得

Hadoop作为大数据时代的奠基技术之一&#xff0c;自问世以来就深刻改变了海量数据存储与处理的方式。本文将带您深入了解Hadoop&#xff0c;从其起源、核心架构、关键组件&#xff0c;到典型应用场景&#xff0c;并结合代码示例和图示&#xff0c;帮助您更好地掌握Hadoop的实战…...

AI生成图表化:深入探索Mermaid

引言 在使用生成式AI时&#xff0c;只要你提出让AI帮你生成mermaid图&#xff0c;AI的生成就会出现丰富的图形&#xff01; 在现代文档编写中&#xff0c;图表的使用不仅能增强文档的可读性&#xff0c;还能更直观地表达复杂的概念和流程。Mermaid 作为一款开源的图表绘制工具…...

25.DDD数量关系

学习视频来源&#xff1a;DDD独家秘籍视频合集 https://space.bilibili.com/24690212/channel/collectiondetail?sid1940048&ctype0 文章目录 关系型数据库的数量关系领域模型的数量关系实现聚合数量关系聚合内聚合间具体说明代码 数量关系是本质吗&#xff1f;领域对象之…...

Linux应用开发————线程池

线程池 定义&#xff1a;简单来说&#xff0c;就是存放多个线程的池子。当创建线程池时&#xff0c;就给池中存放一些线程&#xff0c;如果有任务要执行&#xff0c;就从池中取出一个线程执行任务&#xff0c;依次类推&#xff1b;当所有线程都在执行任务时&#xff0c;其他任务…...

Spring Boot 集成阿里云OSS 完成文件上传下载

前言&#xff1a; 文件上传下载在项目开发中是一个非常常见的业务场景&#xff0c;在云服务上还没有兴起的时候&#xff0c;一般来说都会把文件单独存放到文件服务器上&#xff0c;随着云服务的兴起&#xff0c;各类云服务厂商都提供了 OSS 服务&#xff0c;本篇我们分享 Spri…...

使用ERA5数据绘制风向玫瑰图的简易流程

使用ERA5数据绘制风向玫瑰图的简易流程 今天需要做一个2017年-2023年的平均风向的统计,做一个风向玫瑰图&#xff0c;想到的还是高分辨率的ERA5land的数据&#xff08;0.1分辨率&#xff0c;逐小时分辨率&#xff0c;1950年至今&#xff09;。 风向&#xff0c;我分为了16个&…...

测试脚本并发多进程:pytest-xdist用法

参考&#xff1a;https://www.cnblogs.com/poloyy/p/12694861.html pytest-xdist详解&#xff1a; https://www.cnblogs.com/poloyy/p/14708825.html 总 https://www.cnblogs.com/poloyy/category/1690628.html...

数据可视化的Python实现

一、GDELT介绍 GDELT ( www.gdeltproject.org ) 每时每刻监控着每个国家的几乎每个角落的 100 多种语言的新闻媒体 -- 印刷的、广播的和web 形式的&#xff0c;识别人员、位置、组织、数量、主题、数据源、情绪、报价、图片和每秒都在推动全球社会的事件&#xff0c;GDELT 为全…...

【Linux系列】Linux 系统配置文件详解:`/etc/profile`、`~/.bashrc` 和 `~/.bash_profile`

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...

uni-app实现小程序、H5图片轮播预览、双指缩放、双击放大、单击还原、滑动切换功能

前言 这次的标题有点长&#xff0c;主要是想要表述的功能点有点多&#xff1b; 简单做一下需求描述 产品要求在商品详情页的头部轮播图部分&#xff0c;可以单击预览大图&#xff0c;同时在预览界面可以双指放大缩小图片并且可以移动查看图片&#xff0c;双击放大&#xff0…...

游戏引擎学习第45天

仓库: https://gitee.com/mrxiao_com/2d_game 回顾 我们刚刚开始研究运动方程&#xff0c;展示了如何处理当人物遇到障碍物时的情况。有一种版本是角色会从障碍物上反弹&#xff0c;而另一版本是角色会完全停下来。这种方式感觉不太自然&#xff0c;因为在游戏中&#xff0c;…...

electron常用方法

一&#xff0c;,electron设置去除顶部导航栏和menu 1&#xff0c;electron项目 在创建BrowserWindow实例的main.js页面添加frame&#xff1a;false属性 2&#xff0c;electron-vue项目 在src/main/index.js文件下找到创建窗口的方法&#xff08;createWindow&#xff09;&…...

写作-- 复合句练习

文章目录 练习 11. 家庭的支持和老师的指导对学生的学术成功有积极影响。2. 缺乏准备和未能适应通常会导致在挑战性情境中的糟糕表现。3. 吃垃圾食品和忽视锻炼可能导致严重的健康问题,因此人们应注重保持均衡的生活方式。4. 昨天的大雨导致街道洪水泛滥,因此居民们迁往高地以…...

聊聊网络变压器的浪涌等级标准是怎样划分的呢?

Hqst盈盛&#xff08;华强盛&#xff09;电子导读&#xff1a;聊聊网络变压器的浪涌等级标准是怎样划分的呢&#xff1f; 在和做防雷产品的客户的深度沟通网络变压器产品选型中发现&#xff1a;客户对网络变压器的浪涌等级划分也很希望有更深的了解&#xff0c;今天就这个问题和…...

ADB推送文件到指定路径解析

您执行的命令 adb push ota.zip /sdcard/Download 中&#xff0c;目标路径 /sdcard/Download 是您显式指定的&#xff0c;因此 ADB 会直接将文件推送到此位置。具体过程如下&#xff1a; 1. 命令结构解析 adb push&#xff1a;ADB 的推送指令。ota.zip&#xff1a;本地计算机上…...

【前端】【css预处理器】Sass与Less全面对比与构建对应知识体系

第一章 概述 1.1 Sass和Less简介 1.1.1 Sass简介 Sass&#xff08;Syntactically Awesome Stylesheets&#xff09;是一种 CSS 预处理器&#xff0c;它为 CSS 赋予了更多强大的功能和更灵活的语法结构&#x1f60e;。 语法特点&#xff1a;Sass 有两种语法格式。一种是 .sa…...

记一次前端逻辑绕过登录到内网挖掘

前言 在测试一个学校网站的时候&#xff0c;发现一个未授权访问内网系统&#xff0c;但是这个未授权并不是接口啥的&#xff0c;而是对前端 js 的审计和调试发现的漏洞&#xff0c;这里给大家分享一下这次的漏洞的过程。 进入内网的过程 可以看到是一个图书馆的网站&#xff…...

时序模型介绍

一.整体介绍 1.单变量 vs 多变量时序数据 单变量就是只根据时间预测&#xff0c;多变量还要考虑用户 2.为什么不能用机器学习预测&#xff1a; a.时间不是影响标签的关键因素 b.时间与标签之间的联系过于弱/过于复杂&#xff0c;因此时序模型依赖于时间与时间的相关性来进行预…...

Fine-tuning:微调技术,训练方式,LLaMA-Factory,ms-swift

1&#xff0c;微调技术 特征Full-tuningFreeze-tuningLoRAQLoRA训练参数量全部少量极少极少显存需求高低很低最低模型性能最佳中等较好接近 LoRA模型修改方式无变化局部冻结插入模块量化插入模块多任务共享不便较便非常适合非常适合适合超大模型微调❌✅✅✅&#xff08;最优&…...

C# 打印PDF的常用方法

这里先提供一个helper类的模板 1.使用默认程序打印 using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing.Printing; using System.IO; using System.Runtime.InteropServices;namespace PDF {public static class PrintHelper{#…...

LLaMaFactory 微调QwenCoder模型

步骤一&#xff1a;准备LLamaFactory环境 首先,让我们尝试使用github的方式克隆仓库: git config --global http.sslVerify false && git clone --depth 1 https://github.com/hiyouga/LLaMA-Factory.git # 创建新环境&#xff0c;指定 Python 版本&#xff08;以 3.…...

零基础设计模式——结构型模式 - 代理模式

第三部分&#xff1a;结构型模式 - 代理模式 (Proxy Pattern) 在学习了享元模式如何通过共享对象来优化资源使用后&#xff0c;我们来探讨结构型模式的最后一个模式——代理模式。代理模式为另一个对象提供一个替身或占位符以控制对这个对象的访问。 核心思想&#xff1a;为其…...