以下这段内容是关于 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.pswp.cn/bicheng/91295.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/91295.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/91295.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第十二天:C++ 标准库函数分类总结

C 标准库函数分类总结 数学函数&#xff08;<cmath>&#xff09; 基本运算函数 abs(x)&#xff1a;返回整数或浮点数的绝对值。int a abs(-5); // 返回 5fabs(x)&#xff1a;返回浮点数的绝对值。double b fabs(-3.14); // 返回 3.14fmod(x, y)&#xff1a;计算 x 除以…

Unity Standard Shader 解析(四)之ForwardAdd(简化版)

一、ForwardAdd// Additive forward pass (one light per pass)Pass{Name "FORWARD_DELTA"Tags { "LightMode" "ForwardAdd" }Blend [_SrcBlend] OneFog { Color (0,0,0,0) } // in additive pass fog should be blackZWrite OffZTest LEqualC…

第十九周-文档数据库MongoDB、消息队列和微服务

1. 完成redis单机安装&#xff0c;哨兵模式安装&#xff0c;主从安装&#xff0c;集群安装单机安装#安装依赖包 [rootcentos8~]#yum -y install gcc make jemalloc-devel #如果支持systemd需要安装下面包 [rootubuntu2204 ~]#apt update && apt -y install make gcc li…

C++中sizeof运算符全面详解和代码示例

sizeof 是 C 中的一个编译时运算符&#xff0c;用于获取对象或类型所占的字节数&#xff08;以 size_t 返回&#xff09;。它是掌握底层内存模型、结构体对齐、数组大小计算等的重要工具。1. 基本语法 sizeof(type) // 获取类型的大小 sizeof expression // 获取表达式结果…

内容中台:在一个地方管理多渠道内容

在数字化竞争愈演愈烈的今天&#xff0c;企业官网、社交平台、移动应用、邮件营销等渠道已成为品牌触达用户的关键接口。内容仍是连接企业与客户的核心资产。然而&#xff0c;内容创作与分发的复杂性持续攀升&#xff0c;多平台运营面临重复维护、更新不一致、资源冗余等诸多挑…

【刷题】东方博宜oj 1307 - 数的计数

样例输入&#xff1a; 6输出&#xff1a; 16 26 126 36 136原版是直接输出总数量&#xff0c;但我修改了一些&#xff0c;输出所有的新数。 #include <iostream> #include <vector> #include <string> using namespace std; //int c; void g(int num, string…

阿里云AI代码助手通义灵码开发指导

与阿里云一起轻松实现数智化让算力成为公共服务&#xff1a;用大规模的通用计算&#xff0c;帮助客户做从前不能做的事情&#xff0c;做从前做不到的规模。让数据成为生产资料&#xff1a;用数据的实时在线&#xff0c;帮助客户以数据为中心改变生产生活方式创造新的价值。智能…

设计模式(二十三)行为型:模板方法模式详解

设计模式&#xff08;二十三&#xff09;行为型&#xff1a;模板方法模式详解模板方法模式&#xff08;Template Method Pattern&#xff09;是 GoF 23 种设计模式中的行为型模式之一&#xff0c;其核心价值在于定义一个操作中的算法骨架&#xff0c;而将一些步骤延迟到子类中实…

Postgresql 查询使用正则

今天接到任务&#xff0c;要从数据库中查询数据&#xff0c;对于postgresql 我并不熟悉&#xff0c;问了百度&#xff0c;问了通义千问。发现Postgresql 在写query sql 的时候&#xff0c;可以使用正则匹配&#xff0c;不单是使用like 这种关键字。我像发现了新大陆一样的兴奋。…

【WRF-Chem Emissions教程第八期】转换实用程序

转换实用程序 8.1 将中间二进制文件转换为 WRF-Chem 数据文件 文件命名规范与风格 8.2 Binary data file format(中间二进制排放数据文件的格式和结构) FORTRAN 示例程序说明 8.3 Building the WRF-Chemistry emissions conversion code 编译步骤 验证编译结果 8.4 Namelist …

Qt Ribbon效果界面

实现效果&#xff1a;头文件&#xff1a;#pragma once #include <QMdiArea> #include <QMdiSubWindow> #include <QMainWindow> #include "ui_MainFrame1.h" #include "DockManager.h" #include "DockAreaWidget.h"class Main…

如何修改 MySQL 8.0 的密码,和忘记密码时如何修改

要修改 MySQL 8.0 的密码&#xff0c;可以通过以下几种方法实现&#xff1a;方法 1&#xff1a;使用 ALTER USER 命令&#xff08;推荐&#xff09;这是 MySQL 8.0 推荐的修改密码方式&#xff1a;-- 修改当前登录用户的密码 ALTER USER USER() IDENTIFIED BY 新密码;-- 修改指…

图像处理控件Aspose.Imaging教程:使用 C# 编程将 CMX 转换为 PNG

PNG图像文件格式是广泛使用的图像格式之一。这种图像文件格式提供了增强的共享和显示功能。另一方面&#xff0c;CMX也是 Corel 应用程序主要使用的图像文件格式。然而&#xff0c;将 CMX 转换为 PNG 可以帮助用户在网络上查看和共享文件。因此&#xff0c;在本指南中&#xff…

迪丽热巴写真壁纸

下载&#xff1a;https://pan.quark.cn/s/a740dbac8274迪丽热巴绝美写真&#xff0c;高清壁纸展现独特魅力&#xff0c;每一张都是视觉盛宴

C++11 std::function 详解:通用多态函数包装器

在C11标准中&#xff0c;引入了std::function这一通用多态函数包装器&#xff0c;定义于<functional>头文件中。它彻底改变了C中函数对象的使用方式&#xff0c;为不同类型的可调用实体提供了统一的接口。std::function能够存储、复制和调用任何可复制构造的可调用目标&a…

Kafka运维实战 16 - kafka 分区重新分配【实战】

💻 Kafka运维实战 (17篇) 📝Kafka运维实战 17 - kafka 分区副本从 1 增加到 3【实战】 📝Kafka运维实战 16 - kafka 分区重新分配【实战】 📝Kafka运维实战 15 - kafka 重设消费者组位移入门和实战【实战】 📝Kafka运维实战 14 - kafka消费者组消费进度(Lag)深入理…

智汇AI,应用领航 | 华宇万象问数入选2025全景赋能典型案例

7月29日&#xff0c;以“AI城市&#xff1a;数启新纪元”为主题中关村人工智能与未来城市论坛在中关村国家自主创新示范区展示中心举办。本次论坛围绕人工智能创新应用落地实践、新型数据基础设施建设、数据要素价值释放机制、城市智能治理等关键议题&#xff0c;邀请院士专家和…

sqli-labs:Less-7关卡详细解析

1. 思路&#x1f680; 本关的SQL语句为&#xff1a; $sql"SELECT * FROM users WHERE id(($id)) LIMIT 0,1";注入类型&#xff1a;字符串型&#xff08;单引号、双括号包裹&#xff09;提示&#xff1a;参数id需以))闭合 同样无法像常规一样回显&#xff0c;php输出语…

编程算法:从理论基石到产业变革的核心驱动力

文章目录 算法的本质与效率衡量 基础算法范式的实践价值 排序算法的演进与选择 动态规划的实用技巧 算法在现代技术栈中的应用 大数据处理的算法框架 编译器中的算法优化 算法驱动的产业变革 金融领域的算法应用 医疗健康领域的算法创新 制造业的算法优化 算法的未来趋势 结语 …

深度学习中的注意力机制:原理、应用与未来展望

在人工智能领域&#xff0c;深度学习技术已经取得了巨大的突破&#xff0c;而注意力机制&#xff08;Attention Mechanism&#xff09;作为深度学习中的一个重要概念&#xff0c;正在逐渐改变我们对模型的理解和应用。本文将深入探讨注意力机制的原理、在不同领域的应用以及未来…