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

Apache Ignite 的分布式锁Distributed Locks的介绍

以下这段内容是关于 Apache Ignite 的分布式锁(Distributed Locks) 的介绍。这是一个非常重要的功能,用于在分布式系统中协调多个节点对共享资源的并发访问

下面我们来一步步深入理解它。


🎯 一、一句话理解:什么是 Ignite 分布式锁?

Ignite 分布式锁是一个跨多个服务器节点的“互斥锁”,确保同一时间只有一个节点可以操作某个共享数据(比如缓存中的某个 key)。

✅ 类比:

  • 就像一把“全球唯一的钥匙”:只有拿到这把钥匙的线程才能修改某个数据。
  • 单机环境下用 synchronizedReentrantLock
  • 分布式环境下就需要 IgniteCache.lock() 这种跨 JVM 的锁

🧩 二、核心概念解析

1️⃣ IgniteCache.lock(key) —— 获取一个分布式锁

Lock lock = cache.lock("keyLock");
  • 这个 lock 是一个实现了 java.util.concurrent.locks.Lock 接口的对象。
  • 它不是本地锁!它是集群范围内的分布式锁
  • 当你在 Node A 上调用 lock.lock(),Node B 和 Node C 上试图对同一个 key 加锁的线程都会阻塞等待,直到 Node A 释放锁。

2️⃣ 使用方式:try-finally 确保释放

lock.lock();  // 阻塞直到获取锁
try {// 安全地操作共享资源cache.put("Hello", 11);cache.put("World", 22);
} finally {lock.unlock(); // 必须释放,否则死锁!
}

⚠️ 注意:必须放在 finally 块中释放,防止异常导致锁未释放,造成死锁或资源饥饿


3️⃣ lockAll(keys) —— 批量加锁

Collection<String> keys = Arrays.asList("key1", "key2", "key3");
Lock lock = cache.lockAll(keys);
lock.lock();
try {// 同时锁定多个 keycache.put("key1", 1);cache.put("key2", 2);cache.put("key3", 3);
} finally {lock.unlock();
}
  • 适用于需要原子性地操作多个 key 的场景。
  • 所有 key 的锁会一起获取、一起释放
  • 避免因部分加锁成功而导致的数据不一致问题。

🔐 三、为什么需要分布式锁?

在分布式系统中,多个节点可能同时访问同一份数据。例如:

场景问题解决方案
多个节点同时更新用户余额超卖、余额错乱userId 加分布式锁
多个节点争抢执行定时任务重复执行"task-refresh" 加锁
缓存双写一致性缓存和数据库不一致更新时对 key 加锁

👉 没有锁 → 数据竞争(Race Condition) → 数据错误!


⚙️ 四、Atomicity Mode:必须是 TRANSACTIONAL

CacheConfiguration cfg = new CacheConfiguration("myCache");
cfg.setAtomicityMode(CacheAtomicityMode.TRANSACTIONAL); // 必须设置
  • Ignite 支持两种原子性模式:
    • ATOMIC:高性能,无事务支持,不能使用显式锁。
    • TRANSACTIONAL:支持事务和显式分布式锁

❌ 如果你在 ATOMIC 模式下调用 cache.lock(),会抛出异常!

✅ 所以:要用分布式锁,缓存必须配置为 TRANSACTIONAL 模式。


🔄 五、Locks vs Transactions:锁与事务的关系

这是最容易混淆的部分,原文说得很清楚:

“Explicit locks are not transactional and cannot be used from within transactions.”

我们来拆解这句话:

✅ 情况 1:显式锁 ≠ 事务锁

类型显式锁 (cache.lock())事务中的锁
是否可嵌套在事务中❌ 不可以✅ 可以
是否自动提交/回滚❌ 不支持回滚✅ 支持
如何获取手动 lock.lock()自动由事务管理器获取
使用场景非事务性临界区事务性数据操作

🔴 错误写法(会抛异常):

IgniteTransactions txs = ignite.transactions();
try (Transaction tx = txs.txStart()) {Lock lock = cache.lock("key");lock.lock(); // ❌ 抛异常!不能在事务中使用显式锁cache.put("key", 1);tx.commit();
}

✅ 情况 2:想要“事务中的显式锁”?用 PESSIMISTIC 事务

如果你希望在事务中也能“显式控制锁”的行为(比如立即失败而不是等待),应该使用:

try (Transaction tx = ignite.transactions().txStart(TransactionConcurrency.PESSIMISTIC,  // 悲观并发控制TransactionIsolation.REPEATABLE_READ)) {// 第一次读/写就会自动加锁Integer val = cache.get("key");cache.put("key", val + 1);tx.commit(); // 提交时释放锁
}
悲观事务(PESSIMISTIC)的特点:
  • get()put()立即尝试获取分布式锁
  • 如果锁被占用,可以选择超时失败(避免无限等待)。
  • 行为类似于“显式锁 + 事务”的组合效果。

🧪 六、完整示例:银行转账(防止并发超支)

IgniteCache<String, Integer> cache = ignite.cache("accounts");// 模拟两个账户
String from = "account-A";
String to = "account-B";// 对两个账户加锁(避免死锁:按字母顺序加锁)
List<String> sortedKeys = Arrays.asList(from, to).stream().sorted().collect(Collectors.toList());
Lock lock = cache.lockAll(sortedKeys);lock.lock();
try {Integer balanceA = cache.get(from);Integer balanceB = cache.get(to);if (balanceA >= 100) {cache.put(from, balanceA - 100);cache.put(to, balanceB + 100);System.out.println("转账成功");} else {System.out.println("余额不足");}
} finally {lock.unlock(); // 释放所有锁
}

✅ 保证了即使多个节点同时发起转账,也不会出现“超卖”。


⚠️ 七、注意事项 & 最佳实践

项目建议
🔒 锁粒度尽量小(比如按用户 ID 锁),避免锁整个缓存
⏱️ 锁持有时间越短越好,不要在锁内做耗时操作(如网络请求)
💥 异常处理一定要 finally unlock(),建议用 try-with-resources(如果自定义封装)
🪢 死锁风险多 key 加锁时,按固定顺序加锁(如排序)
📈 性能影响分布式锁涉及网络通信,频繁使用会影响性能
🔄 替代方案考虑使用 EntryProcessorinvoke())进行原子更新,避免手动加锁

✅ 总结:一句话掌握精髓

Ignite 的 cache.lock(key) 提供了一种简单、直观的跨节点互斥机制,让你像使用本地 ReentrantLock 一样保护分布式共享资源,但前提是缓存必须是 TRANSACTIONAL 模式,并且不能与事务混用。


🔄 对比总结表

功能cache.lock() 显式锁悲观事务(PESSIMISTIC)EntryProcessor(invoke)
是否跨节点✅ 是✅ 是✅ 是
是否支持事务❌ 否✅ 是✅ 是(单 key)
是否自动加锁✅ 手动✅ 自动✅ 自动
适用场景非事务临界区多 key 事务操作单 key 原子更新
性能中等中等高(推荐)

如果你想实现高并发下的安全更新,优先考虑 EntryProcessor;如果逻辑复杂必须加锁,再用 lock() 或 悲观事务。

如有具体业务场景(如库存扣减、计数器、任务调度),欢迎继续提问,我可以给出更具体的代码建议!

http://www.lryc.cn/news/603752.html

相关文章:

  • windows电脑截图工具怎么选 windows电脑截图工具合集整理
  • DeepSeek MoE 技术解析:模型架构、通信优化与负载均衡
  • Python与Spark
  • Linux_库制作与原理浅理解
  • vim的`:q!` 与 `ZQ` 笔记250729
  • grep常用指令
  • 【lucene】SegmentCoreReaders
  • 【lucene】currentFrame与staticFrame
  • Qt 移动应用传感器开发
  • 20250729使用WPS打开xlsx格式的电子表格时候隐藏显示fx的编辑栏的方法
  • ElasticStack技术栈概述及Elasticsearch8.2.2集群部署并更换JDK版本为openjdk-17
  • sqlite3---维护命令、回调函数
  • 【机器学习深度学习】分布式训练的核心技术全解:数据并行、模型并行、流水线并行与3D混合并行
  • 基于最小二乘支持向量机(LSSVM)的气象预测
  • css 二维变换之详说
  • 引领汽车加速向具身智能进化,吉利携阶跃星辰参展WAIC 2025
  • 考古学家 - 华为OD统一考试(JavaScript 题解)
  • STM32寄存器中的缩写
  • 【HTML】浅谈 script 标签的 defer 和 async
  • 数据库4.0
  • 健壮性篇(一):优雅地“拥抱”错误:构建一个可预测的错误处理边界
  • vue-计算属性
  • Android Slices:让应用功能在系统级交互中触手可及
  • FPGA数码管驱动模块
  • windows软件ARM64和AMD64(x64)区别,如何查看电脑支持哪种
  • 沪铝本周想法
  • C++ 模板补充
  • 网工知识——OSPF摘要知识
  • 重生之我在暑假学习微服务第四天《Docker-下篇》
  • 《林景媚与时间守护者》