分布式锁总结
文章目录
- 分布式锁
- 什么是分布式锁?
- 分布式锁的实现方式
- 基于数据库(mysql)实现
- 基于缓存(redis)
- 多实例并发访问问题演示
- 项目代码(使用redis)
- 配置nginx.conf
- jmeter压测复现问题
- 并发是1,即不产生并发问题
- 并发30测试,产生并发问题(虽然单实例是`synchronized`,但是这是分布式多实例)
- redis 分布式锁:setnx实现
- 分布式锁的过期时间和看门狗
- 附:redis setnx相关命令和分布式锁
- Redisson
- 代码&测试
- Redisson 底层原理
- 实现可重入锁
- redis分布式锁的问题?
- redis主从架构问题?
- Redlock(超半数加锁成功才成功)
- 高并发分布式锁如何实现
- 基于ZooKeeper实现
- zookeeper节点类型
- zookeeper的watch机制
- zookeeper lock
- 普通临时节点(羊群效应)
- 顺序节点(公平,避免羊群效应)
- Curator InterProcessMutex(可重入公平锁)
- code&测试
- InterProcessMutex 内部原理
- 初始化
- 加锁
- watch
- 释放锁
- redis vs zookeeper AI回答
分布式锁
什么是分布式锁?
锁:共享资源;共享资源互斥的;多任务环境
分布式锁:如果多任务是多个JVM进程,需要一个外部锁,而不是JDK提供的锁
在分布式的部署环境下,通过锁机制来让多客户端互斥的对共享资源进行访问
-
排它性:在同一时间只会有一个客户端能获取到锁,其它客户端无法同时获取
-
避免死锁:这把锁在一段有限的时间之后,一定会被释放(正常释放或异常释放)
-
高可用:获取或释放锁的机制必须高可用且性能佳
分布式锁的实现方式
基于数据库(mysql)实现
新建一个锁表
CREATE TABLE `methodLock` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
`method_name` varchar(64) NOT NULL DEFAULT '' COMMENT '锁定的方法名',
`desc` varchar(1024) NOT NULL DEFAULT '备注信息',
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '保存数据时间,自动生成',
PRIMARY KEY (`id`),
UNIQUE KEY `uidx_method_name` (`method_name `) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='锁定中的方法';
-
insert, delete(method_name有唯一约束)
缺点:- 数据库单点会导致业务不可用
- 锁没有失效时间:一旦解锁操作失败,就会导致锁记录一直在数据库中,其它线程无法再获得到锁。
- 非重入锁:同一个线程在没有释放锁之前无法再次获得该锁。因为数据中数据已经存在记录了
- 非公平锁
-
通过数据库的
排它锁
来实现
在查询语句后面增加for update
(表锁,行锁),数据库会在查询过程中给数据库表增加排它锁
。当某条记录被加上排他锁之后,其它线程无法再在该行记录上增加排它锁
。可以认为获得排它锁的线程即可获得分布式锁,当获取到锁之后,可以执行方法的业务逻辑,执行完方法之后,再通过connection.commit()
操作来释放锁
public boolean lock(){connection.setAutoCommit(false)while (true) {try {result = select * from methodLock where method_name=xxx for update;if (result == null) {return true;}} catch (Exception e) {}sleep(1000);}return false;
}public void unlock(){connection.commit();
}
基于缓存(redis)
多实例并发访问问题演示
项目代码(使用redis)
见项目代码:减库存的例子
- 让Springboot项目启动两个实例(即有两个JVM进程)
curl -X POST \http://localhost:8088/deduct_stock_sync \-H 'Content-Type: application/json'curl -X POST \http://localhost:8089/deduct_stock_sync \-H 'Content-Type: application/json'
减库存调用测试
配置nginx.conf
http {include mime.types;default_type application/octet-stream;#log_format main '$remote_addr - $remote_user [$time_local] "$request" '# '$status $body_bytes_sent "$http_referer" '# '"$http_user_agent" "$http_x_forwarded_for"';#access_log logs/access.log main;sendfile on;#tcp_nopush on;#keepalive_timeout 0;keepalive_timeout 65;#gzip on;upstream redislock{server localhost:8088 weight=1;server localhost:8089 weight=1;}server {listen 8080;server_name localhost;#charset koi8-r;#access_log logs/host.access.log main;location / {root html;index index.html index.htm;proxy_pass http://redislock;}}
}
- nginx启动和关闭命令
mubi@mubideMacBook-Pro nginx $ sudo nginx
mubi@mubideMacBook-Pro nginx $ ps -ef | grep nginx0 47802 1 0 1:18下午 ?? 0:00.00 nginx: master process nginx-2 47803 47802 0 1:18下午 ?? 0:00.00 nginx: worker process501 47835 20264 0 1:18下午 ttys001 0:00.00 grep --color=always nginx
mubi@mubideMacBook-Pro nginx $
sudo nginx -s stop
- 访问测试
curl -X POST \http://localhost:8080/deduct_stock_sync \-H 'Content-Type: application/json'
jmeter压测复现问题
- redis 设置 stock 为 100
并发是1,即不产生并发问题
库存减少30, redis get结果会是最终的70
,符合
并发30测试,产生并发问题(虽然单实例是synchronized
,但是这是分布式多实例)
- 并发30访问测试结果:并不是最后的
70
redis 分布式锁:setnx实现
- 30的并发失败率是60%,即只有12个成功的,最后redis的stock值是88符合预期
可以看到大部分没有抢到redis锁,而返回了系统繁忙错误
分布式锁的过期时间和看门狗
机器宕机可能导致finally释放锁失败,所以必须为redis key设置一个过期时间,但是设置的过期时间是多少是个问题?
-
超时时间是个问题:因为业务时长不确定的;如果设置短了而业务执行很长,那么会由于过期时间删除了可以,那么锁会被其它业务线程给抢了
-
其它线程可能删除别的线程的锁,因为锁没有什么标记
-
改进1
@PostMapping(value = "/deduct_stock_lock")
public String deductStockLock() throws Exception {// setnx,redis单线程String lockKey = "lockKey";String clientId = UUID.randomUUID().toString();// 如下两句要原子操作
// Boolean setOk = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, lockVal);
// stringRedisTemplate.expire(lockKey, 10 , TimeUnit.SECONDS); // 设置过期时间Boolean setOk = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, clientId, 10, TimeUnit.SECONDS);if (!setOk) {throw new Exception("业务繁忙,请稍后再试");}String retVal;try {// 只有一个线程能执行成功,可能有业务异常抛出来,可能宕机等等;但无论如何要释放锁retVal = stockReduce();} finally {// 可能失败if (clientId.equals(stringRedisTemplate.opsForValue().get(lockKey))) {stringRedisTemplate.delete(lockKey);}}return retVal;
}
- 过期时间短不够的问题:可以不断的定时设置,给锁续命: 看门狗;开启新线程,每隔一段时间,判断锁还在不在,然后重新设置过期时间
- set key,value的时候,value设置为当前线程id,然后删除的时候判断下,确保删除正确
附:redis setnx相关命令和分布式锁
-
setnx(SET if Not eXists)
-
EXPIRE key seconds:设置key 的生存时间,当key过期(生存时间为0),会自动删除
如下,一个原子操作设置key:value,并设置10秒的超时
boolean lock(){ret = set key value(thread Id) 10 nx;if (!ret) {return false;}return true;
}void unlock(){val = get keyif ( val != null && val.equals( thread Id) ) {del key;}
}
Redisson
Redisson是一个基于Redis的Java客户端,提供了分布式锁的实现。其核心通过Redis的Lua脚本和原子操作保证锁的互斥性,支持可重入、公平锁、锁续期等功能。
代码&测试
@Bean
public Redisson redisson(){Config config = new Config();config.useSingleServer().setAddress("redis://localhost:6379").setDatabase(0);return (Redisson)Redisson.create(config);
}
@Autowired
private Redisson redisson;@PostMapping(value = "/deduct_stock_redisson")
public String deductStockRedisson() throws Exception {String lockKey = "lockKey";RLock rLock = redisson.getLock(lockKey);String retVal;try {rLock.lock();// 只有一个线程能执行成功,可能有业务异常抛出来,可能宕机等等;但无论如何要释放锁retVal = stockReduce();} finally {rLock.unlock();}return retVal;
}
如下并发请求毫无问题:
Redisson 底层原理
- setnx的设置key与过期时间用脚本实现原子操作
- key设置成功默认30s,则有后台线程每10秒(1/3的原始过期时间定时检查)检查判断,延长过期时间
- 未获取到锁的线程会自旋,直到那个获取到锁的线程将锁释放
实现可重入锁
value中多存储全局信息,可重入次数相关信息
{"count":1,"expireAt":147506817232,"jvmPid":22224, // jvm进程ID"mac":"28-D2-44-0E-0D-9A", // MAC地址"threadId":14 // 线程Id
}
redis分布式锁的问题?
Redis分布式锁会有个缺陷,就是在Redis哨兵模式下:
客户端1
对某个master节点写入了redisson锁,此时会异步复制给对应的slave节点。但是这个过程中一旦发生master节点宕机,主备切换,slave节点从变为了master节点(但是锁信息是没有的)。这时客户端2
来尝试加锁的时候,在新的master节点上也能加锁,此时就会导致多个客户端对同一个分布式锁完成了加锁。
这时系统在业务语义上一定会出现问题,导致各种脏数据的产生。缺陷在哨兵模式或者主从模式下,如果master实例宕机的时候,可能导致多个客户端同时完成加锁。
redis主从架构问题?
补充知识:redis单机qps支持:10w级别
redis主从架构是主同步到从,如果主
设置key成功,但是同步到从
还没结束就挂了;这样从
成为主,但是是没有key存在的,那么另一个线程又能够加锁成功。(redis主从架构锁失效问题?)
redis无法保证强一致性?zookeeper解决,但是zk性能不如redis
Redlock(超半数加锁成功才成功)
- 加锁失败的回滚
- redis加锁多,性能受影响
高并发分布式锁如何实现
- 分段锁思想
基于ZooKeeper实现
回顾zookeeper的一些相关知识: 文件系统+监听通知机制
zookeeper节点类型
- PERSISTENT-持久节点
除非手动删除,否则节点一直存在于 Zookeeper 上; 重启Zookeeper后也会恢复
- EPHEMERAL-临时节点
临时节点的生命周期与客户端会话绑定,一旦客户端会话失效(客户端与zookeeper连接断开不一定会话失效),那么这个客户端创建的所有临时节点都会被移除。
- PERSISTENT_SEQUENTIAL-持久顺序节点
基本特性同持久节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。
- EPHEMERAL_SEQUENTIAL-临时顺序节点
基本特性同临时节点,增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。
zookeeper的watch机制
- 主动推送:watch被触发时,由zookeeper主动推送给客户端,而不需要客户端轮询
- 一次性:数据变化时,watch只会被触发一次;如果客户端想得到后续更新的通知,必须要在watch被触发后重新注册一个watch
- 可见性:如果一个客户端在读请求中附带 Watch,Watch 被触发的同时再次读取数据,客户端在得到 Watch消息之前肯定不可能看到更新后的数据。换句话说,更新通知先于更新结果
- 顺序性:如果多个更新触发了多个 Watch ,那 Watch 被触发的顺序与更新顺序一致
zookeeper lock
普通临时节点(羊群效应)
比如1000个并发,只有1个客户端获取锁成功,其它999个客户端都处在监听并等待中;如果成功释放锁了,那么999个客户端都监听到,再次继续进行创建锁的流程。
所以每次锁有变化,几乎所有客户端节点都要监听并作出反应,这会给集群带来巨大压力,即为:羊群效应
顺序节点(公平,避免羊群效应)
-
首先需要创建一个父节点,尽量是持久节点(PERSISTENT类型)
-
每个要获得锁的线程都会在这个节点下创建个临时顺序节点,
-
由于序号的递增性,可以规定排号最小的那个获得锁。
-
所以,每个线程在尝试占用锁之前,首先判断自己是排号是不是当前最小,如果是,则获取锁。
利用顺序性:每个线程都只监听前一个线程,事件通知也只通知后面都一个线程,而不是通知全部,从而避免羊群效应
Curator InterProcessMutex(可重入公平锁)
curator官方文档
code&测试
实践代码链接
@Component
public class CuratorConfiguration {@Bean(initMethod = "start")public CuratorFramework curatorFramework() {RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);CuratorFramework client = CuratorFrameworkFactory.newClient("127.0.0.1:2181", retryPolicy);return client;}
}
@Autowired
private CuratorFramework curatorFramework;@PostMapping(value = "/deduct_stock_zk")
public String deductStockZk() throws Exception {String path = "/stock";InterProcessMutex interProcessMutex = new InterProcessMutex(curatorFramework, path);String retVal;try {interProcessMutex.acquire();retVal = stockReduce();} catch (Exception e) {throw new Exception("lock error");} finally {interProcessMutex.release();}return retVal;
}
- 压测结果正常
InterProcessMutex 内部原理
初始化
/**
* @param client client
* @param path the path to lock
* @param driver lock driver
*/
public InterProcessMutex(CuratorFramework client, String path, LockInternalsDriver driver)
{this(client, path, LOCK_NAME, 1, driver);
}
/*** Returns a facade of the current instance that tracks* watchers created and allows a one-shot removal of all watchers* via {@link WatcherRemoveCuratorFramework#removeWatchers()}** @return facade*/
public WatcherRemoveCuratorFramework newWatcherRemoveCuratorFramework();
加锁
private boolean internalLock(long time, TimeUnit unit) throws Exception
{/*Note on concurrency: a given lockData instancecan be only acted on by a single thread so locking isn't necessary*/Thread currentThread = Thread.currentThread();// 获取当前线程锁数据,获取到的化,设置可重入LockData lockData = threadData.get(currentThread);if ( lockData != null ){// re-enteringlockData.lockCount.incrementAndGet();return true;}// 尝试获取锁String lockPath = internals.attemptLock(time, unit, getLockNodeBytes());if ( lockPath != null ){// 获取到锁,锁数据加入`threadData`的map结构中LockData newLockData = new LockData(currentThread, lockPath);threadData.put(currentThread, newLockData);return true;}// 没有获取到锁return false;
}
String attemptLock(long time, TimeUnit unit, byte[] lockNodeBytes) throws Exception
{final long startMillis = System.currentTimeMillis();final Long millisToWait = (unit != null) ? unit.toMillis(time) : null;final byte[] localLockNodeBytes = (revocable.get() != null) ? new byte[0] : lockNodeBytes;int retryCount = 0;String ourPath = null;boolean hasTheLock = false;boolean isDone = false;while ( !isDone ){isDone = true;try{ourPath = driver.createsTheLock(client, path, localLockNodeBytes);hasTheLock = internalLockLoop(startMillis, millisToWait, ourPath);}catch ( KeeperException.NoNodeException e ){// gets thrown by StandardLockInternalsDriver when it can't find the lock node// this can happen when the session expires, etc. So, if the retry allows, just try it all againif ( client.getZookeeperClient().getRetryPolicy().allowRetry(retryCount++, System.currentTimeMillis() - startMillis, RetryLoop.getDefaultRetrySleeper()) ){isDone = false;}else{throw e;}}}if ( hasTheLock ){return ourPath;}return null;
}
创建锁是创建的临时顺序
节点
@Override
public String createsTheLock(CuratorFramework client, String path, byte[] lockNodeBytes) throws Exception
{String ourPath;if ( lockNodeBytes != null ){ourPath = client.create().creatingParentContainersIfNeeded().withProtection().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(path, lockNodeBytes);}else{ourPath = client.create().creatingParentContainersIfNeeded().withProtection().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(path);}return ourPath;
}
watch
private boolean internalLockLoop(long startMillis, Long millisToWait, String ourPath) throws Exception
{boolean haveTheLock = false;boolean doDelete = false;try{if ( revocable.get() != null ){client.getData().usingWatcher(revocableWatcher).forPath(ourPath);}while ( (client.getState() == CuratorFrameworkState.STARTED) && !haveTheLock ){// 获取lock下所有节点数据,并排序List<String> children = getSortedChildren();String sequenceNodeName = ourPath.substring(basePath.length() + 1); // +1 to include the slash// 判断获取到锁PredicateResults predicateResults = driver.getsTheLock(client, children, sequenceNodeName, maxLeases);if ( predicateResults.getsTheLock() ){haveTheLock = true;}else{String previousSequencePath = basePath + "/" + predicateResults.getPathToWatch();synchronized(this){try{// use getData() instead of exists() to avoid leaving unneeded watchers which is a type of resource leak// 监听前一个节点,并等待client.getData().usingWatcher(watcher).forPath(previousSequencePath);if ( millisToWait != null ){millisToWait -= (System.currentTimeMillis() - startMillis);startMillis = System.currentTimeMillis();if ( millisToWait <= 0 ){doDelete = true; // timed out - delete our nodebreak;}wait(millisToWait);}else{wait();}}catch ( KeeperException.NoNodeException e ){// it has been deleted (i.e. lock released). Try to acquire again}}}}}catch ( Exception e ){ThreadUtils.checkInterrupted(e);doDelete = true;throw e;}finally{if ( doDelete ){deleteOurPath(ourPath);}}return haveTheLock;
}
是不是加锁成功:是不是最小的那个节点
@Override
public PredicateResults getsTheLock(CuratorFramework client, List<String> children, String sequenceNodeName, int maxLeases) throws Exception
{int ourIndex = children.indexOf(sequenceNodeName);validateOurIndex(sequenceNodeName, ourIndex);boolean getsTheLock = ourIndex < maxLeases;String pathToWatch = getsTheLock ? null : children.get(ourIndex - maxLeases);return new PredicateResults(pathToWatch, getsTheLock);
}
释放锁
可重入判断;删除watchers,删除节点
/*** Perform one release of the mutex if the calling thread is the same thread that acquired it. If the* thread had made multiple calls to acquire, the mutex will still be held when this method returns.** @throws Exception ZK errors, interruptions, current thread does not own the lock*/
@Override
public void release() throws Exception
{/*Note on concurrency: a given lockData instancecan be only acted on by a single thread so locking isn't necessary*/Thread currentThread = Thread.currentThread();LockData lockData = threadData.get(currentThread);if ( lockData == null ){throw new IllegalMonitorStateException("You do not own the lock: " + basePath);}int newLockCount = lockData.lockCount.decrementAndGet();if ( newLockCount > 0 ){return;}if ( newLockCount < 0 ){throw new IllegalMonitorStateException("Lock count has gone negative for lock: " + basePath);}try{internals.releaseLock(lockData.lockPath);}finally{threadData.remove(currentThread);}
}
final void releaseLock(String lockPath) throws Exception
{client.removeWatchers();revocable.set(null);deleteOurPath(lockPath);
}
redis vs zookeeper AI回答
- 一致性模型
Zookeeper 提供强一致性,这意味着当客户端在一个服务器上看到某个状态更新后,其他服务器也会立即反映这一变化。这种特性使得 Zookeeper 非常适合用于需要严格一致性的场景。
相比之下,Redis 默认提供最终一致性。虽然可以通过 Redlock 算法来增强其一致性保障4,但在某些极端情况下(如网络分区或主从延迟较高时),仍然可能存在短暂的数据不一致问题。
- 可靠性与容错能力
Zookeeper 使用 Paxos 或 ZAB 协议构建高可用集群,在部分节点失效的情况下仍能保持服务正常运行。因此,即使少数节点发生故障,整个系统依然能够继续运作。
然而,标准的 Redis 实现存在单点故障风险。尽管引入 Sentinel 或 Cluster 模式可以在一定程度上缓解此问题,但如果主节点崩溃且未及时完成 failover,则可能导致锁丢失的情况出现。此外,由于 Redis 主从之间采用异步复制机制,可能会进一步加剧该类问题的发生概率。
- 性能表现
在高频次、低延时需求下,Redis 显示出了显著的优势。它是一种内存级数据库,所有操作几乎都在 O(1) 时间复杂度内完成,这使其成为高性能应用场景下的理想选择。
而 Zookeeper 更注重于稳定性和一致性而非极致速度。对于那些对实时响应要求不高但强调可靠性的业务来说,Zookeeper 是更合适的选择。
- 功能扩展性
借助 Redisson 库的支持,开发者能够在 Redis 基础之上轻松获得诸如可重入锁、自动续期以及公平锁等功能。这些额外的功能极大地增强了 Redis 锁机制的实际应用价值。
至于 Zookeeper,虽然原生 API 较为简单直接,但它允许用户自定义复杂的逻辑流程以满足特定需求。不过相较于 Redisson 所提供的开箱即用型解决方案而言,开发成本相对更高一些。
相关文章:

分布式锁总结
文章目录 分布式锁什么是分布式锁?分布式锁的实现方式基于数据库(mysql)实现基于缓存(redis)多实例并发访问问题演示项目代码(使用redis)配置nginx.confjmeter压测复现问题并发是1,即不产生并发问题并发30测试,产生并发问题(虽然单实例是synchronized&am…...

使用MybatisPlus实现sql日志打印优化
背景: 在排查无忧行后台服务日志时,一个请求可能会包含多个执行的sql,经常会遇到SQL语句与对应参数不连续显示,或者参数较多需要逐个匹配的情况。这种情况下,如果需要还原完整SQL语句就会比较耗时。因此,我…...
springboot中redis的事务的研究
redis的事务类似于队列操作,执行过程分为三步: 开启事务入队操作执行事务 使用到的几个命令如下: 命令说明multi开启一个事务exec事务提交discard事务回滚watch监听key(s):当监听一个key(s)时,如果在本次事务提交之…...
为什么我输入对了密码,还是不能用 su 切换到 root?
“为什么我输入对了密码,还是不能用 su 切换到 root?” 其实这背后可能不是“密码错了”,而是系统不允许你用 su 切 root,即使密码对了。 👇 以下是最常见的几个真正原因: ❌ 1. Root 用户没有设置密码&…...

client.chat.completions.create方法参数详解
response client.chat.completions.create(model"gpt-3.5-turbo", # 必需参数messages[], # 必需参数temperature1.0, # 可选参数max_tokensNone, # 可选参数top_p1.0, # 可选参数frequency_penalty0.0, # 可选参数presenc…...
量子计算与云计算的融合:技术前沿与应用前景
目录 引言 量子计算基础 量子计算的基本原理 量子计算的优势与挑战 量子计算的发展阶段 云计算基础 云计算的基本概念 云计算的应用领域 云计算面临的挑战 量子计算与云计算的结合 量子云计算的概念与架构 量子云计算的服务模式 量子云计算的优势 量子云计算的发展…...
《企业级日志该怎么打?Java日志规范、分层设计与埋点实践》
大家好呀!👋 今天我们要聊一个Java开发中超级重要但又经常被忽视的话题——日志系统!📝 不管你是刚入门的小白,还是工作多年的老司机,日志都是我们每天都要打交道的"好朋友"。那么,如…...
python模块管理环境变量
概要 在 Python 应用中,为了将配置信息与代码分离、增强安全性并支持多环境(开发、测试、生产)运行,使用专门的模块来管理环境变量是最佳实践。常见工具包括: 标准库 os.environ:直接读取操作系统环境变量…...
【泛微系统】后端开发Action常用方法
后端开发Action常用方法 代码实例经验分享:代码实例 经验分享: 本文分享了后端开发中处理工作流Action的常用方法,主要包含以下内容:1) 获取工作流基础信息,如流程ID、节点ID、表单ID等;2) 操作请求信息,包括请求紧急程度、操作类型、用户信息等;3) 表单数据处理,展示…...
【算法】力扣体系分类
第一章 算法基础题型 1.1 排序算法题 1.1.1 冒泡排序相关题 冒泡排序是一种简单的排序算法,它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,…...
sql:如何查询一个数据表字段:Scrp 数据不为空?
在SQL中,要查询一个数据表中的字段 Scrp 不为空的记录,可以使用 IS NOT NULL 条件。以下是一个基本的SQL查询示例: SELECT * FROM your_table_name WHERE Scrp IS NOT NULL;在这个查询中,your_table_name 应该替换为你的实际数据…...

深入浅出人工智能:机器学习、深度学习、强化学习原理详解与对比!
各位朋友,大家好!今天咱们聊聊人工智能领域里最火的“三剑客”:机器学习 (Machine Learning)、深度学习 (Deep Learning) 和 强化学习 (Reinforcement Learning)。 听起来是不是有点高大上? 别怕,我保证把它们讲得明明…...
索引下探(Index Condition Pushdown,简称ICP)
索引下探(Index Condition Pushdown,简称ICP)是一种数据库查询优化技术,常见于MySQL等关系型数据库中。 1. 核心概念 作用:将原本在服务器层执行的WHERE条件判断尽可能下推到存储引擎层执行。减少回表查询次数支持部…...

基于 ColBERT 框架的后交互 (late interaction) 模型速递:Reason-ModernColBERT
一、Reason-ModernColBERT 模型概述 Reason-ModernColBERT 是一种基于 ColBERT 框架的后交互 (late interaction) 模型,专为信息检索任务中的推理密集型场景设计。该模型在 reasonir-hq 数据集上进行训练,于 BRIGHT 基准测试中取得了极具竞争力的性能表…...

vector中reserve导致的析构函数问题
接上一节vector实现,解决杨辉三角问题时,我在最后调试的时候,发现return vv时,调用析构函数,到第四步时才析构含有14641的vector。我设置了一个全局变量i来记录。 初始为35: 当为39时,也就是第…...

微软开源多智能体自定义自动化工作流系统:构建企业级AI驱动的智能引擎
微软近期推出了一款开源解决方案加速器——Multi-Agent Custom Automation Engine Solution Accelerator,这是一个基于AI多智能体协作的自动化工作流系统。该系统通过指挥多个智能体(Agent)协同完成复杂任务,显著提升企业在数据处理、业务流程管理等场景中的效率与准确性。…...
关于vector、queue、list哪边是front、哪边是back,增加、删除元素操作
容器的 front、back 及操作方向 1.1vector(动态数组) 结构:连续内存块,支持快速随机访问。 操作方向: front:第一个元素(索引 0)。 back:最后一个元素(索引…...
KubeVela入门到精通-K8S多集群交付
目录 1、介绍 2、部署 3、部署UI界面 4、御载 5、Velaux概念 6、OAM应用模型介绍 7、应用部署计划 8、系统架构 9、基础环境配置 9.1 创建项目 9.2 创建集群 9.3 创建交付目标 9.4 创建环境 9.5、创建服务测试 9.6、服务操作 10、插件、项目、权限管理 10.1 插…...
RocketMq的消息类型及代码案例
RocketMQ 提供了多种消息类型,以满足不同业务场景对 顺序性、事务性、时效性 的要求。其核心设计思想是通过解耦 “消息传递模式” 与 “业务逻辑”,实现高性能、高可靠的分布式通信。 一、主要类型包括 普通消息(基础类型)顺序…...
Eigen 直线拟合/曲线拟合/圆拟合/椭圆拟合
一、直线拟合 使用Eigen库进行直线拟合是数据分析和科学计算中的常见任务,主要通过最小二乘法实现。以下是关键实现方法和示例: 核心原理最小二乘法通过最小化点到直线距离的平方和来求解最优直线参数间接平差法是最小二乘法的具体实现形式,适用于直线拟合场景通过构建误差…...

安卓无障碍脚本开发全教程
文章目录 第一部分:无障碍服务基础1.1 无障碍服务概述核心功能: 1.2 基本原理与架构1.3 开发环境配置所需工具:关键依赖: 第二部分:创建基础无障碍服务2.1 服务声明配置2.2 服务配置文件关键属性说明: 2.3 …...
svn迁移到git保留记录和Python字符串格式化 f-string的进化历程
svn迁移到git保留记录 and Python字符串格式化(二): f-string的进化历程 在将项目从SVN迁移到Git时,保留完整的版本历史记录非常重要。下面是详细的步骤和工具,可以帮助你完成这一过程: 安装Git和SVN工具 首先&#…...

SOC-ESP32S3部分:10-GPIO中断按键中断实现
飞书文档https://x509p6c8to.feishu.cn/wiki/W4Wlw45P2izk5PkfXEaceMAunKg 学习了GPIO输入和输出功能后,参考示例工程,我们再来看看GPIO中断,IO中断的配置分为三步 配置中断触发类型安装中断服务注册中断回调函数 ESP32-S3的所有通用GPIO…...
【神经网络与深度学习】扩散模型之原理解释
引言: 在人工智能的生成领域,扩散模型(Diffusion Model)是一项极具突破性的技术。它不仅能够生成高质量的图像,还可以应用于音频、3D建模等领域。扩散模型的核心思想来源于物理扩散现象,其工作方式类似于从…...
语音合成之十六 语音合成(TTS)跳跃与重复问题的解析:成因、机制及解决方案
语音合成(TTS)跳跃与重复问题的解析:成因、机制及解决方案 引言TTS中跳跃与重复问题的根本原因注意力机制的失效文本到语音的对齐挑战自回归(AR)TTS模型的固有挑战时长建模的重要性输入数据特征的影响 构建鲁棒性&…...

战略-2.1 -战略分析(PEST/五力模型/成功关键因素)
战略分析路径,先宏观(PEST)、再产业(产品生命周期、五力模型、成功关键因素)、再竞争对手分析、最后企业内部分析。 本文介绍:PEST、产品生命周期、五力模型、成功关键因素、产业内的战略群组 一、宏观环境…...

python第三方库安装错位
问题所在 今天在安装我的django库时,我的库安装到了python3.13版本。我本意是想安装到python3.11版本的。我的pycharm右下角也设置了python3.11 但是太可恶了,我在pycharm的项目终端执行安装命令的时候还是给我安装到了python3.13的位置。 解决方法 我…...

如何把vue项目部署在nginx上
1:在vscode中把vue项目打包会出现dist文件夹 按照图示内容即可把vue项目部署在nginx上...

Vue3集成Element Plus完整指南:从安装到主题定制下-实现后台管理系统框架搭建
本文将详细介绍如何使用 Vue 3 构建一个综合管理系统,包括路由配置、页面布局以及常用组件集成。 一、路由配置 首先,我们来看系统的路由配置,这是整个应用的基础架构: import {createRouter, createWebHistory} from vue-rout…...

SpringBoot项目配置文件、yml配置文件
一. 配置文件格式 1. SpringBoot项目提供了多种属性配置方式(properties、yaml、yml)。 二. yml配置文件 1. 格式: (1) 数值前边必须有空格,作为分隔符。 (2) 使用缩进表示层级关系,缩进时,不允许使用Tab键,只能使用空…...