MoltLock分布式锁:现代应用的高性能并发控制解决方案

news2026/5/4 6:31:14
1. 项目概述一把为现代应用而生的“智能锁”在分布式系统和微服务架构成为主流的今天我们每天都在和各种各样的锁打交道。无论是防止数据库的并发更新还是协调多个服务实例对共享资源的访问锁机制都是确保数据一致性和系统稳定性的基石。然而传统的锁方案无论是基于数据库的悲观锁、乐观锁还是基于Redis的分布式锁在实际落地时总会遇到一些“水土不服”的情况性能瓶颈、死锁风险、锁粒度难以控制、与业务逻辑耦合过深等等。很多时候我们不得不花费大量精力去封装、去适配、去处理各种边界情况这无疑增加了开发和维护的复杂度。最近在社区里注意到一个名为MoltLock的项目它定位为一个“现代、高性能、功能丰富的分布式锁库”。这个描述立刻引起了我的兴趣。在深入研究了它的源码、设计理念和实际测试后我发现它并非又一个简单的Redis锁包装器而是一个经过深思熟虑、为解决上述痛点而生的工具。它更像是一把为现代云原生应用量身定制的“智能锁”不仅提供了基础的加锁、解锁功能更在易用性、可靠性和高级特性上做了大量工作。接下来我将结合自己十多年的后端开发经验从设计思路到实操细节为你完整拆解MoltLock看看它如何让“锁”这件事变得简单而强大。2. 核心设计理念与架构拆解2.1 为什么需要另一个分布式锁库在讨论MoltLock的具体实现之前我们必须先回答一个根本问题市面上已经有Redisson、etcd、ZooKeeper等多种成熟的分布式锁方案为什么还需要MoltLock通过分析其源码和文档我认为其核心驱动力在于对“开发者体验”和“云原生适配性”的极致追求。传统的方案往往存在一些固有短板。例如直接使用Redis的SETNX命令实现锁需要开发者自行处理锁续期、避免误删他人锁、实现可重入等复杂逻辑极易出错。而一些功能完善的客户端库其API设计可能较为冗长或者与特定的框架、编程范式绑定过深。MoltLock的设计目标很明确提供一个接口简洁、功能完备、默认安全、易于集成的锁抽象。它试图将最佳实践内化到库中让开发者通过几行直观的代码就能获得一个生产可用的、健壮的分布式锁而无需关心底层复杂的实现细节。2.2 架构分层与核心组件MoltLock的架构清晰的分为了三层这种分层设计保证了其灵活性和可扩展性。第一层锁接口抽象层。这是面向开发者的核心API层。它定义了几个关键接口如Lock、RwLock读写锁等。这些接口的设计非常简洁通常只包含lock、tryLock、unlock等核心方法。但简洁不等于简陋MoltLock在接口层面就考虑到了超时、重试等策略可以通过流畅的Builder模式进行配置。例如你不仅可以设置获取锁的等待超时时间还可以设置在锁持有期间后台自动续期的间隔这为实现一个“看门狗”机制提供了基础。第二层锁实现层。这一层包含了针对不同后端存储的具体锁实现。目前MoltLock主要支持Redis作为后端存储这也是生产环境中最常见的选择。这一层的实现是库的“引擎”它负责将高层的锁操作加锁、解锁、续期翻译成对Redis的特定命令调用。这里的设计精髓在于对Redis命令的精准运用和组合。例如为了实现原子性的“获取锁并设置过期时间”它使用了Redis的SET命令配合NX不存在才设置和PX毫秒级过期选项这比老旧的SETNXEXPIRE非原子组合要可靠得多。同时为了实现可重入它可能在Redis中存储一个结构化的值包含持有者标识和重入计数。第三层后端驱动与连接管理层。这一层负责与Redis等存储服务的实际网络通信。MoltLock通常不会重复造轮子而是基于成熟的Redis客户端如Jedis、Lettuce进行封装。这一层需要处理连接池管理、序列化、异常处理、重连等基础设施问题。一个好的设计是让这一层对上层透明无论底层用的是集群模式Redis还是哨兵模式上层的锁API调用方式都保持一致。这种分层架构的好处显而易见高内聚、低耦合。如果你想支持一个新的后端比如etcd你只需要在锁实现层新增一个实现并适配对应的驱动层上层的应用代码几乎无需改动。这为库未来的扩展留下了充足的空间。2.3 默认的安全性与最佳实践内化这是MoltLock最值得称道的设计哲学之一将分布式锁的最佳实践设置为默认行为。很多锁问题的根源在于开发者忽略了某些关键细节而MoltLock试图在框架层面解决这些问题。自动续期看门狗机制这是防止业务逻辑执行时间超过锁过期时间而导致锁失效的关键。MoltLock可以在获取锁后启动一个后台线程或定时任务定期比如在过期时间的1/3处去重置锁的过期时间。只要持有锁的客户端还“活着”锁就不会因为业务处理慢而意外释放避免了数据不一致的风险。这个机制通常是默认开启或极易开启的。防误删与原子释放每个锁在创建时都会关联一个全局唯一的标识通常是客户端ID线程ID随机数。释放锁时MoltLock会先检查当前锁的持有者是否是自己通过比对标识然后再执行删除操作。并且这个“检查删除”的操作通过Lua脚本在Redis端原子性执行防止在检查之后、删除之前锁过期并被其他客户端获取的极端情况。可重入支持在单JVM内的同一个线程多次获取同一把锁是常见需求例如递归调用。MoltLock在本地内存和Redis中维护了重入计数只有重入计数降为0时才会真正释放Redis中的锁资源。注意虽然看门狗机制极大地增强了安全性但它也带来了额外的网络开销和复杂性。在业务逻辑执行时间非常确定且短促的场景下你可以考虑精确计算并设置一个合理的过期时间然后关闭自动续期以获得更高的性能。3. 从零开始MoltLock的集成与基础使用3.1 环境准备与依赖引入假设我们正在构建一个基于Spring Boot的Java微服务项目。集成MoltLock的第一步是引入其依赖。由于它可能尚未进入中央仓库我们需要检查项目的发布方式可能是GitHub Packages或直接引用JAR。这里以假设它已发布到Maven中央仓库为例在pom.xml中添加依赖。dependency groupIdio.github.berkmh/groupId artifactIdmoltlock-core/artifactId version{最新版本号}/version /dependency dependency groupIdio.github.berkmh/groupId artifactIdmoltlock-redis/artifactId !-- 以Redis实现为例 -- version{最新版本号}/version /dependency同时确保项目中已经包含了Redis客户端的依赖比如Lettuce或Jedis。MoltLock的Redis模块通常会声明对这些客户端的传递依赖但最好显式声明以控制版本。dependency groupIdio.lettuce/groupId artifactIdlettuce-core/artifactId version6.2.4.RELEASE/version /dependency3.2 配置锁管理器LockManager在Spring Boot中我们通常通过配置类来创建和配置LockManager它是获取锁的工厂类。Configuration public class LockConfig { Bean public RedisClient redisClient() { // 创建Redis客户端连接这里使用Lettuce连接本地Redis RedisURI redisUri RedisURI.create(redis://localhost:6379); return RedisClient.create(redisUri); } Bean public LockManager lockManager(RedisClient redisClient) { // 1. 创建Redis后端连接器 RedisLockStore store new RedisLockStore(redisClient); // 2. 配置锁管理器Builder LockManagerBuilder builder LockManager.builder() .store(store) // 设置存储后端 .lockNamespace(myapp:lock:) // 设置锁键前缀便于管理和监控 .defaultLockTimeout(Duration.ofSeconds(30)) // 默认锁超时时间 .watchDogInterval(Duration.ofSeconds(10)) // 看门狗续期间隔 .enableWatchDog(true); // 启用看门狗 // 3. 构建LockManager return builder.build(); } }配置项解析lockNamespace: 强烈建议设置。它为所有锁的键添加一个统一前缀如myapp:lock:order:123方便在Redis中通过keys myapp:lock:*进行查询和监控也避免了与其他业务键名冲突。defaultLockTimeout: 这是锁的租约时间。即使业务没执行完超过这个时间锁也会自动释放防止死锁。看门狗机制会在这个时间到期前续期。watchDogInterval: 看门狗的执行间隔。通常设置为defaultLockTimeout的1/3这是一个经验值能在保证及时续期的同时避免过于频繁的Redis调用。3.3 你的第一把锁基础加锁与解锁配置完成后就可以在业务代码中注入LockManager并使用它了。让我们从一个最简单的场景开始秒杀活动中扣减库存。Service public class InventoryService { Autowired private LockManager lockManager; Autowired private InventoryMapper inventoryMapper; // 假设的数据库Mapper public boolean deductStock(Long itemId, Integer quantity) { // 构造锁的键通常与要保护的资源强相关 String lockKey inventory: itemId; // 尝试获取锁最多等待2秒锁持有超时30秒 Lock lock lockManager.createLock(lockKey); boolean acquired false; try { acquired lock.tryLock(2, TimeUnit.SECONDS); if (!acquired) { log.warn(获取库存锁失败itemId: {}, itemId); return false; // 获取锁失败直接返回避免阻塞 } // 临界区代码执行库存扣减 Inventory inventory inventoryMapper.selectForUpdate(itemId); // 或使用乐观锁 if (inventory.getStock() quantity) { inventory.setStock(inventory.getStock() - quantity); inventoryMapper.updateById(inventory); log.info(扣减库存成功itemId: {}, 剩余: {}, itemId, inventory.getStock()); return true; } else { log.warn(库存不足itemId: {}, itemId); return false; } } catch (InterruptedException e) { Thread.currentThread().interrupt(); // 恢复中断状态 log.error(锁获取被中断, e); return false; } finally { // 务必在finally块中释放锁 if (acquired) { lock.unlock(); } } } }代码要点与避坑指南锁键设计锁的键lockKey必须唯一对应你要保护的资源。这里是inventory:${itemId}确保了不同商品之间的库存扣减不会相互阻塞只有同一商品的并发请求才会竞争锁。这是细粒度锁的设计能极大提升并发能力。tryLockvslocktryLock是非阻塞或带超时等待的获取失败会立即或超时后返回false。而lock()方法是阻塞式的会一直等待直到获取锁这在分布式环境下容易导致大量线程挂起通常不推荐使用。在生产环境中优先使用tryLock并设置一个合理的等待超时时间。释放锁的位置必须在finally块中释放锁确保无论业务逻辑成功还是抛出异常锁都能被释放避免死锁。同时只有成功获取锁后才需要释放。锁的持有时间临界区内的代码应尽可能简短高效。锁的持有时间越长系统的并发度就越低其他等待线程的延迟就越高。库存查询和更新应使用最有效的数据库操作如带条件的更新update inventory set stock stock - 1 where id ? and stock 0甚至可以尝试在应用层用Redis预扣库存再异步同步到数据库。4. 高级特性实战超越基础锁MoltLock如果只提供基础锁那它的价值就有限了。它真正强大的地方在于提供了一系列开箱即用的高级特性让我们能应对更复杂的并发场景。4.1 读写锁RwLock优化读多写少场景在很多业务中读操作如查询商品信息、查询订单详情的频率远高于写操作如修改商品信息、更新订单状态。使用普通的互斥锁会导致大量的读操作也被串行化严重浪费资源。读写锁应运而生它允许多个读锁同时持有但写锁是独占的。Service public class ProductService { Autowired private LockManager lockManager; Autowired private ProductCache productCache; // 本地或分布式缓存 public Product getProductDetail(Long productId) { String rwLockKey product:rw: productId; RwLock rwLock lockManager.createRwLock(rwLockKey); Lock readLock rwLock.readLock(); try { if (readLock.tryLock(1, TimeUnit.SECONDS)) { // 成功获取读锁可以并发执行 // 1. 先查本地缓存 Product product productCache.get(productId); if (product ! null) { return product; } // 2. 缓存未命中查数据库此时其他读请求依然可以并发进行 product productMapper.selectById(productId); if (product ! null) { productCache.put(productId, product); } return product; } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { readLock.unlock(); } // 获取读锁失败可以降级为直接查库可能读到旧数据或抛出异常 return productMapper.selectById(productId); } Transactional public boolean updateProductPrice(Long productId, BigDecimal newPrice) { String rwLockKey product:rw: productId; RwLock rwLock lockManager.createRwLock(rwLockKey); Lock writeLock rwLock.writeLock(); boolean acquired false; try { acquired writeLock.tryLock(3, TimeUnit.SECONDS); if (!acquired) { throw new RuntimeException(更新产品信息冲突请稍后重试); } // 成功获取写锁此时没有其他读锁或写锁 Product product productMapper.selectById(productId); product.setPrice(newPrice); productMapper.updateById(product); // **关键步骤**更新后立即失效或更新缓存 productCache.invalidate(productId); return true; } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(e); } finally { if (acquired) { writeLock.unlock(); } } } }读写锁的使用心得缓存一致性读写锁常与缓存结合使用。写操作在更新数据库后必须失效或更新对应的缓存条目这是保证缓存与数据库一致性的关键。否则后续的读请求可能从缓存中读到脏数据。锁降级MoltLock的读写锁可能支持锁降级即持有写锁的线程可以再获取读锁然后释放写锁这样依然持有读锁。这在一些先更新再长时间读取的场景下很有用但需要查看其具体实现是否支持。避免锁升级锁升级读锁升级为写锁通常是不被允许的因为容易导致死锁。如果需要应先释放读锁再尝试获取写锁。4.2 联锁MultiLock与红锁RedLock算法有时我们需要同时锁定多个资源。例如转账业务需要锁定转出账户和转入账户。简单地按顺序加锁两个单独的锁可能会因为加锁顺序不一致而导致死锁线程A锁账户1线程B锁账户2然后A等B释放账户2B等A释放账户1。联锁MultiLock是MoltLock提供的一种解决方案它允许你将多个锁捆绑在一起作为一个原子操作来获取和释放。public boolean transfer(Long fromAccountId, Long toAccountId, BigDecimal amount) { // 确定一个全局固定的加锁顺序例如按照账户ID排序 ListString lockKeys Arrays.asList(account: Math.min(fromAccountId, toAccountId), account: Math.max(fromAccountId, toAccountId)); MultiLock multiLock lockManager.createMultiLock(lockKeys); boolean acquired false; try { // 尝试原子性地获取所有锁 acquired multiLock.tryLock(5, TimeUnit.SECONDS); if (!acquired) { return false; } // 执行转账核心逻辑... return doTransfer(fromAccountId, toAccountId, amount); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return false; } finally { if (acquired) { multiLock.unlock(); // 原子性释放所有锁 } } }联锁在内部会按照你提供的顺序依次尝试获取每个锁。如果中途任何一个锁获取失败它会释放已经获得的所有锁然后重试或失败返回。这避免了死锁但联锁的成功率受限于所有单个锁的可用性任何一个锁被长时间占用都会导致整个操作失败。对于更高要求的场景MoltLock可能实现了RedLock算法。RedLock是Redis官方推荐的用于在多个独立的Redis主节点上实现分布式锁的算法。其核心思想是客户端向超过半数的N个独立Redis实例申请锁只有当从大多数N/21实例上都成功获取锁时才算真正持有锁。这能在单个Redis实例故障时依然保证锁的可用性但牺牲了部分性能。重要提示RedLock算法在社区存在争议比如时钟漂移问题使用前务必充分理解其局限性和使用前提。在绝大多数业务场景下使用一个由哨兵或集群模式保障高可用的Redis服务配合MoltLock的单实例锁实现已经足够可靠。仅在金融级等对一致性有极端要求的场景下才需要考虑RedLock这类多主方案并且通常需要运维和基础设施的强力支持。4.3 锁的监听器与事件驱动MoltLock可能提供了监听器接口允许你在锁获取成功、锁释放、锁过期等事件发生时执行自定义逻辑。这对于实现更复杂的协调任务、监控或审计非常有用。lockManager.addListener(new LockListener() { Override public void onLockAcquired(String lockKey, String lockId) { log.info(锁[{}]被客户端[{}]获取, lockKey, lockId); // 可以在这里触发一个事件通知其他系统资源已被占用 eventPublisher.publish(new LockAcquiredEvent(lockKey, lockId)); } Override public void onLockReleased(String lockKey, String lockId) { log.info(锁[{}]被客户端[{}]释放, lockKey, lockId); metrics.counter(lock.released).increment(); } Override public void onLockExpired(String lockKey, String lockId) { log.error(锁[{}]过期持有者[{}]。这可能意味着业务进程卡死或GC暂停。, lockKey, lockId); // 触发告警需要人工介入检查业务健康状态 alertManager.sendCriticalAlert(分布式锁异常过期, lockKey); } });onLockExpired事件尤其重要。在启用了看门狗的情况下锁理论上不应该过期。如果触发了过期事件往往意味着持有锁的JVM进程发生了长时间GC、网络分区、或看门狗线程意外死亡等严重问题。这是一个需要立即告警的信号。5. 生产环境部署、监控与排错指南5.1 配置优化与高可用考量将MoltLock用于生产环境不仅仅是引入依赖那么简单。以下是一些关键的配置和架构建议Redis后端高可用锁服务的可用性取决于Redis。必须使用Redis哨兵Sentinel或集群Cluster模式避免单点故障。在MoltLock配置中需要正确配置连接字符串指向哨兵或集群节点。// 连接Redis哨兵示例 RedisURI redisUri RedisURI.Builder.sentinel(sentinel-host1, 26379, mymaster) .withSentinel(sentinel-host2, 26379) .withSentinel(sentinel-host3, 26379) .build();连接池与超时参数根据业务并发量调整Redis客户端的连接池大小maxActive,maxIdle等。同时合理设置网络连接和读写超时避免因为网络抖动导致锁操作长时间阻塞。这些参数通常在Redis客户端如Lettuce层面配置。锁的默认超时时间defaultLockTimeout不宜设置过短或过长。过短会导致看门狗频繁续期增加Redis压力且业务稍有延迟就可能锁过期过长则意味着故障后资源被锁定的时间更久。建议根据业务逻辑的P99耗时来设置并加上一定的缓冲如2-3倍。例如业务逻辑通常200ms完成可以设置锁超时为1s。命名空间与键模式如前所述使用清晰的lockNamespace。这不仅能避免键冲突更重要的是便于监控和清理。你可以写一个定时任务扫描并删除那些明显已经过期但可能因客户端崩溃未正常删除的锁键虽然MoltLock有自动过期但这是一种额外的保障。5.2 监控与可观测性“没有监控的系统就是在裸奔”。对于分布式锁我们需要监控以下几个关键指标锁获取成功率/失败率这是最直接的业务健康度指标。失败率飙升可能意味着资源竞争激烈、系统负载过高或锁超时时间设置不合理。锁平均持有时间监控锁从获取到释放的平均时长。如果时间异常增长可能意味着某个临界区内的业务逻辑变慢需要优化。看门狗续期次数与失败次数续期失败可能预示着Redis网络问题或客户端与Redis的连接不稳定。Redis相关指标Redis实例的内存、CPU、连接数、命令延迟尤其是SET、GET、EVAL等锁相关命令。锁的频繁操作会对Redis产生压力。可以在MoltLock的监听器中埋点将上述事件发送到监控系统如Prometheus。同时在Redis中可以通过INFO commandstats命令查看命令的调用次数和耗时。5.3 常见问题排查实录在实际使用中你可能会遇到以下问题。这里记录了我的排查思路问题一日志中偶尔出现“锁已过期”的警告但业务逻辑看起来执行很快。排查首先检查onLockExpired事件是否被触发。如果触发说明看门狗续期失败问题可能不在业务代码。检查GC日志。长时间的Full GC会导致应用线程暂停看门狗线程也可能被挂起从而错过续期。使用jstat或APM工具观察GC情况。检查网络监控。在续期时刻是否存在网络瞬断或Redis服务端的高延迟。检查锁超时时间是否设置得太短。即使业务平均耗时短但如果有慢查询或外部接口调用可能导致个别请求超时。解决优化JVM参数减少GC停顿时间。适当增加锁的默认超时时间例如从5s增加到10s。确保Redis服务端与客户端的网络稳定。问题二在高并发下出现了库存超卖即锁似乎没起作用。排查最可能的原因锁的范围不对。检查lockKey。是否所有竞争线程都对同一个资源使用了相同的锁键例如是否错误地用了lock(“inventory”)而不是lock(“inventory:”itemId)导致所有商品共用一把锁失去了细粒度控制的意义检查锁的获取和释放是否在正确的代码块内。是否在tryLock失败后错误地执行了库存扣减逻辑检查数据库事务隔离级别。如果是在tryLock内部先查库存然后更新但数据库事务隔离级别是“读已提交”且查询和更新不是原子操作仍然可能产生并发问题。锁和数据库事务要配合使用临界区内的数据库操作最好使用SELECT ... FOR UPDATE悲观锁或带版本的乐观锁。解决复核锁键的设计确保其精确匹配要保护的资源。在临界区内使用最强的数据一致性保证如数据库行锁。添加更详细的日志记录锁获取、业务执行、锁释放的全过程便于复盘。问题三Redis故障切换Failover后出现两个客户端同时持有同一把锁。背景在Redis主从异步复制下如果客户端在主节点上获取了锁但锁的SET命令还未同步到从节点主节点就宕机了。哨兵提升了一个从节点为新主这个新主上没有刚才的锁信息。此时另一个客户端向新主申请同一把锁也会成功。这就导致了锁的互斥性被破坏。排查这是Redis主从复制异步性带来的固有风险。查看MoltLock是否针对此场景有增强方案。解决使用RedLock算法如前所述这是应对此问题的经典方案但复杂度高。等待复制MoltLock的高级配置可能提供了waitForReplication选项即在主节点设置锁后等待锁信息复制到一定数量的从节点后再返回成功。但这会增加锁获取的延迟且需要Redis配置支持。业务层容忍评估业务是否能接受在极端故障场景下出现的短暂锁失效。例如对于库存扣减可以结合数据库的唯一约束或乐观锁版本号做最终兜底即使锁短暂失效也不会导致最终数据错误可能产生一些无效的竞争和重试。这需要根据业务的一致性要求来做权衡。6. 性能压测与调优建议在将任何中间件上生产前进行性能压测是必不可少的环节。对于MoltLock我们需要关注两个层面的性能客户端应用侧的锁操作延迟以及Redis服务端的压力。压测场景设计基准测试单客户端线程连续执行“获取锁-执行业务逻辑可模拟1ms延迟-释放锁”循环。测量平均耗时和QPS。这反映了锁库本身的开销。并发竞争测试模拟N个客户端线程同时竞争同一把锁。观察锁获取的平均等待时间、成功率和Redis的commandstats。这反映了在热点资源下的表现。多资源测试模拟N个客户端线程随机竞争M把不同的锁M远大于N。这反映了锁库和Redis在低竞争、高并发场景下的吞吐量。调优建议根据压测结果调整锁超时时间如果锁竞争激烈获取锁的平均等待时间接近tryLock的超时时间可以考虑缩短业务逻辑或增加超时时间但后者需谨慎会延长故障恢复时间。调整Redis配置如果压测发现Redis的CPU或网络IO成为瓶颈需要考虑升级Redis实例规格或者使用Redis集群将锁的键分散到多个分片上。注意MoltLock的联锁MultiLock或红锁RedLock在集群模式下可能需要特殊处理。客户端连接池优化增加Redis客户端的连接池大小可以缓解高并发下获取连接等待的时间。但也不是越大越好需要监控连接数。慎用看门狗在看门狗机制下即使业务逻辑很快锁也会持续到超时时间才释放因为看门狗会续期。如果你能精确控制业务最大耗时可以关闭看门狗使用固定的、略大于业务耗时的过期时间这样锁能更早被释放提高并发度。MoltLock作为一个设计精良的分布式锁库它把复杂性和最佳实践封装在内部提供给开发者一个简洁而强大的接口。从简单的互斥锁到复杂的读写锁、联锁从基础的防死锁到高级的看门狗和监听器它覆盖了分布式锁的绝大多数应用场景。然而再好的工具也需要正确的使用方式。理解其原理合理设计锁键配置好超时和重试并结合业务数据库的事务机制才能构建出真正健壮、高性能的并发控制系统。在实际项目中我建议从小范围、非核心的业务开始试点充分压测和观察再逐步推广到全站。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…