配置 Data Grid
Red Hat Data Grid 复制链接链接已复制到粘贴板!
Data Grid 是一个高性能分布式内存数据存储。
- 无架构数据结构
- 将不同对象存储为键值对的灵活性。
- 基于网格的数据存储
- 旨在在集群中分发和复制数据。
- 弹性扩展
- 动态调整节点数量,以便在不中断服务的情况下满足需求。
- 数据互操作性
- 从不同端点在网格中存储、检索和查询数据。
Data Grid 文档 复制链接链接已复制到粘贴板!
红帽客户门户网站中提供了 Data Grid 的文档。
Data Grid 下载 复制链接链接已复制到粘贴板!
访问红帽客户门户上的 Data Grid 软件下载。
您必须有一个红帽帐户才能访问和下载数据中心软件。
使开源包含更多 复制链接链接已复制到粘贴板!
红帽致力于替换我们的代码、文档和 Web 属性中存在问题的语言。我们从这四个术语开始:master、slave、黑名单和白名单。由于此项工作十分艰巨,这些更改将在即将推出的几个发行版本中逐步实施。有关更多详情,请参阅我们的首席技术官 Chris Wright 提供的消息。
第 1 章 Data Grid Caches 复制链接链接已复制到粘贴板!
Data Grid 缓存提供灵活的内存数据存储,您可以配置以适应用例,例如:
- 使用高速本地缓存提高应用程序性能。
- 通过减少写入操作的卷来优化数据库。
- 为跨集群的一致数据提供弹性和持久性。
1.1. 缓存接口 复制链接链接已复制到粘贴板!
cache<K,V > 是 Data Grid 的中央接口,并扩展了 java.util.concurrent.ConcurrentMap。
缓存条目是 key:value 格式的高度并发数据结构,它支持广泛且可配置的数据类型,从简单字符串到更复杂的对象。
1.2. 缓存管理器 复制链接链接已复制到粘贴板!
Data Grid 提供了一个 CacheManager 接口,可让您创建、修改和管理本地或集群缓存。缓存管理器是使用数据网格缓存的起点。
实现有两个 CacheManager :
EmbeddedCacheManager- 在与客户端应用程序(也称为 Library Mode)相同的 Java 虚拟机(JVM)中运行 Data Grid 时的缓存入口点。
RemoteCacheManager-
在将 Data Grid 作为远程服务器在其自身 JVM 中运行 Data Grid 时的缓存入口点。当它开始运行时,
RemoteCacheManager会在 Data Grid 服务器上建立到 Hot Rod 端点的持久 TCP 连接。
嵌入式和远程 CacheManager 实施都共享一些方法和属性。但是,EmbeddedCacheManager 和 RemoteCacheManager 之间存在语义差异。
1.3. 缓存容器 复制链接链接已复制到粘贴板!
缓存容器声明缓存管理器控制的一个或多个本地或集群缓存。
缓存容器声明
<cache-container name="clustered" default-cache="default"> ... </cache-container>
<cache-container name="clustered" default-cache="default">
...
</cache-container>
1.4. 缓存模式 复制链接链接已复制到粘贴板!
Data Grid Cache Manager 可以创建和控制使用不同模式的多个缓存。例如,您可以使用相同的缓存管理器进行本地缓存、分发缓存和缓存,并带有 invalidation 模式。
- 本地缓存
- Data Grid 作为单一节点运行,永远不会在缓存条目上复制读取或写入操作。
- 集群缓存
- 在同一网络中运行的数据源实例可以自动发现相互发现并形成集群来处理缓存操作。
- Invalidation 模式
- 每当操作修改缓存中条目时,Data Grid 不会从所有节点中复制缓存条目,而不是从所有节点中复制过时的数据。Data Grid 仅执行本地读操作。
- 复制的缓存
- Data Grid 复制所有节点上的每个缓存条目,并仅执行本地读操作。
- 分布式缓存
- Data Grid 在节点的子集存储缓存条目,并将条目分配给固定所有者节点。Data Grid 请求从所有者节点读取操作,以确保它返回正确的值。
- scattered Caches
- Data Grid 在节点子集中存储缓存条目。默认情况下,Data Grid 为 scattered 缓存中的每个缓存条目分配一个主要所有者和一个备份所有者。Data Grid 以与分布式缓存相同的方式分配主要所有者,而备份所有者始终是启动写入操作的节点。Data Grid 从至少一个所有者节点读取操作,以确保它返回正确的值。
1.4.1. 缓存模式比较 复制链接链接已复制到粘贴板!
您选择的缓存模式取决于您的数据所需的质量和保证。
下表总结了缓存模式之间的主要区别:
| 缓存模式 | clustered? | 读取性能 | 写入性能 | 容量 | 可用性 | 功能 |
|---|---|---|---|---|---|---|
| Local | 否 | 高 (本地) | 高 (本地) | 单一节点 | 单一节点 | complete |
| Simple(简单) | 否 | 最高 (本地) | 最高 (本地) | 单一节点 | 单一节点 | 部分: 没有事务、持久性或索引。 |
| Invalidation | 是 | 高 (本地) | 低 (所有节点,无数据) | 单一节点 | 单一节点 | 部分: 无索引。 |
| 复制 | 是 | 高 (本地) | 最低 (所有节点) | 最小节点 | 所有节点 | complete |
| 分布式 | 是 | Medium (所有者) | Medium (所有者节点) | 所有节点容量的总和除以所有者数。 | 所有者节点 | complete |
| scattered | 是 | Medium (主要) | 更高 (单个 RPC) | 所有节点容量总和除以 2 个。 | 所有者节点 | 部分: 没有事务。 |
第 2 章 本地缓存 复制链接链接已复制到粘贴板!
虽然 Data Grid 在集群模式中特别有意义,但它还提供非常强大的本地模式。在这个模式中,它充当类似于 ConcurrentHashMap 的简单内存中数据缓存。
但为什么要使用本地缓存而不是映射?缓存提供很多简单映射功能,包括 write-through 和 write-behind 到持久性存储,驱除条目以防止内存不足和过期。
Data Grid 的 Cache 接口将 JDK 的 ConcurrentMap criu-criu making 从映射扩展到 Data Grid 简单。
Data Grid 缓存也支持事务,与现有事务管理器集成或运行单独的事务管理器。本地缓存事务有两个选择:
-
何时锁定?在写入操作中或用户明确调用
AdvancedCache.lock (keys)时,pessimistic 锁定 锁定密钥。Optimistic locking 仅在事务提交期间锁定密钥,而是在提交时抛出WriteSkewCheckException,如果在当前事务读取它们后另一个事务修改了同一密钥,则只会在提交时抛出 WriteSkewCheckException。 - 隔离级别。我们 支持已提交且 可重复读取。
2.1. 简单缓存 复制链接链接已复制到粘贴板!
传统的本地缓存使用与集群缓存相同的架构,即它们使用拦截器堆栈。这样,可以重复使用许多实施。但是,如果不需要高级功能并且性能更为重要,则可以使用拦截器堆栈,也可以使用简单的缓存。
那么,哪些功能被剥离?从配置的角度来看,简单的缓存不支持:
- 事务和调用批处理
- Persistence (缓存存储和加载程序)
- 自定义拦截器(没有拦截器堆栈!)
- 索引
- transcoding
- 存储为二进制(对本地缓存非常有用)
从 API 的角度来看,这些功能会抛出异常:
- 添加自定义拦截器
- 分布式执行器框架
那么,是什么?
- 基本映射类 API
- 缓存监听程序(本地监听程序)
- 过期
- 驱除
- 安全
- JMX 访问
统计(但在最大性能方面,建议使用 statistics-available=false 来将此关闭)。
- 声明性配置
<local-cache name="mySimpleCache" simple-cache="true"> <!-- expiration, eviction, security... --> </local-cache><local-cache name="mySimpleCache" simple-cache="true"> <!-- expiration, eviction, security... --> </local-cache>Copy to Clipboard Copied! Toggle word wrap Toggle overflow - 编程配置
DefaultCacheManager cm = getCacheManager(); ConfigurationBuilder builder = new ConfigurationBuilder().simpleCache(true); cm.defineConfiguration("mySimpleCache", builder.build()); Cache cache = cm.getCache("mySimpleCache");DefaultCacheManager cm = getCacheManager(); ConfigurationBuilder builder = new ConfigurationBuilder().simpleCache(true); cm.defineConfiguration("mySimpleCache", builder.build()); Cache cache = cm.getCache("mySimpleCache");Copy to Clipboard Copied! Toggle word wrap Toggle overflow
如果您将其配置为使用事务,配置验证会抛出异常,则对不支持的功能进行简单的缓存检查。
第 3 章 集群缓存 复制链接链接已复制到粘贴板!
集群缓存使用 JGroups 技术作为传输层将数据在整个网络间存储数据。
3.1. Invalidation 模式 复制链接链接已复制到粘贴板!
您可以使用 Data Grid invalidation 模式来优化执行大量读操作的系统。一个很好的例子是使用失效的,以防止在状态更改时进行大量数据库写入。
只有在为数据库(如数据库)具有另一个永久存储,且仅在读密集型系统中优化时,此缓存模式才有意义,以防止每次读取时达到数据库。如果缓存被配置为无效,则每次数据在缓存中都会更改,集群中的其他缓存都会收到一条信息通知它们的数据现在过时,应该从内存和任何本地存储中删除。
图 3.1. Invalidation 模式
有时,应用程序会从外部存储读取值,并希望将其写入本地缓存,而无需将其从其他节点中删除。要做到这一点,它必须调用 Cache.putForExternalRead (key, value) 而不是 Cache.put (key, value)。
Invalidation 模式可用于共享缓存存储。写入操作同时更新共享存储,它会从其他节点的内存中删除过时的值。这样做的好处是两倍:与复制整个值相比,网络流量被最小化,因为与复制整个值相比,网络流量会很小,集群中的其他缓存也会以 lazy 方式查找修改的数据,只在需要时才需要。
切勿将 invalidation 模式用于 本地存储。Invalidation 消息不会删除本地存储中的条目,有些节点将保持过时的值。
也可以使用特殊的缓存加载程序 ClusterLoader 配置无效的缓存缓存。启用 ClusterLoader 时,找不到本地节点上密钥的读取操作将首先从所有其他节点请求它,并将它存储在本地内存中。在某些情况下,它将存储过时的值,因此只有在对过时的值具有高容错能力时才使用它。
invalidation 模式可以是同步或异步模式。同步时,写入块直到集群中的所有节点都被驱除。异步时,原始器广播消息,但不会等待响应。这意味着其他节点仍然会在原始器上完成写入后看到过时的值。
事务可用于批量处理无效消息。事务在主所有者上获取密钥锁定。要了解有关如何分配主要所有者的更多信息,请阅读 主要所有者部分。
- 使用 pessimistic 锁定时,每个写入都会触发锁定消息,该消息广播到所有节点。在事务提交过程中,原始器会广播一阶段准备消息(可选)使所有受影响的密钥无效,并释放锁定。
- 使用最佳锁定时,原始器会广播准备消息、提交消息和解锁消息(可选)。一个阶段准备或解锁消息是 fire-and-forget,最后一个消息始终释放锁定。
3.2. 复制的缓存 复制链接链接已复制到粘贴板!
写入任何节点上的复制缓存的条目将复制到集群中的所有其他节点,并可以从任何节点本地检索。复制模式提供了一种快速轻松地在集群中共享状态的方法,但复制只在小集群(在 10 个节点下)中表现良好,因为使用集群大小线性扩展所需的消息数量。Data Grid 可以配置为使用 UDP 多播,这会将此问题降低到某种程度。
每个键都有一个主所有者,用于序列化数据容器更新以提供一致性。要了解有关如何分配主要所有者的更多信息,请阅读 主要所有者部分。
图 3.2. 复制模式
复制模式可以是同步的,也可以是异步模式。
-
同步复制会阻止调用者(例如,在
cache.put (key, value)上),直到修改成功复制到集群中的所有节点。 - 异步复制在后台执行复制,写入操作会立即返回。不建议异步复制,因为远程节点上发生的通信错误或错误不会报告给调用者。
如果启用了事务,则不会通过主所有者复制写入操作。
- 使用 pessimistic 锁定时,每个写入都会触发锁定消息,该消息广播到所有节点。在事务提交期间,源器会广播一阶段准备消息和解锁消息(可选)。一个阶段准备,或解锁消息是 fire-and-forget。
- 使用最佳锁定时,原始器会广播准备消息、提交消息和解锁消息(可选)。同样,一阶段准备或解锁消息是 fire-and-forget。
3.3. 分布式缓存 复制链接链接已复制到粘贴板!
分发会尝试在缓存中保留任意条目的固定副本数,配置为 numOwners。这允许缓存线性扩展,在节点添加到集群中时存储更多数据。
当节点加入并离开集群时,会出现一个键超过 numOwners 副本的时间。特别是,如果 numOwners 节点快速连续,一些条目将会丢失,因此我们表示分布式缓存容许 numOwners - 1 节点失败。
副本数代表在性能和数据持久性之间权衡。您维护的更副本,性能越低,但由于服务器或网络故障而丢失数据的风险也较低。无论有多少副本被维护,分布仍然会线性扩展,这对于 Data Grid 的可扩展性至关重要。
密钥的所有者被分成 一个主所有者,它协调对密钥的写入,以及零个或更多 备份所有者。要了解有关如何分配主和备份所有者的更多信息,请阅读 主要所有权 部分。
图 3.3. 分布式模式
读取操作将从主所有者请求值,但如果它没有以合理的时间响应,我们也从备份所有者请求值。( infinispan.stagger.delay 系统属性(以毫秒为单位)控制请求之间的延迟。) 如果本地缓存中存在密钥,或者所有所有者都速度较慢,则读取操作可能需要 0 个信息。
写入操作也会生成最多 2 个 * numOwners 消息:一个消息从原始者到主所有者,numOwners - 1 消息来自主所有者,即来自主到备份的消息,以及对应的 ACK 消息。
缓存拓扑更改可能会导致重试和其他消息,同时用于读取和写入。
与复制模式一样,分布式模式也可以是同步或异步的。与在复制模式中一样,不建议异步复制,因为它可能会丢失更新。除了丢失更新外,当线程写入密钥时,异步分布式缓存也可以看到过时的值,然后立即读取同一密钥。
事务分布式缓存使用与事务复制缓存相同的消息类型,除了 lock/prepare/commit/unlock 消息外,消息只会发送到受影响的节点(至少属于事务的节点),而不是广播到集群中的所有节点。作为优化,如果事务写入单个密钥,并且原始器是密钥的主要所有者,则不会复制锁定消息。
3.3.1. 读取一致性 复制链接链接已复制到粘贴板!
即使使用同步复制,分布式缓存也无法线性。(对于事务缓存,我们说它们不支持序列化/快照隔离。) 我们可以有一个线程进行单个放置:
cache.get(k) -> v1 cache.put(k, v2) cache.get(k) -> v2
cache.get(k) -> v1
cache.put(k, v2)
cache.get(k) -> v2
但是,另一个线程可能会以不同顺序看到值:
cache.get(k) -> v2 cache.get(k) -> v1
cache.get(k) -> v2
cache.get(k) -> v1
原因在于,读取 可以从任何 所有者返回值,具体取决于主所有者的回复速度。写入不是所有 owners owners- iwlin 事实的原子,只有在从备份收到确认后,主才会提交更新。当主要正在等待备份中的确认消息时,从备份中读取将会看到新值,但从主读取将看到旧值。
3.3.2. 密钥所有权 复制链接链接已复制到粘贴板!
分布式缓存将条目分成固定数量的片段,并将每个网段分配给所有者节点列表。复制的缓存执行相同的操作,但每个节点都是所有者的除外。
所有者列表中的第一个节点 是主所有者。列表中的其他节点是 备份所有者。当缓存拓扑更改时,因为节点加入或离开集群,段所有权表会广播到每个节点。这允许节点查找密钥,而不生成多播请求或维护每个密钥的元数据。
numSegments 属性配置可用的片段数量。但是,除非重启了集群,否则片段的数量无法更改。
同样,key-to-segment 映射无法更改。无论集群拓扑更改如何,键都必须映射到同一段。务必要确保 key-to-segment 映射平均分配分配给每个节点的片段数量,同时尽量减少集群拓扑更改时必须移动的片段数量。
您可以通过配置 KeyPartitioner 或使用 Grouping API 来自定义 key-to-segment 映射。
但是,Data Grid 提供以下实现:
- SyncConsistentHashFactory
使用基于 一致的哈希 的算法。当禁用服务器提示时,默认选择。
只要集群是对称,这个实现始终将密钥分配给每个缓存中的同一节点。换句话说,所有缓存在所有节点上运行。这种实现存在一些负点,因此负载分布稍不均匀。它还在加入或离开时移动更多片段,而不是严格必要。
- TopologyAwareSyncConsistentHashFactory
-
与
SyncConsistentHashFactory类似,但针对 Server Hinting 进行调整。当启用服务器提示时,默认选择。 - DefaultConsistentHashFactory
实现比
SyncConsistentHashFactory甚至一个缺点。加入集群的顺序决定了哪些节点拥有哪些部分。因此,可能会将密钥分配给不同缓存中的不同节点。是从 5.2 到版本 8.1 到禁用服务器提示的版本 8.1 的默认设置。
- TopologyAwareConsistentHashFactory
与 DefaultConsistentHashFactory 类似,但针对 Server Hinting 进行调整。
是从 5.2 到版本 8.1 到启用服务器提示的版本 8.1 的默认设置。
- ReplicatedConsistentHashFactory
- 用于在内部实施复制缓存。您永远不会在分布式缓存中明确选择此算法。
3.3.2.1. 容量因素 复制链接链接已复制到粘贴板!
容量因素根据节点可用的资源分配片段到节点的映射。
要配置容量因素,您可以指定任何非负数,Data Grid 哈希算法会为每个节点分配一个负载因其容量因子(作为主要所有者和备份所有者)权重。
例如,nodeA 在同一 Data Grid 集群中有 2 倍的内存,它比 nodeB 可用。在这种情况下,将 capacityFactor 设置为 2 的值,将 Data Grid 配置为将片段数量分配给 nodeA。
可以设置容量因数 0, 但仅在节点没有加入集群时,才有足够长以有用的数据所有者。
3.3.3. 零容量节点 复制链接链接已复制到粘贴板!
您可能需要配置一个整个节点,其中每个缓存、用户定义的缓存和内部缓存的容量因素都是 0。在定义零容量节点时,节点不会保存任何数据。这是您如何声明零容量节点:
<cache-container zero-capacity-node="true" />
<cache-container zero-capacity-node="true" />
new GlobalConfigurationBuilder().zeroCapacityNode(true);
new GlobalConfigurationBuilder().zeroCapacityNode(true);
但请注意,对于分布式缓存,这才是正确的。如果您使用复制缓存,则节点仍会保留值的副本。仅使用分布式缓存来充分利用此功能。
3.3.4. 哈希配置 复制链接链接已复制到粘贴板!
这是您如何通过 XML 以声明性方式配置哈希:
<distributed-cache name="distributedCache" owners="2" segments="100" capacity-factor="2" />
<distributed-cache name="distributedCache" owners="2" segments="100" capacity-factor="2" />
这是您在 Java 中以编程方式配置它:
3.3.5. 初始集群大小 复制链接链接已复制到粘贴板!
在处理拓扑更改时,数据网格非常动态性质(例如,在运行时添加 / 节点)意味着,节点在启动前不会等待其他节点存在。虽然这非常灵活,但可能不适用于在缓存启动前需要特定节点加入集群的应用程序。因此,您可以在继续缓存初始化前指定应该加入集群的节点数量。要做到这一点,请使用 initialClusterSize 和 initialClusterTimeout 传输属性。声明性 XML 配置:
<transport initial-cluster-size="4" initial-cluster-timeout="30000" />
<transport initial-cluster-size="4" initial-cluster-timeout="30000" />
编程 Java 配置:
GlobalConfiguration global = new GlobalConfigurationBuilder() .transport() .initialClusterSize(4) .initialClusterTimeout(30000, TimeUnit.MILLISECONDS) .build();
GlobalConfiguration global = new GlobalConfigurationBuilder()
.transport()
.initialClusterSize(4)
.initialClusterTimeout(30000, TimeUnit.MILLISECONDS)
.build();
以上配置将在初始化前等待 4 个节点加入集群。如果初始节点没有出现在指定的超时时间中,缓存管理器将无法启动。
3.3.6. L1 缓存 复制链接链接已复制到粘贴板!
启用 L1 后,节点将在短时间内在本地进行远程读取(默认为配置 10 分钟),重复查找将返回本地 L1 值,而不是再次询问所有者。
图 3.4. L1 缓存
L1 缓存不能自由。启用它的成本是,每个条目更新都必须将无效的消息广播到所有节点。当缓存配置有最大大小时,L1 条目可以像任何其他条目一样被驱除。启用 L1 将提高对非本地密钥重复读取的性能,但它会减慢写速度,它将增加对某种程度的内存消耗。
L1 缓存是否适合您?正确的方法是,在启用了 L1 的情况下对应用程序进行基准测试,并查看哪个最适合您的访问模式。
3.3.7. 服务器提示 复制链接链接已复制到粘贴板!
可以指定以下拓扑提示:
- 机器
- 当多个 JVM 实例在同一节点上运行,或者多个虚拟机在同一物理机上运行时,这可能最有用。
- rack
- 在大型集群中,位于同一机架中的节点更有可能同时遇到硬件或网络故障。
- 站点
- 有些集群可能有多个物理位置的节点,以实现额外的弹性。请注意,跨站点复制是需要跨越两个或多个数据中心的集群的另一个替代方案。
以上所有都是可选的。提供时,分发算法将尝试尽可能将每个段的所有权分散到多个站点、机架和机器(按这个顺序)。
3.3.7.1. 配置 复制链接链接已复制到粘贴板!
提示在传输级别配置:
<transport
cluster="MyCluster"
machine="LinuxServer01"
rack="Rack01"
site="US-WestCoast" />
<transport
cluster="MyCluster"
machine="LinuxServer01"
rack="Rack01"
site="US-WestCoast" />
3.3.8. 密钥关联性服务 复制链接链接已复制到粘贴板!
在分布式缓存中,密钥被分配给具有不透明算法的节点列表。无法撤销计算,并生成映射到特定节点的密钥。但是,我们可以生成一系列(伪)随机键,查看其主所有者是什么,并在需要密钥映射到特定节点时将它们分发给应用程序。
3.3.8.1. API 复制链接链接已复制到粘贴板!
以下代码片段描述了如何获取和使用对此服务的引用。
服务在第 2 步中启动:此时,它使用提供的 Executor 生成和队列密钥。在第 3 步中,我们从服务获取一个密钥,并在第 4 步中使用它。
3.3.8.2. 生命周期 复制链接链接已复制到粘贴板!
KeyAffinityService 扩展了 生命周期,允许停止和(重新)启动它:
public interface Lifecycle {
void start();
void stop();
}
public interface Lifecycle {
void start();
void stop();
}
该服务通过 KeyAffinityServiceFactory 进行实例化。所有工厂方法都有一个 Executor 参数,该参数用于异步密钥生成(因此它不会在调用者的线程中发生)。用户负责处理此可执行文件的关机 。
启动后,需要明确停止 KeyAffinityService。这会停止后台密钥生成,并释放其他持有的资源。
KeyAffinityService 本身停止的唯一情形是注册它的缓存管理器关闭的唯一情况。
3.3.8.3. 拓扑更改 复制链接链接已复制到粘贴板!
当缓存拓扑更改时(例如节点加入或离开集群),KeyAffinityService 生成的密钥的所有权可能会改变。密钥关联性服务跟踪这些拓扑更改,且不会返回当前映射到不同节点的键,但不会对之前生成的密钥进行任何操作。
因此,应用程序应完全将 KeyAffinityService 视为优化,它们不应依赖生成的密钥的位置进行正确的性。
特别是,应用程序不应依赖 KeyAffinityService 生成的密钥来始终放在同一地址。密钥共存仅由 Grouping API 提供。
3.3.8.4. Grouping API 复制链接链接已复制到粘贴板!
对 Key affinity 服务 的补充,分组 API 允许您在同一节点上并置一组条目,但无法选择实际节点。
3.3.8.5. 它如何工作? 复制链接链接已复制到粘贴板!
默认情况下,使用键的 hashCode () 计算密钥片段。如果使用 grouping API,Data Grid 将计算组的片段,并用作密钥的片段。有关如何将段映射到节点的更多详细信息,请参阅 Key Ownership 部分。
在使用组 API 时,每个节点仍然可以计算每个密钥的所有者,而无需联系其他节点。因此,无法手动指定组。组可以涉及条目(由密钥类生成)或 extrinsic (由外部功能生成)。
3.3.8.6. 如何使用 grouping API? 复制链接链接已复制到粘贴板!
首先,您必须启用组。如果您要以编程方式配置数据网格,请调用:
Configuration c = new ConfigurationBuilder() .clustering().hash().groups().enabled() .build();
Configuration c = new ConfigurationBuilder()
.clustering().hash().groups().enabled()
.build();
或者,如果您使用 XML:
<distributed-cache> <groups enabled="true"/> </distributed-cache>
<distributed-cache>
<groups enabled="true"/>
</distributed-cache>
如果您掌控了关键类(您可以修改类定义,它不是不可修改库的一部分),我们建议使用内部组。内部组通过将 @Group 注释添加到方法来指定。我们来看一个例子:
group 方法必须返回 String
如果您没有对密钥类进行控制,或者组的确定是键类的不当问题,我们建议使用 extrinsic 组。通过实施 Grouper 接口来指定 extrinsic 组。
public interface Grouper<T> {
String computeGroup(T key, String group);
Class<T> getKeyType();
}
public interface Grouper<T> {
String computeGroup(T key, String group);
Class<T> getKeyType();
}
如果为同一键类型配置了多个 Grouper 类,则调用它们的所有类,接收上一个键计算的值。如果键类也具有 @Group 注释,则第一个 Grouper 将接收被注释的方法计算的组。这可让您在使用内部组时对组进行更大的控制。让我们来看看一个 Grouper 实现示例:
grouper 实现必须在缓存配置中显式注册。如果您要以编程方式配置数据网格:
Configuration c = new ConfigurationBuilder() .clustering().hash().groups().enabled().addGrouper(new KXGrouper()) .build();
Configuration c = new ConfigurationBuilder()
.clustering().hash().groups().enabled().addGrouper(new KXGrouper())
.build();
或者,如果您使用 XML:
<distributed-cache>
<groups enabled="true">
<grouper class="com.acme.KXGrouper" />
</groups>
</distributed-cache>
<distributed-cache>
<groups enabled="true">
<grouper class="com.acme.KXGrouper" />
</groups>
</distributed-cache>
3.3.8.7. 高级接口 复制链接链接已复制到粘贴板!
AdvancedCache 有两个特定于组的方法:
- getGroup(groupName)
- 检索属于某个组的缓存中的所有密钥。
- removeGroup(groupName)
- 删除属于组缓存中的所有密钥。
两种方法都会迭代整个数据容器和存储(如果存在),因此当缓存包含大量小组时,它们可能会很慢。
3.4. scattered Caches 复制链接链接已复制到粘贴板!
分散模式与分发模式非常相似,因为它允许线性扩展集群。它通过维护两个数据副本(使用 numOwners=2 作为分发模式)来允许单节点故障。与分布式不同,数据的位置不是固定的;虽然我们使用相同的 Consistent Hash 算法来查找主要所有者,但备份副本存储在上次写入数据的节点上。当写入源自主所有者时,备份副本将存储在任何其他节点上(此副本的确切位置不重要)。
这对任何写入(Distribution Mode)具有单一远程过程调用(RPC)的优势,但读取必须始终以主所有者为目标。这会提高写入速度,但读速度可能较慢,因此这个模式更适合于写入密集型应用程序。
存储多个备份副本也会降低内存消耗。要删除过时的备份副本,invalidation 消息会在集群中广播,这会产生一些开销。这使得在非常大的集群中分散模式性能较低(这种情况将来可能会优化)。
当节点崩溃时,主副本可能会丢失。因此,集群必须协调备份,并找出最后写入的备份副本。这个过程会在状态传输过程中生成更多网络流量。
由于数据写入器也是备份,因此即使我们在传输级别上指定了 machine/rack/site ids,集群也无法对同一机器/rack/site 上的多个故障具有弹性。
目前无法在事务缓存中使用 scattered 模式。不支持异步复制;改为使用异步缓存 API。功能命令不会被实施,但预计会很快添加它们。
缓存被配置为与其他缓存模式类似,以下是声明性配置示例:
<scattered-cache name="scatteredCache" />
<scattered-cache name="scatteredCache" />
这是如何以编程方式配置它:
Configuration c = new ConfigurationBuilder() .clustering().cacheMode(CacheMode.SCATTERED_SYNC) .build();
Configuration c = new ConfigurationBuilder()
.clustering().cacheMode(CacheMode.SCATTERED_SYNC)
.build();
分散模式不在服务器配置中公开,因为服务器通常通过 Hot Rod 协议访问。该协议会自动为写入选择主要所有者,因此写入(具有两个所有者的分布式模式中)还需要集群中的单个 RPC。因此,分散的缓存不会带来性能优势。
3.5. 带有集群缓存的异步通信 复制链接链接已复制到粘贴板!
3.5.1. 异步通信 复制链接链接已复制到粘贴板!
所有集群缓存模式都可以配置为在 < replicated-cache/>、< 使用与 distributed-cache> 或 < invalidation-cache/& gt; 元素中mode="ASYNC" 属性的异步通信。
使用异步通信时,原始器节点不会从其他节点接收有关操作状态的任何确认,因此无法检查它是否在其他节点上成功。
我们不推荐异步通信,因为它们可能会导致数据不一致,因此结果很难考虑。然而,有时速度比一致性更重要,而对于这些情况,可以使用 选项。
3.5.2. Asynchronous API 复制链接链接已复制到粘贴板!
Asynchronous API 允许您使用同步通信,但不阻止用户线程。
有一个注意事项:异步操作不会保留程序顺序。如果线程调用 cache.putAsync (k, v1); cache.putAsync (k, v2),则最终值 k 可以是 v1 或 v2。与使用异步通信相比,最终的值不能是一个节点上的 v1,另一个节点上的 v2。
3.5.3. 返回异步通信中的值 复制链接链接已复制到粘贴板!
由于 Cache 接口扩展 java.util.Map,因此 放置(密钥、值) 和 remove (key) 等写入方法默认返回前面的值。
在某些情况下,返回值可能不正确:
-
使用带有、Flag.IGNORE_RETURN_VALUEFlag.SKIP_REMOTE_LOOKUP或Flag.SKIP_CACHE_LOAD. -
当缓存配置有
unreliable-return-values="true"时。 - 使用异步通信时。
- 当有多个并发写入同一密钥时,缓存拓扑会改变。拓扑更改将使数据网格重试写操作,重试操作的返回值不是可靠的。
事务缓存在 3 和 4 时返回正确的之前值。但是,事务缓存在分布式模式中也有一个 getcha:,则 read-committed 隔离级别也以可重复读取的形式实施。这意味着 "double-checked locking" 示例无法正常工作:
实施此操作的正确方法是使用 cache.getAdvancedCache ().withFlags (Flag.FORCE_WRITE_LOCK).get (k)。
在具有最佳锁定的缓存中,写入也可以返回过时的值。写入偏移检查可能会避免过时的值。
第 4 章 配置数据网格缓存 复制链接链接已复制到粘贴板!
借助 Data Grid,您可以以声明性和编程方式定义用于缓存的属性和选项。
声明性配置使用遵循 Data Grid 模式的 XML 文件。另一方面,编程配置使用 Data Grid API。
在大多数情况下,您可以使用声明性配置作为缓存定义的起点。在运行时,您可以以编程方式配置缓存来调优设置或指定附加属性。但是,Data Grid 提供灵活性,以便您可以选择声明性、编程或两者的组合。
4.1. 声明性配置 复制链接链接已复制到粘贴板!
您可以通过在 infinispan.xml 中定义属性来配置数据网格缓存。
以下示例显示了 Data Grid 配置的基本结构:
4.1.1. 缓存配置模板 复制链接链接已复制到粘贴板!
借助 Data Grid,您可以定义可应用到多个缓存定义的配置模板,或用作复杂配置的基础。
例如,以下配置包含本地缓存的配置模板:
配置模板的继承
配置模板也可以从其他模板继承,以扩展和覆盖设置。
配置模板继承是分级。要使子配置模板从父级继承,您必须在父模板后面包含它。
以下是配置模板继承的示例:
对于具有多个值的元素(如 属性 )而言,配置模板继承是额外的。从父配置生成子配置合并值。
例如,& lt;property value_x="foo" /> 在父配置中与 < property value_y="bar" /> 合并,以便在子配置中生成 < ;property value_x="foo" value_y="bar" />。
4.1.2. 缓存配置通配符 复制链接链接已复制到粘贴板!
您可以使用通配符将缓存定义与配置模板匹配。
如果缓存名称与多个通配符匹配,则数据网格会抛出异常。
4.1.3. 多个配置文件 复制链接链接已复制到粘贴板!
Data Grid 支持 XML 包含(XInclude),允许您在多个文件中分割配置。
例如,以下配置使用 XInclude :
<infinispan xmlns:xi="http://www.w3.org/2001/XInclude">
<cache-container default-cache="cache-1">
<xi:include href="local.xml" />
</cache-container>
</infinispan>
<infinispan xmlns:xi="http://www.w3.org/2001/XInclude">
<cache-container default-cache="cache-1">
<xi:include href="local.xml" />
</cache-container>
</infinispan>
- 1
- 包括一个包含以下缓存定义的
local.xml文件:<local-cache name="mycache"/>
<local-cache name="mycache"/>Copy to Clipboard Copied! Toggle word wrap Toggle overflow
如果要将模式用于包含的片段,请使用 infinispan-config-fragment-11.0.xsd 模式:
include-with-schema.xml
<local-cache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:infinispan:config:11.0 https://infinispan.org/schemas/infinispan-config-fragment-11.0.xsd"
xmlns="urn:infinispan:config:11.0"
name="mycache"/>
<local-cache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:infinispan:config:11.0 https://infinispan.org/schemas/infinispan-config-fragment-11.0.xsd"
xmlns="urn:infinispan:config:11.0"
name="mycache"/>
Data Grid 配置只提供对 XInclude 规格的最小支持。例如,您无法使用 xpointer 属性,xi:fallback 元素、文本处理或内容协商。
参考
4.2. Data Grid Configuration API 复制链接链接已复制到粘贴板!
以编程方式配置数据网格。
全局配置
使用 GlobalConfiguration 类,将配置应用到 Cache Manager 下的所有缓存。
GlobalConfiguration globalConfig = new GlobalConfigurationBuilder() .cacheContainer().statistics(true) .metrics().gauges(true).histograms(true) .jmx().enable() .build();
GlobalConfiguration globalConfig = new GlobalConfigurationBuilder()
.cacheContainer().statistics(true)
.metrics().gauges(true).histograms(true)
.jmx().enable()
.build();
参考资料:
缓存配置
使用 ConfigurationBuilder 类来配置缓存。
参考资料:
4.3. 以编程方式配置缓存 复制链接链接已复制到粘贴板!
使用 Cache Manager 定义缓存配置。
本节中的示例使用 EmbeddedCacheManager,它是一个在与客户端相同的 JVM 中运行的 Cache Manager。
要使用 HotRod 客户端远程配置缓存,请使用 RemoteCacheManager。如需更多信息,请参阅 HotRod 文档。
配置新的缓存实例
以下示例配置一个新的缓存实例:
从现有配置创建新缓存
以下示例从现有缓存配置创建新缓存配置:
- 1
- 使用名为"replicatedCache"的缓存配置作为基础。
第 5 章 配置数据容器 复制链接链接已复制到粘贴板!
配置数据容器,其中 Data Grid 存储条目。指定缓存条目的编码,将数据保存在非堆内存中,并使用驱除或过期策略来只在内存中保留活动条目。
5.1. 为 Data Grid Caches 配置编码 复制链接链接已复制到粘贴板!
定义 MediaType,Data Grid 在写入和读取缓存时用于对数据进行编码。
当您定义 MediaType 时,您可以将数据格式指定为 Data Grid。
如果要使用 Data Grid Console、Hot Rod 客户端和 REST 客户端,请指定 application/x-protostream MediaType,以便 Data Grid 以 Protobuf 格式对数据进行编码。
流程
为 Data Grid 缓存配置中的键和值指定 MediaType。
-
声明:设置
encoding属性。 -
以编程方式:使用
encoding ()方法。
-
声明:设置
声明性示例
- 对键和值使用相同的编码:
<local-cache> <encoding media-type="application/x-protostream"/> </local-cache>
<local-cache>
<encoding media-type="application/x-protostream"/>
</local-cache>
- 对键和值使用不同的编码:
编程示例
- 对键和值使用相同的编码:
- 对键和值使用不同的编码:
ConfigurationBuilder cfg = new ConfigurationBuilder();
cfg.encoding().key().mediaType("text/plain");
cfg.encoding().value().mediaType("application/json");
ConfigurationBuilder cfg = new ConfigurationBuilder();
cfg.encoding().key().mediaType("text/plain");
cfg.encoding().value().mediaType("application/json");
5.1.1. 以 Protobuf 格式存储数据 复制链接链接已复制到粘贴板!
将数据存储在缓存中作为 Protobuf 编码的条目提供了一个独立于平台的配置,可让您从任何客户端执行缓存操作。
当您为 Data Grid Search 配置索引时,Data Grid 会自动使用 application/x-protostream 介质类型存储键和值。
流程
将
application/x-protostream指定为键和值的 MediaType,如下所示:Copy to Clipboard Copied! Toggle word wrap Toggle overflow - 配置您的客户端。
热 Rod 客户端必须注册协议缓冲区模式定义,以描述实体和客户端 marshallers。
数据网格在 application/x-protostream 和 application/json 之间转换,因此 REST 客户端只需要发送以下标头来读取和写入 JSON 格式的数据:
-
accept:用于读取操作的 application/json。 -
content-Type: 用于写入操作的 application/json。
5.2. 配置 Data Grid 以存储缓存条目 Off Heap 复制链接链接已复制到粘贴板!
Data Grid 可以使用 JVM 堆内存或 off-heap 原生内存作为缓存条目的 data 容器。默认情况下,Data Grid 将缓存条目存储在 JVM 堆内存中。
根据条目总数或最大内存量,数据网格可以使用带驱除的现成存储。使用 off-heap 时,Data Grid 使用 application/x-protostream 编码以 Protobuf 格式存储缓存条目。
流程
要将缓存条目存储在 out-heap 内存中,请使用以下方法之一配置 Data Grid :
-
声明:将
storage="OFF_HEAP"属性添加到memory元素。 -
以编程方式:在
MemoryConfigurationBuilder类中调用storage (OFF_HEAP)方法。
声明示例
<local-cache name="off_heap"> <encoding media-type="application/x-protostream"/> <memory storage="OFF_HEAP" max-size="1.5GB" when-full="REMOVE"/> </local-cache>
<local-cache name="off_heap">
<encoding media-type="application/x-protostream"/>
<memory storage="OFF_HEAP" max-size="1.5GB" when-full="REMOVE"/>
</local-cache>
Programmatic 示例
5.3. 配置驱除和过期 复制链接链接已复制到粘贴板!
驱除和过期是通过删除旧、未使用的条目来清理数据容器的两个策略。虽然驱除和过期也类似,但它们有一些重要的区别。
- 当容器大于配置的阈值时,Data Grid 驱除可让 Data Grid 通过删除条目来控制数据容器的大小。
-
mvapich 过期日期限制了存在时间条目的数量。Data Grid 使用调度程序定期删除过期条目。过期但还没有被删除的条目会在访问时立即被删除 ; 在这种情况下,过期条目的
get ()调用会返回 "null" 值。 - criu 驱除是 Data Grid 节点的本地驱除。
- mvapich 过期时间跨数据网格集群发生。
- 您可以组合使用驱除和过期,也可以相互独立使用。
-
您可以在
infinispan.xml中配置驱除和过期,以对条目应用缓存范围默认值。 - 您可以为特定条目显式定义过期设置,但您无法基于每个条目定义驱除。
- 您可以手动驱除条目并手动触发过期。
5.3.1. 驱除 复制链接链接已复制到粘贴板!
通过驱除,您可以通过删除缓存条目来控制数据容器的大小,以便在添加新条目时腾出空间。
驱除会从内存中删除条目,而不是从持久性缓存存储中删除条目。为确保在 Data Grid 驱除它们后条目仍然可用,您应该配置持久的缓存存储。
Data Grid 驱除依赖于两个配置:
- 数据容器的最大大小。
- 删除条目的策略。
数据容器大小
借助数据网格,您可以将条目存储在 Java 堆或原生内存(off-heap)中,并为数据容器设置最大大小。
您可以通过以下两种方式之一配置数据容器的最大大小:
-
条目总数(
max-count)。 最大内存量(
max-size)。要根据内存量执行驱除,您可以以字节为单位定义最大大小。因此,您必须使用二进制存储格式(如
application/x-protostream)对条目进行编码。
驱除缓存条目
当您配置 内存时,Data Grid 大约是数据容器的当前内存用量。添加或修改条目时,Data Grid 会将数据容器的当前内存用量与最大大小进行比较。如果大小超过最大值,Data Grid 将执行驱除。
在线程中立即发生驱除,添加超过最大大小的条目。
请考虑以下配置作为示例:
<memory max-count="50"/>
<memory max-count="50"/>
在这种情况下,缓存可以有总计 50 个条目。缓存达到条目总数后,写入操作会触发 Data Grid 来执行驱除。
驱除策略
策略控制数据网格执行驱除的方式。您可以手动执行驱除,或将 Data Grid 配置为执行以下操作之一:
- 删除旧条目来为新条目腾出空间。
抛出
ContainerFullException并防止创建新条目。异常驱除策略只适用于使用 2 阶段提交的事务缓存,不适用于 1 阶段提交或同步优化。
Data Grid 包括 Caffeine 缓存库,它实现了 Least Frequently Used (LFU)缓存替换算法(称为 TinyLFU)。对于非堆存储,Data Grid 使用 Least Recently Used (LRU)算法的自定义实现。
5.3.1.1. 为数据网格缓存配置总条目数 复制链接链接已复制到粘贴板!
将缓存条目的数据容器的大小限制为条目总数。
流程
- 使用适当的存储格式配置数据网格缓存编码。
指定缓存在 Data Grid 执行驱除前可以包含的条目总数。
-
声明:设置
max-count属性。 -
编程方式:调用
maxCount ()方法。
-
声明:设置
配置驱除策略,以控制 Data Grid 如何删除条目。
-
声明:设置
when-full属性。 -
编程方式:调用
whenFull ()方法。
-
声明:设置
声明示例
<local-cache name="maximum_count"> <encoding media-type="application/x-protostream"/> <memory max-count="500" when-full="REMOVE"/> </local-cache>
<local-cache name="maximum_count">
<encoding media-type="application/x-protostream"/>
<memory max-count="500" when-full="REMOVE"/>
</local-cache>
Programmatic 示例
5.3.1.2. 为数据网格缓存配置最大内存量 复制链接链接已复制到粘贴板!
将缓存条目的数据容器的大小限制为最大内存量。
流程
将您的数据网格缓存配置为使用支持二进制编码的存储格式。
您必须使用二进制存储格式根据最大内存量执行驱除。
配置缓存在数据网格执行驱除前可以使用的最大内存量(以字节为单位)。
-
声明式 :设置
max-size属性。 -
以编程方式:使用
maxSize ()方法。
-
声明式 :设置
- (可选)指定测量的字节单位。默认值为 B (字节)。对于支持的单元,请参阅 配置模式。
配置驱除策略,以控制 Data Grid 如何删除条目。
-
声明:设置
when-full属性。 -
以编程方式:使用
whenFull ()方法。
-
声明:设置
声明示例
<local-cache name="maximum_size"> <encoding media-type="application/x-protostream"/> <memory max-size="1.5GB" when-full="REMOVE"/> </local-cache>
<local-cache name="maximum_size">
<encoding media-type="application/x-protostream"/>
<memory max-size="1.5GB" when-full="REMOVE"/>
</local-cache>
Programmatic 示例
5.3.1.3. 驱除示例 复制链接链接已复制到粘贴板!
您可以将驱除配置为缓存定义的一部分。
默认内存配置
不启用驱除,这是默认配置。Data Grid 将缓存条目作为对象存储在 JVM 堆中。
<memory />
<memory />
根据条目总数进行驱除
Data Grid 将缓存条目作为对象存储在 JVM 堆中。当数据容器中有 100 个条目,Data Grid 会收到创建新条目的请求时发生驱除:
<memory max-count="100"/>
<memory max-count="100"/>
基于驱除的最大大小(以字节为单位)
如果您使用二进制存储格式对缓存条目进行编码,例如: application/x-protostream 格式,请将缓存条目存储为 byte[] 阵列。
在以下示例中,当数据容器的大小达到 500 MB (megabytes)时,Data Grid 会执行驱除,并获得创建新条目的请求:
<encoding media-type="application/x-protostream"/> <memory max-size="500 MB"/>
<encoding media-type="application/x-protostream"/>
<memory max-size="500 MB"/>
off-heap 存储
Data Grid 将缓存条目存储为原生内存的字节数。当数据容器中有 100 个条目,Data Grid 会收到创建新条目的请求时发生驱除:
<memory storage="OFF_HEAP" max-count="100"/>
<memory storage="OFF_HEAP" max-count="100"/>
带有例外策略的 off-heap 存储
Data Grid 将缓存条目存储为原生内存的字节数。当数据容器中有 100 个条目,并且 Data Grid 获取创建新条目的请求时,它会抛出异常,且不允许新条目:
<transaction mode="NONE"/> <memory storage="OFF_HEAP" max-count="100" when-full="EXCEPTION"/>
<transaction mode="NONE"/>
<memory storage="OFF_HEAP" max-count="100" when-full="EXCEPTION"/>
手动驱除
Data Grid 将缓存条目作为对象存储在 JVM 堆中。驱除没有被启用,而是使用 evict () 方法手动执行。
此配置可防止在启用传递但没有配置驱除时发出警告信息。
<memory when-full="MANUAL"/>
<memory when-full="MANUAL"/>
使用驱除进行传递
当数据网格驱除条目时,传递保留数据到缓存存储。如果启用了传递,您应该始终启用驱除,例如:
<persistence passivation="true"> ... </persistence> <memory max-count="100"/>
<persistence passivation="true">
...
</persistence>
<memory max-count="100"/>
参考
5.3.2. 过期 复制链接链接已复制到粘贴板!
当过期达到以下时间限制之一时,过期会从缓存中删除条目:
- Lifespan
- 设置条目可以存在的最大时间。
- 最大闲置
指定条目可以保持闲置的时长。如果没有为条目发生操作,它们就变为空闲状态。
重要最大闲置过期时间目前不支持带有持久性缓存存储的缓存配置。
当使用基于例外的驱除策略时,会过期但还没有从缓存数中删除到数据容器大小的条目。
5.3.2.1. Expiration 工作方式 复制链接链接已复制到粘贴板!
当您配置过期时,Data Grid 使用用来决定条目何时过期的元数据存储密钥。
-
Lifespan 使用
创建时间戳,以及 lifespan配置属性的值。 -
最大闲置
使用最后使用的时间戳和max-idle配置属性的值。
Data Grid 检查是否设置了 lifespan 或 maximum idle 元数据,然后将值与当前时间进行比较。
如果 (creation + lifespan > currentTime) 或 (lastUsed + maxIdle > currentTime),则 Data Grid 会检测到条目已过期。
每当被过期者访问或找到条目时,都会发生过期。
例如,k1 达到最大闲置时间,客户端发出 Cache.get (k1) 请求。在这种情况下,Data Grid 会检测到条目已过期,并从数据容器中删除它。Cache.get () 返回 null。
Data Grid 还过期了来自缓存存储的条目,但仅具有寿命到期。最大闲置过期时间不适用于缓存存储。如果是缓存加载程序,Data Grid 无法使条目过期,因为加载程序只能从外部存储读取。
只要 用于缓存条目,Data Grid 增加了过期元数据。这可能会将密钥的大小增加到 32 字节。
5.3.2.2. expiration Reaper 复制链接链接已复制到粘贴板!
Data Grid 使用定期运行的已回收线程来检测和删除过期条目。expiration takinger 可确保不再被访问的过期条目被删除。
Data Grid ExpirationManager 接口处理过期时间,并公开 processExpiration () 方法。
在某些情况下,您可以通过调用 processExpiration () 来禁用过期程序并手动使条目过期;例如,如果您使用本地缓存模式以及定期运行维护线程的自定义应用程序。
如果您使用集群缓存模式,则不应禁用过期时间。
在使用缓存存储时,Data Grid 总是使用 expiration 回收程序。在这种情况下,您无法禁用它。
5.3.2.3. 最大空闲和集群缓存 复制链接链接已复制到粘贴板!
因为最大闲置过期时间依赖于缓存条目的最后一次访问时间,所以集群缓存模式有一些限制。
随着寿命到期,缓存条目的创建时间提供在集群缓存之间保持一致的值。例如,所有节点上的 k1 创建时间始终相同。
对于使用集群缓存的最大闲置过期时间,所有节点上的条目最后一次访问时间并不相同。为确保条目在集群中有相同的相对访问时间,Data Grid 会在访问密钥时向所有所有者发送 touch 命令。
Data Grid send 的 touch 命令有以下注意事项:
-
cache.get ()请求不会返回,直到所有 touch 命令完成为止。这个同步行为会增加客户端请求的延迟。 - touch 命令还会更新所有所有者上的缓存条目的"集中访问"元数据,用于数据网格用于驱除。
- 通过分散的缓存模式,数据网格向所有节点发送 touch 命令,而不只是主和备份所有者。
附加信息
- 最大闲置过期时间不适用于 invalidation 模式。
- 集群缓存之间的迭代可能会返回超过最大闲置时间限制的过期条目。这个行为可确保性能,因为迭代过程中不会执行远程调用。另请注意,迭代不会刷新任何过期条目。
5.3.2.4. 过期示例 复制链接链接已复制到粘贴板!
当您将 Data Grid 配置为过期条目时,您可以为以下设置 lifespan 和最大闲置时间:
-
缓存中的所有条目(缓存范围)。您可以在
infinispan.xml中配置缓存范围的过期时间,或者以编程方式使用ConfigurationBuilder。 - 每个条目,其优先级高于缓存范围的过期值。您可以在创建时为特定条目配置过期。
当您为缓存条目明确定义 lifespan 和最大闲置时间值时,Data Grid 会将这些值与缓存条目一起复制。同样,如果您配置缓存存储,Data Grid 会保留过期值以及条目。
为所有缓存条目配置过期
在 2 秒后使所有缓存条目过期:
<expiration lifespan="2000" />
<expiration lifespan="2000" />
在最后一次访问时间后使所有缓存条目 1 秒过期:
<expiration max-idle="1000" />
<expiration max-idle="1000" />
禁用带有 interval 属性的过期时间,并在最后一次访问时间后手动使条目 1 秒过期:
<expiration max-idle="1000" interval="-1" />
<expiration max-idle="1000" interval="-1" />
在最后一次访问时间后 5 秒或 1 秒后使所有缓存条目过期,以首先发生:
<expiration lifespan="5000" max-idle="1000" />
<expiration lifespan="5000" max-idle="1000" />
在创建缓存条目时配置过期
以下示例演示了如何在创建缓存条目时配置 lifespan 和最大闲置值:
如果 Data Grid 配置为所有条目定义了 lifespan 值 1000,则前面的 Cache.put () 请求会导致条目过期:
第 6 章 配置统计、指标和 JMX 复制链接链接已复制到粘贴板!
启用数据网格导出到指标端点或通过 JMX MBeans 导出的统计信息。您还可以注册 JMX MBeans 来执行管理操作。
6.1. 启用 Data Grid Statistics 复制链接链接已复制到粘贴板!
借助 Data Grid,您可以为缓存管理器和缓存启用统计信息。但是,为缓存管理器启用统计信息不会为其控制的缓存启用统计信息。您必须明确启用缓存的统计信息。
默认情况下,Data Grid 服务器为缓存管理器启用统计信息。
流程
- 以声明性方式或以编程方式启用统计信息。
声明性
<cache-container statistics="true"> <local-cache name="mycache" statistics="true"/> </cache-container>
<cache-container statistics="true">
<local-cache name="mycache" statistics="true"/>
</cache-container>
以编程方式
6.2. 启用 Data Grid Metrics 复制链接链接已复制到粘贴板!
配置 Data Grid 以导出量表和直方图。
流程
- 以声明性方式或以编程方式配置指标。
声明性
<cache-container statistics="true"> <metrics gauges="true" histograms="true" /> </cache-container>
<cache-container statistics="true">
<metrics gauges="true" histograms="true" />
</cache-container>
以编程方式
GlobalConfiguration globalConfig = new GlobalConfigurationBuilder() .statistics().enable() .metrics().gauges(true).histograms(true) .build();
GlobalConfiguration globalConfig = new GlobalConfigurationBuilder()
.statistics().enable()
.metrics().gauges(true).histograms(true)
.build();
6.2.1. Data Grid Metrics 复制链接链接已复制到粘贴板!
网格与 Eclipse MicroProfile Metrics API 兼容,并可生成量表和直方图指标。
-
Data Grid 指标在
供应商范围内提供。与 JVM 相关的指标在 Data Grid服务器的基本范围内提供。 - 量表提供值,如写操作或 JVM 正常运行时间的平均纳秒数。默认启用量表。如果您启用统计信息,Data Grid 会自动生成量表。
- histograms 提供有关操作执行时间的详细信息,如读取、写入和删除时间。默认情况下,Data Grid 不会启用直方图,因为它们需要额外的计算。
6.3. 配置数据网格以注册 JMX MBeans 复制链接链接已复制到粘贴板!
Data Grid 可以注册可以用来收集统计信息并执行管理操作的 JMX MBeans。但是,您必须将统计信息单独启用到 JMX,否则 Data Grid 都为所有统计属性提供 0 值。
流程
- 以声明性方式或以编程方式启用 JMX。
声明性
<cache-container> <jmx enabled="true" /> </cache-container>
<cache-container>
<jmx enabled="true" />
</cache-container>
- 1
- 注册数据网格 JMX MBeans.
以编程方式
GlobalConfiguration globalConfig = new GlobalConfigurationBuilder() .jmx().enable() .build();
GlobalConfiguration globalConfig = new GlobalConfigurationBuilder()
.jmx().enable()
.build();
- 1
- 注册数据网格 JMX MBeans.
6.3.1. 命名多个缓存管理器 复制链接链接已复制到粘贴板!
如果多个 Data Grid Cache Manager 在同一 JVM 上运行,您应该唯一地标识每个缓存管理器以防止冲突。
流程
- 在您的环境中唯一标识每个缓存管理器。
例如,以下示例将 "Hibernate2LC" 指定为缓存管理器名称,这会导致名为 org.infinispan:type=CacheManager,name="Hibernate2LC" 的 JMX MBean。
声明性
<cache-container name="Hibernate2LC"> <jmx enabled="true" /> ... </cache-container>
<cache-container name="Hibernate2LC">
<jmx enabled="true" />
...
</cache-container>
以编程方式
GlobalConfiguration globalConfig = new GlobalConfigurationBuilder()
.cacheManagerName("Hibernate2LC")
.jmx().enable()
.build();
GlobalConfiguration globalConfig = new GlobalConfigurationBuilder()
.cacheManagerName("Hibernate2LC")
.jmx().enable()
.build();
6.3.2. 在自定义 MBean 服务器中注册 MBeans 复制链接链接已复制到粘贴板!
Data Grid 包含一个 MBeanServerLookup 接口,可用于在自定义 MBeanServer 实例中注册 MBeans。
流程
-
创建
MBeanServerLookup的实现,以便getMBeanServer ()方法返回自定义 MBeanServer 实例。 - 使用类的完全限定域名配置 Data Grid,如下例所示:
声明性
<cache-container> <jmx enabled="true" mbean-server-lookup="com.acme.MyMBeanServerLookup" /> </cache-container>
<cache-container>
<jmx enabled="true" mbean-server-lookup="com.acme.MyMBeanServerLookup" />
</cache-container>
以编程方式
GlobalConfiguration globalConfig = new GlobalConfigurationBuilder() .jmx().enable().mBeanServerLookup(new com.acme.MyMBeanServerLookup()) .build();
GlobalConfiguration globalConfig = new GlobalConfigurationBuilder()
.jmx().enable().mBeanServerLookup(new com.acme.MyMBeanServerLookup())
.build();
6.3.3. Data Grid MBeans 复制链接链接已复制到粘贴板!
Data Grid 公开了代表可管理资源的 JMX MBeans。
org.infinispan:type=Cache- 可用于缓存实例的属性和操作。
org.infinispan:type=CacheManager- 可用于缓存管理器的属性和操作,包括数据网格缓存和集群健康统计信息。
有关可用 JMX MBeans 的完整列表以及描述和可用的操作和属性,请参阅 Data Grid JMX 组件 文档。
第 7 章 设置持久性存储 复制链接链接已复制到粘贴板!
Data Grid 可以向外部存储保留内存数据,为您提供管理数据的额外功能,例如:
- 持久性
- 通过添加缓存存储,您可以将数据持久保留到非易失性存储中,以便在重启后保留数据。
- 直写缓存
- 将 Data Grid 配置为持久性存储前面的缓存层简化了应用程序的数据访问,因为 Data Grid 处理与外部存储的所有交互。
- 数据溢出
- 使用驱除和传递技术可确保 Data Grid 只保留在内存中使用的数据,并将旧的条目写入持久性存储。
7.1. Data Grid Cache Stores 复制链接链接已复制到粘贴板!
缓存存储将 Data Grid 连接到持久性数据源,并实现 NonBlockingStore 接口。
7.1.1. 配置缓存存储 复制链接链接已复制到粘贴板!
以声明性方式或以编程方式在链中添加缓存存储到 Data Grid 缓存。缓存读取操作会按照配置的顺序检查每个缓存存储,直到它们找到有效的非null 元素。写入操作会影响所有缓存存储,但您配置为只读的缓存存储除外。
流程
-
使用
persistence参数配置缓存的持久性层。 配置缓存存储是否对节点本地,还是在集群中共享。
以声明性方式使用共享属性,或者以编程方式使用
方法。shared(false)根据需要配置其他缓存存储属性。自定义缓存存储也可以包含
属性参数。注意将缓存存储配置为共享或不共享(仅限本地)决定您应该设置哪些参数。在某些情况下,在缓存存储配置中使用错误的参数组合可能会导致数据丢失或性能问题。
例如,如果缓存存储对节点而言是本地的,则最好获取状态并在启动时清除。但是,如果缓存存储被共享,则您不应该在启动时获取状态或清除。
本地(非共享)文件存储
共享自定义缓存存储
单个文件存储
7.1.2. 为基于文件的缓存存储设置全局持久位置 复制链接链接已复制到粘贴板!
Data Grid 使用全局文件系统位置将数据保存到持久性存储。
全局持久位置对于每个 Data Grid 实例都必须是唯一的。要在多个实例间共享数据,请使用共享持久位置。
Data Grid 服务器使用 $RHDG_HOME/server/data 目录作为全局持久位置。
如果您使用 Data Grid 作为嵌入在自定义应用程序中的库,并且启用了 global-state,则全局持久位置默认为 user.dir 系统属性。此系统属性通常使用应用程序启动的目录。您应该将全局持久位置配置为使用合适的位置。
声明性配置
new GlobalConfigurationBuilder().globalState().enable().persistentLocation("example", "my.data");
new GlobalConfigurationBuilder().globalState().enable().persistentLocation("example", "my.data");
基于文件的缓存存储和全局持久位置
使用基于文件的缓存存储时,您可以选择为存储指定文件系统目录。除非声明绝对路径,否则目录始终相对于全局持久位置。
例如,您要配置全局持久位置,如下所示:
<global-state> <persistent-location path="/tmp/example" relative-to="my.data"/> </global-state>
<global-state>
<persistent-location path="/tmp/example" relative-to="my.data"/>
</global-state>
然后,配置使用名为 myDataStore 的单个文件缓存存储,如下所示:
<file-store path="myDataStore"/>
<file-store path="myDataStore"/>
在这种情况下,配置会在 /tmp/example/myDataStore/myCache.dat中生成单一文件缓存存储
如果您试图设置位于全局持久位置外的绝对路径,并且启用了 global-state,Data Grid 会抛出以下异常:
ISPN000558: "The store location 'foo' is not a child of the global persistent location 'bar'"
ISPN000558: "The store location 'foo' is not a child of the global persistent location 'bar'"
7.1.3. passivation 复制链接链接已复制到粘贴板!
传递配置 Data Grid,在从内存中驱除这些条目时写入条目以缓存存储。这样,传递可确保只维护一个条目的副本,可以是内存中或缓存存储中,这可以防止不必要的且可能昂贵的写入持久性存储。
当尝试访问传递的条目时,激活是从缓存存储中恢复内存的过程。因此,当启用 passivation 时,您必须配置实现 CacheWriter 和 CacheLoader 接口的缓存存储,以便可以从持久性存储写入和加载条目。
当 Data Grid 从缓存中驱除条目时,它会通知缓存监听程序被传递该条目,然后将条目存储在缓存存储中。当 Data Grid 获取被驱除条目的访问请求时,它会从缓存存储加载条目到内存中,然后通知缓存监听程序已激活该条目。
- 传递使用数据网格配置中的第一个缓存加载程序,并忽略所有其他缓存。
不支持传递:
- 事务存储.传递写入并从实际数据网格提交边界范围之外的存储中删除条目。
- 共享存储。共享缓存存储需要条目始终存在于存储中用于其他所有者。因此,不支持 passivation,因为无法删除条目。
如果您启用了使用事务存储或共享存储的传递,则数据网格会抛出异常。
7.1.3.1. 传递和缓存存储 复制链接链接已复制到粘贴板!
禁用 passivation
写入内存中的数据会导致写入持久性存储。
如果 Data Grid 从内存驱除数据,则持久性存储中的数据会包括从内存驱除的条目。这样,持久性存储是内存缓存的超集。
如果没有配置驱除,则持久性存储中的数据会在内存中提供数据副本。
启用 passivation
只有在数据从内存中驱除数据时,Data Grid 才会将数据添加到持久性存储中。
当 Data Grid 激活条目时,它会在内存中恢复数据并从持久性存储中删除数据。这样,持久性存储中的数据和数据的数据形成了整个数据集的独立子集,两者之间没有交集。
使用共享缓存存储时,持久性存储中的条目可能会过时。这是因为 Data Grid 在激活时不会从共享缓存存储中删除传递的条目。
值会在内存中更新,但之前传递的条目会保留在持久性存储中,且超出日期值。
下表显示了在一系列操作后内存和持久性存储中的数据:
| 操作 | 禁用 passivation | 启用 passivation | 使用共享缓存存储启用传递 |
|---|---|---|---|
| 插入 k1。 |
memory: k1 |
memory: k1 |
memory: k1 |
| 插入 k2. |
memory: k1, k2 |
memory: k1, k2 |
memory: k1, k2 |
| 驱除线程运行和驱除 k1。 |
memory: k2 |
memory: k2 |
memory: k2 |
| 读 k1。 |
memory: k1, k2 |
memory: k1, k2 |
memory: k1, k2 |
| 驱除线程运行和驱除 k2。 |
memory: k1 |
memory: k1 |
memory: k1 |
| 删除 k2。 |
memory: k1 |
memory: k1 |
memory: k1 |
7.1.4. cache Loaders 和 Transactional Caches 复制链接链接已复制到粘贴板!
只有基于 JDBC 字符串的缓存存储支持事务操作。如果将缓存配置为事务性,您应该设置 transactional=true,以便使数据存储在持久性存储中与内存中的数据同步。
对于所有其他缓存存储,Data Grid 不对事务操作中的缓存加载程序进行编码。如果事务在在内存中修改数据时成功修改数据,但不会完全应用缓存存储中的数据更改,这可能会导致数据不一致。在这种情况下,手动恢复无法使用缓存存储。
7.1.5. 分段缓存存储 复制链接链接已复制到粘贴板!
缓存存储可以将数据组织到哪些键映射到的散列空间片段中。
分段存储提高了批量操作的读取性能;例如,流传输数据(Cache.size、Cache.entrySet.stream)、预加载缓存和执行状态传输操作。
但是,分段存储也可以丢失写操作的性能。此性能丢失适用于可以使用事务或写成存储进行批量写入操作。因此,您应该在启用分段存储前评估写操作的开销。如果写入操作的性能丢失了大量性能,则批量读取操作的性能可能无法接受。
为缓存存储配置的片段数量必须与您使用 cluster. hash.numSegments 参数在 Data Grid 配置中定义的片段数量匹配。
如果在添加分段缓存存储后更改配置中的 numSegments 参数,则 Data Grid 无法从该缓存存储中读取数据。
参考
7.1.6. 基于文件系统的缓存存储 复制链接链接已复制到粘贴板!
在大多数情况下,基于文件系统的缓存存储适用于本地缓存存储,用于内存溢出的数据,因为它超过大小和/或时间限制。
您不应该在 NFS、Microsoft Windows 或 Samba 共享等共享文件系统上使用基于文件系统的缓存存储。共享文件系统不提供文件锁定功能,这可能导致数据崩溃。
同样,共享文件系统不是事务性的。如果您试图将事务缓存与共享文件系统搭配使用,则在提交阶段写入文件时可能会出现无法恢复的故障。
7.1.7. 直写 复制链接链接已复制到粘贴板!
直写是一个缓存写入模式,写入内存和写入缓存存储是同步的。当客户端应用程序更新缓存条目时,在大多数情况下,Data Grid 不会返回调用,直到它更新缓存存储为止。这个缓存写入模式会导致对缓存存储更新会在客户端线程的界限内进行。
Write-Through 模式的主要优点是,缓存和缓存存储同时更新,这样可确保缓存存储始终与缓存一致。
但是,Write-Through 模式可能会降低性能,因为需要访问和更新缓存存储会直接为缓存操作添加延迟。
Data Grid 默认为 Write-Through 模式,除非您在缓存存储上明确配置 Write-Behind 模式。
直写配置
<persistence passivation="false">
<file-store fetch-state="true"
read-only="false"
purge="false" path="${java.io.tmpdir}"/>
</persistence>
<persistence passivation="false">
<file-store fetch-state="true"
read-only="false"
purge="false" path="${java.io.tmpdir}"/>
</persistence>
参考
7.1.8. write-Behind 复制链接链接已复制到粘贴板!
write-Behind 是一个缓存写入模式,其中写入内存是同步的,写入缓存存储是异步的。
当客户端发送写入请求时,Data Grid 会将这些操作添加到修改队列中。在数据网格加入队列时处理操作,以便调用线程不会被阻止,操作会立即完成。
如果修改队列中的写入操作数量超过队列的大小,则数据网格会将这些额外的操作添加到队列中。但是,这些操作不会完成,直到已在队列中的 Data Grid 进程操作为止。
例如,调用 Cache.putAsync 立即返回,如果修改队列未满,则 Stage 也会立即完成。如果修改队列已满,或者 Data Grid 当前正在处理批处理写操作,则 Cache.putAsync 会立即返回,并且 Stage 稍后完成。
write-Behind 模式提供与 Write-Through 模式相比的性能优势,因为缓存操作不需要等待更新底层缓存存储完成。但是,在处理修改队列前,缓存存储中的数据与缓存中的数据不一致。因此,Write-Behind 模式适合具有低延迟的缓存存储,如不共享和本地基于文件系统的缓存存储,其中写入缓存和写入缓存存储之间的时间尽可能小。
write-behind 配置
前面的配置示例使用 fail-silently 参数来控制缓存存储不可用时或修改队列已满时发生的情况。
-
如果
fail-silently="true",则 Data Grid 会记录 WARN 消息并拒绝写操作。 如果
fail-silently="false",如果数据网格在写入操作过程中检测到缓存存储不可用,则数据网格会抛出异常。同样,如果修改队列已满,Data Grid 会抛出异常。在某些情况下,如果修改队列中存在 Data Grid 重启和写操作,则数据丢失可能会发生。例如,缓存存储离线,但在检测缓存存储不可用时,写入操作会添加到修改队列中,因为它未满。如果 Data Grid 在缓存存储恢复在线前重启或者其他不可用,则修改队列中的写入操作将会丢失,因为它们没有保留。
参考
7.2. 缓存存储实施 复制链接链接已复制到粘贴板!
Data Grid 提供几个您可以使用的缓存存储实现。或者,您可以提供自定义缓存存储。
7.2.1. Cluster Cache Loaders 复制链接链接已复制到粘贴板!
ClusterCacheLoader 从其他 Data Grid 集群成员检索数据,但不保留数据。换句话说,ClusterCacheLoader 不是缓存存储。
ClusterCacheLoader 提供了一个非阻塞部分替代状态传输。如果本地节点上没有这些密钥,则 ClusterCacheLoader 会从其他节点获取密钥,这与 lazily 加载缓存内容类似。
下图也适用于 ClusterCacheLoader :
-
预加载不会生效(
preload=true)。 -
不支持获取持久性状态(
fetch-state=true)。 - 不支持分段。
ClusterLoader 已被弃用,并将在以后的发行版本中删除。
声明性配置
<persistence> <cluster-loader remote-timeout="500"/> </persistence>
<persistence>
<cluster-loader remote-timeout="500"/>
</persistence>
编程配置
ConfigurationBuilder b = new ConfigurationBuilder();
b.persistence()
.addClusterLoader()
.remoteCallTimeout(500);
ConfigurationBuilder b = new ConfigurationBuilder();
b.persistence()
.addClusterLoader()
.remoteCallTimeout(500);
7.2.2. 单个文件缓存存储 复制链接链接已复制到粘贴板!
单个文件缓存存储( SingleFileStore )将数据持久保留到文件。Data Grid 还维护一个键内存中索引,而键和值存储在 文件中。默认情况下,单文件缓存存储被分段,这意味着 Data Grid 为每个网段创建单独的文件。
因为 SingleFileStore 保留了键和值位置的内存索引,所以它需要额外的内存,具体取决于密钥大小和键数。因此,对于密钥大的用例,不建议使用 SingleFileStore。
在某些情况下,SingleFileStore 也可以变得碎片。如果值的大小持续增加,则不会使用单个文件中的可用空间,但条目会附加到文件的末尾。只有在条目可以容纳它时,才会使用文件中的可用空间。同样,如果您从内存中删除所有条目,则单个文件存储的大小不会减小,或者被碎片整理。
声明性配置
<persistence> <file-store max-entries="5000"/> </persistence>
<persistence>
<file-store max-entries="5000"/>
</persistence>
编程配置
- 对于嵌入式部署,请执行以下操作:
ConfigurationBuilder b = new ConfigurationBuilder();
b.persistence()
.addSingleFileStore()
.maxEntries(5000);
ConfigurationBuilder b = new ConfigurationBuilder();
b.persistence()
.addSingleFileStore()
.maxEntries(5000);
- 对于服务器部署,请执行以下操作:
分段
单个文件缓存支持分段,并为每个片段创建一个单独的实例,这会在您配置的路径中生成多个目录。每个目录都是代表数据映射的网段的数字。
7.2.3. 基于 JDBC 字符串的缓存存储 复制链接链接已复制到粘贴板!
基于 JDBC 字符串的缓存存储 JdbcStringBasedStore,使用 JDBC 驱动程序在底层数据库中加载和存储值。
JdbcStringBasedStore 将每个条目存储在表中自己的行中,以提高并发负载的吞吐量。JdbcStringBasedStore 还使用一个简单的一对一映射,它使用 key-to-string-mapper 接口将每个键映射到 String 对象。
Data Grid 提供了一个默认的实现,即 DefaultTwoWayKey2StringMapper,它处理原语类型。
默认情况下,Data Grid 共享不会被存储,这意味着集群中的所有节点都写入到每个更新上的底层存储。如果只希望操作一次写入底层数据库,您必须将 JDBC 存储配置为共享。
分段
JdbcStringBasedStore 默认使用分段,并且需要数据库表中的列来代表条目所属的网段。
7.2.3.1. connection Factories 复制链接链接已复制到粘贴板!
JdbcStringBasedStore 依赖于 ConnectionFactory 实现来连接数据库。
Data Grid 提供以下 ConnectionFactory 实现:
PooledConnectionFactoryConfigurationBuilder
基于通过 PooledConnectionFactoryConfiguration 配置的 Agroal 的连接工厂。
或者,您可以指定前缀为 org.infinispan.agroal. 的配置属性,如下例所示:
然后,您可以通过 PooledConnectionFactoryConfiguration.propertyFile 将 Data Grid 配置为使用您的属性文件。
您应该将 PooledConnectionFactory 用于独立部署,而不是 servlet 容器中的部署。
ManagedConnectionFactoryConfigurationBuilder
可与受管环境(如应用服务器)一起使用的连接工厂。此连接工厂可以探索 JNDI 树中的可配置位置,并将连接管理委派给 DataSource。
SimpleConnectionFactoryConfigurationBuilder
一个连接工厂,用于在每个调用时创建数据库连接。您应该只对测试或开发环境使用此连接工厂。
7.2.3.2. 基于 JDBC 字符串的缓存存储配置 复制链接链接已复制到粘贴板!
您可以以编程方式或声明性地配置 JdbcStringBasedStore。
声明性配置
-
使用
PooledConnectionFactory
-
使用
ManagedConnectionFactory
编程配置
-
使用
PooledConnectionFactory
-
使用
ManagedConnectionFactory
7.2.4. JPA Cache Stores 复制链接链接已复制到粘贴板!
JPA (Java Persistence API)缓存存储、JpaStore、使用正式模式来持久保留数据。然后,其他应用程序可以从持久性存储中读取,以从 Data Grid 加载数据。但是,其他应用程序不应与 Data Grid 同时使用持久性存储。
使用 JpaStore 时,您应该考虑以下问题:
- 密钥应该是实体的 ID。值应该是实体对象。
-
只允许单个
@Id或@EmbeddedId注释。 -
不支持使用
@GeneratedValue注释自动生成的 ID。 - 所有条目都存储为 immortal。
-
JpaStore不支持分段。
声明性配置
| 参数 | 描述 |
|---|---|
|
|
指定 JPA 配置文件 |
|
| 指定应该存储在此缓存中的完全限定域名。只允许一个类。 |
编程配置
Configuration cacheConfig = new ConfigurationBuilder().persistence()
.addStore(JpaStoreConfigurationBuilder.class)
.persistenceUnitName("org.infinispan.loaders.jpa.configurationTest")
.entityClass(User.class)
.build();
Configuration cacheConfig = new ConfigurationBuilder().persistence()
.addStore(JpaStoreConfigurationBuilder.class)
.persistenceUnitName("org.infinispan.loaders.jpa.configurationTest")
.entityClass(User.class)
.build();
| 参数 | 描述 |
|---|---|
|
|
指定 JPA 配置文件 |
|
| 指定应该存储在此缓存中的完全限定域名。只允许一个类。 |
7.2.4.1. JPA Cache Store Usage Example 复制链接链接已复制到粘贴板!
本节提供了使用 JPA 缓存存储的示例。
先决条件
-
配置数据网格以汇总您的 JPA 实体。默认情况下,Data Grid 使用 ProtoStream 进行 marshalling Java 对象。要 marshall JPA 实体,您必须创建一个
SerializationContextInitializer实现,该实现使用SerializationContext注册.proto模式和 marshaller。
流程
在
persistence.xml中定义持久性单元 "my PersistenceUnit"。<persistence-unit name="myPersistenceUnit"> ... </persistence-unit>
<persistence-unit name="myPersistenceUnit"> ... </persistence-unit>Copy to Clipboard Copied! Toggle word wrap Toggle overflow 创建用户实体类。
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 使用 JPA 缓存存储配置名为"usersCache"的缓存。
然后,您可以将缓存"usersCache"配置为使用 JPA Cache Store,以便在将数据放入缓存中时,数据将基于 JPA 配置保留到数据库中。
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 使用 JPA 缓存存储的缓存只能存储一种类型的数据,如下例所示:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow @EmbeddedId注释允许您使用复合键,如下例所示:Copy to Clipboard Copied! Toggle word wrap Toggle overflow
7.2.5. 远程缓存存储 复制链接链接已复制到粘贴板!
远程缓存存储 RemoteStore,使用 Hot Rod 协议在 Data Grid 集群中存储数据。
以下是一个 RemoteStore 配置示例,它将数据存储在两个 Data Grid Server 实例(名为 "one" 和 "two" 的缓存中:
如果您将远程缓存存储配置为共享,则无法预加载数据。换句话说,如果您的配置中是 shared="true",则必须设置 preload="false"。
声明性配置
编程配置
分段
RemoteStore 支持分段,并且可以按网段发布密钥和条目,从而提高批量操作的效率。但是,只有 Data Grid Hot Rod 协议版本 2.3 或更高版本才提供分段。
当您为 RemoteStore 启用分段时,它会使用您在 Data Grid 服务器配置中定义的片段数量。
如果源缓存被分段并使用与 RemoteStore 不同的片段数,则返回不正确的值用于批量操作。在这种情况下,您应该禁用 RemoteStore 的分段。
7.2.6. RocksDB Cache Stores 复制链接链接已复制到粘贴板!
RocksDB 为高并发环境提供具有高性能和可靠性的基于键值的文件系统存储。
RocksDB 缓存存储( RocksDBStore )使用两个数据库。一个数据库为内存中的数据提供主要缓存存储;其他数据库包含 Data Grid 从内存中过期的条目。
声明性配置
编程配置
| 参数 | 描述 |
|---|---|
|
| 指定提供主缓存存储的 RocksDB 数据库的路径。如果您没有设置位置,则会自动创建它。请注意,路径必须相对于全局持久位置。 |
|
| 指定用于为过期数据提供缓存存储的 RocksDB 数据库的路径。如果您没有设置位置,则会自动创建它。请注意,路径必须相对于全局持久位置。 |
|
| 为过期条目设置内存中队列的大小。当队列达到大小时,Data Grid flushes 已过期到 RocksDB 缓存存储中。 |
|
| 在删除和重新初始化(re-init) RocksDB 数据库前设置最大条目数。对于较小的缓存存储,迭代所有条目并删除每个条目可以更快速的方法。 |
RocksDB 调优参数
您还可以指定以下 RocksDB 调优参数:
-
compressionType -
blockSize -
cacheSize
RocksDB 配置属性
另外,还可在配置中设置属性,如下所示:
-
带有
数据库的前缀属性,用于调整和调优 RocksDB 数据库。 -
为
数据添加前缀属性,以配置 RocksDB 存储数据的列系列。
<property name="database.max_background_compactions">2</property> <property name="data.write_buffer_size">64MB</property> <property name="data.compression_per_level">kNoCompression:kNoCompression:kNoCompression:kSnappyCompression:kZSTD:kZSTD</property>
<property name="database.max_background_compactions">2</property>
<property name="data.write_buffer_size">64MB</property>
<property name="data.compression_per_level">kNoCompression:kNoCompression:kNoCompression:kSnappyCompression:kZSTD:kZSTD</property>
分段
RocksDBStore 支持分段,并为每个网段创建单独的列系列。分段 RocksDB 缓存存储提高了查找性能和迭代,但写入操作的性能略低。
您不应该配置超过几百个片段。RocksDB 没有设计为无数列系列。太多片段也会显著增加缓存存储启动时间。
7.2.7. soft-Index 文件存储 复制链接链接已复制到粘贴板!
Soft-Index 文件缓存存储( SoftIndexFileStore )提供基于文件的本地存储。
SoftIndexFileStore 是一个 Java 实现,它使用使用 Java 软引用缓存内存的 B+ Tree 变体。B+ Tree,名为 Index,将文件系统上的一个文件卸载到单个文件,每次缓存存储重启时都会进行重新构建。
SoftIndexFileStore 将数据存储在一组文件中,而不是单个文件中。当任何文件使用量低于 50% 时,文件中的条目会被覆盖给另一个文件,然后删除该文件。
SoftIndexFileStore 在一组使用附加方法写入的文件中保留数据。因此,如果您在传统的 magnetic 磁盘中使用 SoftIndexFileStore,则不需要在编写突发条目时看到它。
SoftIndexFileStore 中的大多数结构都绑定了,因此内存不足异常不会带来风险。您还可以为同时打开文件配置限制。
默认情况下,索引中节点的大小限制为 4096 字节。这个大小还限制密钥长度;更精确地限制序列化密钥的长度。因此,您无法使用超过节点大小的密钥 15 字节。另外,密钥长度存储为 "short",这会将密钥长度限制为 32767 字节。如果键在进行序列化后较长,SoftIndexFileStore 会抛出异常。
SoftIndexFileStore 无法检测到过期的条目,这可能导致过度使用文件系统中的空间。
AdvancedStore.purgeExpired () 没有在 SoftIndexFileStore 中实施。
声明性配置
编程配置
ConfigurationBuilder b = new ConfigurationBuilder();
b.persistence()
.addStore(SoftIndexFileStoreConfigurationBuilder.class)
.indexLocation("testCache/index");
.dataLocation("testCache/data")
ConfigurationBuilder b = new ConfigurationBuilder();
b.persistence()
.addStore(SoftIndexFileStoreConfigurationBuilder.class)
.indexLocation("testCache/index");
.dataLocation("testCache/data")
分段
soft-Index 文件缓存存储支持分段,并为每个片段创建一个单独的实例,这会导致您配置的路径中的多个目录。每个目录都是代表数据映射的网段的数字。
7.2.8. 实施自定义缓存存储 复制链接链接已复制到粘贴板!
您可以通过 Data Grid persistent SPI 创建自定义缓存存储。
7.2.8.1. Data Grid Persistence SPI 复制链接链接已复制到粘贴板!
Data Grid Service Provider Interface (SPI)通过 NonBlockingStore 接口为外部存储启用读取和写入操作,并具有以下功能:
- JCache 兼容供应商的可移植性
-
Data Grid 使用处理阻塞代码的适配器维护
NonBlockingStore接口和JSR-107JCache 规格之间的兼容性。 - 简化的事务集成
- Data Grid 会自动处理锁定,因此您的实施不需要协调对持久性存储的并发访问。根据您使用的锁定模式,通常不会发生对同一密钥的并发写入。但是,您应该预期持久性存储上的操作源自多个线程,并创建实施来容许此行为。
- 并行迭代
- 借助 Data Grid,您可以并行迭代具有多个线程的持久性存储中的条目。
- 减少序列化结果,从而减少 CPU 用量
- Data Grid 以序列化格式公开存储的条目,可以远程传输。因此,Data Grid 不需要从持久性存储检索的反序列化条目,然后在写入有线时再次序列化。
7.2.8.2. 创建缓存存储 复制链接链接已复制到粘贴板!
通过实施 NonBlockingStore 接口创建自定义缓存存储。
- 实施适当的 Data Grid 持久 SPI。
-
如果有自定义配置,给存储类标上
@ConfiguredBy注释。 如果需要,创建自定义缓存存储配置和构建器。
-
扩展
AbstractStoreConfiguration和AbstractStoreConfigurationBuilder. (可选)在存储配置类中添加以下注解,以确保您的自定义配置构建器从 XML 解析您的缓存存储配置:
-
@ConfigurationFor @BuiltBy如果没有添加这些注解,则
CustomStoreConfigurationBuilder会解析AbstractStoreConfiguration中定义的通用存储属性,并且忽略任何其他元素。注意如果配置没有声明
@ConfigurationFor注释,则 Data Grid 初始化缓存时会记录警告消息。
-
-
扩展
7.2.8.3. 配置数据网格以使用自定义存储 复制链接链接已复制到粘贴板!
创建自定义缓存存储实施后,将 Data Grid 配置为使用它。
声明性配置
<local-cache name="customStoreExample">
<persistence>
<store class="org.infinispan.persistence.dummy.DummyInMemoryStore" />
</persistence>
</local-cache>
<local-cache name="customStoreExample">
<persistence>
<store class="org.infinispan.persistence.dummy.DummyInMemoryStore" />
</persistence>
</local-cache>
编程配置
Configuration config = new ConfigurationBuilder()
.persistence()
.addStore(CustomStoreConfigurationBuilder.class)
.build();
Configuration config = new ConfigurationBuilder()
.persistence()
.addStore(CustomStoreConfigurationBuilder.class)
.build();
7.2.8.4. 部署自定义缓存存储 复制链接链接已复制到粘贴板!
您可以将自定义缓存打包到 JAR 文件中,并将它们部署到 Data Grid 服务器中,如下所示:
- 将自定义缓存存储实施打包在 JAR 文件中。
-
将您的 JAR 文件添加到 Data Grid 服务器的
server/lib目录中。
7.3. 在缓存存储之间迁移 复制链接链接已复制到粘贴板!
Data Grid 提供了一个实用程序,用于将数据从一个缓存存储迁移到另一个缓存存储。
7.3.1. 缓存存储 Migrator 复制链接链接已复制到粘贴板!
Data Grid 提供 StoreMigrator.java 工具,它为最新的数据网格缓存存储实施重新创建数据。
StoreMigrator 从以前的 Data Grid 版本获取缓存存储作为源,并使用缓存存储实施作为目标。
运行 StoreMigrator 时,它会使用您使用 EmbeddedCacheManager 接口定义的缓存存储类型创建目标缓存。StoreMigrator 然后从源存储加载条目到内存中,然后将其放入目标缓存中。
StoreMigrator 还允许您将数据从一种类型的缓存存储迁移到另一种缓存存储。例如,您可以从基于 JDBC 字符串的缓存存储迁移到单文件缓存存储。
StoreMigrator 无法将数据从分段缓存存储迁移到:
- 非分段缓存存储。
- 分段缓存存储,它们具有不同的片段数。
7.3.2. 获取 Store Migrator 复制链接链接已复制到粘贴板!
StoreMigrator 作为 Data Grid 工具库 infinispan-tools 的一部分,并包含在 Maven 存储库中。
流程
为
StoreMigrator配置pom.xml,如下所示:Copy to Clipboard Copied! Toggle word wrap Toggle overflow
7.3.3. 配置 Store Migrator 复制链接链接已复制到粘贴板!
在 migrator.properties 文件中为源和目标缓存存储设置属性。
流程
-
创建
migrator.properties文件。 在
migrator.properties中配置源缓存存储。使用
source预先填充所有配置属性,如下例所示:source.type=SOFT_INDEX_FILE_STORE source.cache_name=myCache source.location=/path/to/source/sifs
source.type=SOFT_INDEX_FILE_STORE source.cache_name=myCache source.location=/path/to/source/sifsCopy to Clipboard Copied! Toggle word wrap Toggle overflow
在
migrator.properties中配置目标缓存存储。使用
target预先填充所有配置属性,如下例所示:target.type=SINGLE_FILE_STORE target.cache_name=myCache target.location=/path/to/target/sfs.dat
target.type=SINGLE_FILE_STORE target.cache_name=myCache target.location=/path/to/target/sfs.datCopy to Clipboard Copied! Toggle word wrap Toggle overflow
7.3.3.1. 存储 Migrator 属性 复制链接链接已复制到粘贴板!
在 StoreMigrator 属性中配置源和目标缓存存储。
| 属性 | 描述 | 必填/选填 |
|---|---|---|
|
| 指定源或目标的缓存存储类型类型。
| 必填 |
| 属性 | 描述 | 值示例 | 必填/选填 |
|---|---|---|---|
|
| 命名存储后端的缓存。 |
| 必填 |
|
| 指定可以使用分段的目标缓存存储的片段数量。
片段数量必须与 Data Grid 配置中的 换句话说,缓存存储的片段数量必须与对应缓存的片段数匹配。如果片段数量不相同,则数据网格无法从缓存存储中读取数据。 |
| 选填 |
| 属性 | 描述 | 必填/选填 |
|---|---|---|
|
| 指定底层数据库的 dialect。 | 必填 |
|
| 指定源缓存存储的 marshaller 版本。设置以下值之一:
*
*
* | 仅用于源存储。
例如: |
|
| 指定自定义 marshaller 类。 | 使用自定义 marshallers 时需要此项。 |
|
|
指定以逗号分隔的自定义 | 选填 |
|
| 指定 JDBC 连接 URL。 | 必填 |
|
| 指定 JDBC 驱动程序的类。 | 必填 |
|
| 指定数据库用户名。 | 必填 |
|
| 指定数据库用户名的密码。 | 必填 |
|
| 设置数据库主版本。 | 选填 |
|
| 设置数据库次版本。 | 选填 |
|
| 禁用数据库 upsert。 | 选填 |
|
| 指定是否创建表索引。 | 选填 |
|
| 指定表名称的额外前缀。 | 选填 |
|
| 指定列名称。 | 必填 |
|
| 指定列类型。 | 必填 |
|
|
指定 | 选填 |
要从旧的 Data Grid 版本中迁移 Binary 缓存存储,请在以下属性中将 table.string view 改为 table.binary.\* :
-
source.table.binary.table_name_prefix -
source.table.binary.<id\|data\|timestamp>.name -
source.table.binary.<id\|data\|timestamp>.type
| 属性 | 描述 | 必填/选填 |
|---|---|---|
|
| 设置数据库目录。 | 必填 |
|
| 指定要使用的压缩类型。 | 选填 |
Example configuration for migrating from a RocksDB cache store.
# Example configuration for migrating from a RocksDB cache store.
source.type=ROCKSDB
source.cache_name=myCache
source.location=/path/to/rocksdb/database
source.compression=SNAPPY
| 属性 | 描述 | 必填/选填 |
|---|---|---|
|
|
设置包含缓存存储 | 必填 |
Example configuration for migrating to a Single File cache store.
# Example configuration for migrating to a Single File cache store.
target.type=SINGLE_FILE_STORE
target.cache_name=myCache
target.location=/path/to/sfs.dat
| 属性 | 描述 | 值 |
|---|---|---|
| 必填/选填 |
| 设置数据库目录。 |
| 必填 |
| 设置数据库索引目录。 |
Example configuration for migrating to a Soft-Index File cache store.
# Example configuration for migrating to a Soft-Index File cache store.
target.type=SOFT_INDEX_FILE_STORE
target.cache_name=myCache
target.location=path/to/sifs/database
target.location=path/to/sifs/index
7.3.4. 迁移缓存存储 复制链接链接已复制到粘贴板!
运行 StoreMigrator 将数据从一个缓存存储迁移到另一个缓存存储。
先决条件
-
获取
infinispan-tools.jar. -
创建
migrator.properties文件,以配置源和目标缓存存储。
流程
如果从源构建
infinispan-tools.jar,请执行以下操作:-
将源和目标数据库(如 JDBC 驱动程序)的
infinispan-tools.jar和依赖项添加到您的类路径。 -
将
migrator.properties文件指定为StoreMigrator的参数。
-
将源和目标数据库(如 JDBC 驱动程序)的
如果您从 Maven 存储库拉取
infinispan-tools.jar,请运行以下命令:mvn exec:java
第 8 章 设置分区处理 复制链接链接已复制到粘贴板!
8.1. 分区处理 复制链接链接已复制到粘贴板!
Data Grid 集群基于存储数据的多个节点进行构建。为了在出现节点故障时不会丢失数据,Data Grid 在 Data Grid parlancemvapich-osgiover 多个节点中复制相同的 data iwl-setuptoolscache 条目。这种数据冗余级别通过 numOwners 配置属性配置,并确保只要同时超过 numOwners 节点崩溃,Data Grid 具有可用数据的副本。
但是,可能会出现灾难性情况,在这种情况下,超过 numOwners 节点会从集群中消失:
- 脑裂
- 这会导致路由器崩溃,这会将集群分成两个或者多个分区,或者独立操作的子集群。在这些情况下,多个从不同分区读取/写入的客户端可以看到同一缓存条目的不同版本,对于许多应用程序来说,这个版本有问题。请注意,有方法可以缓解发生脑裂的可能性,如冗余网络或 IP 绑定。它们只减少问题发生的时间窗口。
numOwners节点按顺序崩溃-
当至少有
numOwners节点在快速连续崩溃时,Data Grid 没有时间来在崩溃之间正确重新平衡其状态,则结果会部分数据丢失。
本节中讨论的分区处理功能允许用户配置在出现脑裂时可在缓存上执行哪些操作。Data Grid 提供多个分区处理策略,该策略在 Brewer 的 CAP The orem CAP theorem 决定在存在分区时是否牺牲可用性或一致性。以下是所提供的策略列表:
| 策略 | 描述 | CAP |
|---|---|---|
| DENY_READ_WRITES | 如果分区没有给定段的所有所有者,则针对该网段中的所有键,读取和写入都将被拒绝。 | 一致性 |
| ALLOW_READS | 如果这个分区中存在给定密钥,则允许读取它,但仅在此分区包含网段的所有所有者时才允许写入。这仍然是一种一致的方法,因为某些条目在此分区中可用,但从客户端应用程序的角度来看,它不是确定的。 | 一致性 |
| ALLOW_READ_WRITES | 允许每个分区中的条目被分离,在分区合并时尝试冲突解析。 | 可用性 |
应用的要求应确定适合哪些策略。例如,DENY_READ_WRITES 更适合具有高一致性要求的应用程序;例如,从系统读取的数据必须准确时。虽然如果将 Data Grid 用作最佳缓存,分区可能非常可容忍,并且 ALLOW_READ_WRITES 可能更适合,因为它通过一致性来实现可用性。
以下小节描述了 Data Grid 如何为每个分区处理策略处理 脑裂 和 连续故障。这后接一个部分,说明 Data Grid 如何通过合并 策略 在分区合并时允许自动冲突解决。最后,我们提供了一个 描述如何配置分区处理策略和合并策略的部分。
8.1.1. 脑裂 复制链接链接已复制到粘贴板!
在脑裂情形中,每个网络分区将安装自己的 JGroups 视图,从其他分区中删除节点。我们没有一种直接的方法来确定是否被分成两个或者多个分区,因为分区不能相互识别。相反,我们假设,当一个或多个节点从 JGroups 集群消失时,我们假设集群被分割,而无需发送显式保留消息。
8.1.1.1. 分割策略 复制链接链接已复制到粘贴板!
在本节中,我们将详细介绍每个分区处理策略在发生脑裂时的行为。
8.1.1.1.1. ALLOW_READ_WRITES 复制链接链接已复制到粘贴板!
每个分区继续作为独立集群运行,所有分区都保留在 AVAILABLE 模式中。这意味着每个分区只能看到数据的一部分,每个分区都可以在缓存中写入冲突的更新。在分区合并过程中,利用 ConflictManager 和配置的 EntryMergePolicy 自动解决这些冲突。
8.1.1.1.2. DENY_READ_WRITES 复制链接链接已复制到粘贴板!
当检测到分割时,每个分区不会立即开始重新平衡,而是首先检查它是否应该进入 DEGRADED 模式:
- 如果至少有一个片段丢失了其所有所有者(至少在最后一次重新平衡以来的 numOwners 节点保留),则分区进入 DEGRADED 模式。
- 如果分区没有在 最新的稳定拓扑中 包含简单的大多数节点(floor (numNodes/2)+ 1),分区还会进入 DEGRADED 模式。
- 否则,分区会保持正常运行,它会开始重新平衡。
每次重新平衡操作时都会更新 stable 拓扑,协调器决定另一个重新平衡不需要。
这些规则确保最多一个分区处于 AVAILABLE 模式,其他分区进入 DEGRADED 模式。
当分区处于 DEGRADED 模式时,它只允许访问所拥有的密钥:
- 对于在这个分区内节点上所有副本的条目,请求(读和写)是隐藏的。
-
对部分或完全归消失的节点拥有的条目的请求将被拒绝,并显示
AvailabilityException。
这样可保证分区不能为同一键(缓存是一致的)写入不同的值,并且一个分区无法读取在其他分区中更新的密钥(没有过时的数据)。
要说明,请考虑初始集群 M = {A, B, C, D},使用 numOwners = 2 配置。此外,请考虑三个键 k1、k2 和 k3 (可能存在于缓存中或不存在),例如 owners (k1)= {A,B}, owners (k2)= {B,C} 和 owners (k3)= {C,D}.然后,两个分区中的网络分割: N1 = {A, B} 和 N2 = {C, D},它们进入 DEGRADED 模式并的行为如下:
-
在
N1上,k1可用于读/写、k2(完全拥有)和k3(非拥有)不可用,并且访问它们的结果是AvailabilityException -
在
N2上,k1和k2不适用于读/写,k3可用
分区处理流程的相关方面是,当脑裂发生时,生成的分区依赖于原始片段映射(在脑裂之前存在的分区)来计算密钥所有权。因此,如果 k1、 或 k 2k3 已存在缓存或者其可用性不同,则这并不重要。
如果稍后,如果网络修复和 N1 和 N2 分区合并回初始集群 M,则 M 会退出降级模式,然后再次可用。在这个合并操作中,因为 M 已再次变为可用,所以 ConflictManager 和配置的 EntryMergePolicy 会被用来检查在脑裂发生和被检测之间可能发生的任何冲突。
再如,集群可以在两个分区 O1 中分割 = {A, B, C} 和 O2 = {D} = {D},分区 O1 将保持完全可用(在剩余成员上重新平衡缓存条目)。但是,分区 O2 将检测到分割并进入降级模式。由于它没有任何完全拥有的密钥,因此它将拒绝任何具有 AvailabilityException 的读取或写入操作。
如果随后将 O1 和 O2 合并合并回 M,则 ConflictManager 会尝试解决任何冲突,D 会再次变为完全可用。
8.1.1.1.3. ALLOW_READS 复制链接链接已复制到粘贴板!
分区以与 DENY_READ_WRITES 相同的方式处理,除非当分区处于 DEGRADED 模式时,对部分拥有的密钥 WILL 不会抛出 AvailabilityException。
8.1.1.2. 当前限制 复制链接链接已复制到粘贴板!
两个分区可以启动隔离,只要它们无法合并,就可以读取和写入不一致的数据。未来,我们将允许自定义可用性策略(例如,检查某个节点是否是集群的一部分),或者检查是否可以访问外部计算机,从而可以处理该情况。
8.1.2. 成功节点停止 复制链接链接已复制到粘贴板!
如上一节所述,Data Grid 无法检测节点是否因为进程/机器崩溃而离开 JGroups 视图,或者由于网络故障:每当节点离开 JGroups 集群时,它假定是因为网络问题而离开的。
如果配置的副本数(numOwners)大于 1,集群可以保持可用,并将尝试在崩溃的节点上生成新数据副本。但是,其他节点可能会在重新平衡过程中崩溃。如果超过 numOwners 节点在较短的时间内崩溃,则一些缓存条目可能会完全从集群消失。在这种情况下,在启用了 DENY_READ_WRITES 或 ALLOW_READS 策略的情况下,Data Grid 假设(正确)有脑裂并进入 DEGRADED 模式,如 split-brain 部分所述。
管理员也可以快速关闭多个 numOwners 节点,从而导致只存储在这些节点上的数据丢失。当管理员正常关闭节点时,Data Grid 知道节点无法返回。但是,如果这些节点崩溃,集群不会跟踪每个节点的保留方式,缓存仍然进入 DEGRADED 模式。
在这个阶段,集群无法恢复其状态,除了在使用外部源的数据重启时停止和禁止它。集群应该配置有适当的 numOwners,以避免 numOwners 成功节点失败,因此这种情况应该非常罕见。如果应用程序可以在缓存中处理一些数据,管理员可以通过 JMX 将可用性模式强制返回到 AVAILABLE。
8.1.3. 冲突管理器 复制链接链接已复制到粘贴板!
冲突管理器是一个允许用户检索给定键的所有存储副本值的工具。除了允许用户处理存储副本有冲突值的缓存条目的流外。另外,通过利用 EntryMergePolicy 接口的实现,可以自动解析冲突。
8.1.3.1. 检测冲突 复制链接链接已复制到粘贴板!
通过检索给定键的每个存储值来检测到冲突。冲突管理器从当前一致的哈希定义的每个键的写入所有者中检索值。然后,使用 .equals 方法来确定所有值是否相等。如果所有值都相等,则键没有冲突,否则会发生冲突。请注意,如果给定节点上没有条目,则返回 null 值,因此如果给定键存在 null 和 non-null 值,则会导致冲突。
8.1.3.2. 合并策略 复制链接链接已复制到粘贴板!
如果给定 CacheEntry 的一个或多个副本之间出现冲突,则需要定义冲突解析算法,因此我们提供 EntryMergePolicy 接口。这个接口由单个方法"merge"组成,返回的 CacheEntry 被耗尽为给定键的"resolved"条目。当返回非null CacheEntry 时,此条目值为"put"到缓存中的所有副本。但是,当合并实现返回 null 值时,与冲突键关联的所有副本都会从缓存中移除。
合并方法采用两个参数:"preferredEntry"和"otherEntries"。在分区合并的上下文中,preferredEntry 是存储在分区中的 CacheEntry 的主副本,其中包含最多节点,或者如果分区等于具有最大 topologyId 的分区。如果重叠的分区(例如,节点 A 存在于两个分区 {A}, {A,B,C})的拓扑中,我们选择了 {A} 作为首选分区,因为它将具有更高的 topologId,因为其他分区的拓扑位于其他分区的拓扑后面。如果没有发生分区合并,"preferredEntry"只是 CacheEntry 的主副本。第二个参数 "otherEntries" 只是与检测到冲突的密钥相关联的所有其他条目的列表。
只有在检测到冲突时,才会调用 EntryMergePolicy::merge,如果所有 CacheEntrys 都相同,则不会调用它。
目前,Data Grid 提供以下 EntryMergePolicy 实现:
| policy | 描述 |
|---|---|
| MergePolicy.NONE (默认) | 不会尝试解决冲突。在次版本分区上托管的条目会被删除,且此分区中的节点不会保存任何数据,直到重新平衡启动为止。请注意,这个行为等同于不支持冲突解析的 Infinispan 版本。请注意,在这种情况下,对在次版本分区上托管的条目进行的所有更改都会丢失,但在重新平衡完成后,所有条目都将一致。 |
| MergePolicy.PREFERRED_ALWAYS | 始终使用 "preferredEntry"。MergePolicy.NONE 几乎相当于 PREFERRED_ALWAYS,没有执行冲突解析的性能影响,因此应选择 MergePolicy.NONE,除非以下场景是问题。当使用 DENY_READ_WRITES 或 DENY_READ 策略时,写入操作只能在分区进入 DEGRADED 模式时部分完成,从而导致包含不一致值的副本。MergePolicy.PREFERRED_ALWAYS 将检测到不一致并解决,而 MergePolicy.NONE 在集群重新平衡后,CacheEntry 副本将保持不一致的情况。 |
| MergePolicy.PREFERRED_NON_NULL | 使用"preferredEntry" (如果不是 null),否则会使用 "otherEntries" 的第一个条目。 |
| MergePolicy.REMOVE_ALL | 当检测到冲突时,始终从缓存中删除密钥。 |
| 完全限定类名称 | 合并的自定义实现将使用 自定义合并策略 |
8.1.4. 使用方法 复制链接链接已复制到粘贴板!
在分区合并过程中,ConflictManager 会自动尝试解决配置的 EntryMergePolicy 冲突,但也可以手动搜索应用程序所需的/解析冲突。
以下代码演示了如何检索 EmbeddedCacheManager 的 ConflictManager,如何检索给定密钥的所有版本以及如何检查给定缓存中的冲突。
虽然每个条目都处理 ConflictManager::getConflicts 流,但底层分割器器位于每个片段上事实上 lazily-loading 缓存条目。
8.1.5. 配置分区处理 复制链接链接已复制到粘贴板!
除非缓存是分布式或复制的,否则分区处理配置将被忽略。默认分区处理策略是 ALLOW_READ_WRITES,默认的 EntryMergePolicy 是 MergePolicies::PREFERRED_ALWAYS。
<distributed-cache name="the-default-cache"> <partition-handling when-split="ALLOW_READ_WRITES" merge-policy="PREFERRED_NON_NULL"/> </distributed-cache>
<distributed-cache name="the-default-cache">
<partition-handling when-split="ALLOW_READ_WRITES" merge-policy="PREFERRED_NON_NULL"/>
</distributed-cache>
以编程方式实现相同的:
ConfigurationBuilder dcc = new ConfigurationBuilder();
dcc.clustering().partitionHandling()
.whenSplit(PartitionHandling.ALLOW_READ_WRITES)
.mergePolicy(MergePolicy.PREFERRED_ALWAYS);
ConfigurationBuilder dcc = new ConfigurationBuilder();
dcc.clustering().partitionHandling()
.whenSplit(PartitionHandling.ALLOW_READ_WRITES)
.mergePolicy(MergePolicy.PREFERRED_ALWAYS);
8.1.5.1. 实施自定义合并策略 复制链接链接已复制到粘贴板!
也可以提供 EntryMergePolicy 的自定义实现
<distributed-cache name="the-default-cache"> <partition-handling when-split="ALLOW_READ_WRITES" merge-policy="org.example.CustomMergePolicy"/> </distributed-cache>
<distributed-cache name="the-default-cache">
<partition-handling when-split="ALLOW_READ_WRITES" merge-policy="org.example.CustomMergePolicy"/>
</distributed-cache>
ConfigurationBuilder dcc = new ConfigurationBuilder();
dcc.clustering().partitionHandling()
.whenSplit(PartitionHandling.ALLOW_READ_WRITES)
.mergePolicy(new CustomMergePolicy());
ConfigurationBuilder dcc = new ConfigurationBuilder();
dcc.clustering().partitionHandling()
.whenSplit(PartitionHandling.ALLOW_READ_WRITES)
.mergePolicy(new CustomMergePolicy());
8.1.5.2. 将自定义合并策略部署到 Infinispan 服务器实例 复制链接链接已复制到粘贴板!
要在服务器上使用自定义 EntryMergePolicy 实现,需要把实施类部署到服务器。这可以通过利用 java service-provider 惯例并将类文件打包在 jar 中,其具有 META-INF/services/org.infinispan.conflict.EntryMergePolicy 文件,其中包含 EntryMergePolicy 实施的完全限定类名称。
list all necessary implementations of EntryMergePolicy with the full qualified name
# list all necessary implementations of EntryMergePolicy with the full qualified name
org.example.CustomMergePolicy
要使自定义合并策略在服务器上使用,您应该启用对象存储,如果您的策略语义需要访问存储的 Key/Value 对象。这是因为服务器中缓存条目可能会以 marshalled 格式存储,返回到您的策略的 Key/Value 对象将是 WrappedByteArray 的实例。但是,如果自定义策略只依赖于与缓存条目关联的元数据,则不需要对象存储,应该避免(除非因其他原因而需要)因为每个请求的额外性能成本增加。最后,如果使用其中一个提供的合并策略,则无需对象存储。
8.1.6. 监控和管理 复制链接链接已复制到粘贴板!
缓存的可用性模式在 JMX 中作为 Cache MBean 的属性公开。该属性是可写的,允许管理员强制将缓存从 DEGRADED 模式强制迁移到 AVAILABLE (一致性成本)。
可用性模式也可以通过 AdvancedCache 接口访问: