做包装设计的网站,天天广告联盟官网,库存管理软件免费版app,淄博网络推广哪家好配置内存
#1.内存架构
#1.1.概述
Ignite内存架构通过可以同时在内存和磁盘上存储和处理数据及索引#xff0c;得到了支持磁盘持久化的内存级性能。 多层存储的运行方式类似于操作系统#xff08;例如Linux#xff09;的虚拟内存。但是这两种类型架构之间的主要区别是得到了支持磁盘持久化的内存级性能。 多层存储的运行方式类似于操作系统例如Linux的虚拟内存。但是这两种类型架构之间的主要区别是多层存储始终将磁盘视为数据的超集如果启用了持久化在故障或者重启后仍然可以保留数据而传统的虚拟内存仅将磁盘作为交换扩展一旦进程停止数据就会被清除。
#1.2.内存架构
多层架构是一种基于固定大小页面的内存架构这些页面存储在内存Java堆外的托管非堆区中并按磁盘上的特定层次结构进行组织。
Ignite在内存和磁盘上都维护相同的二进制数据表示形式这样在内存和磁盘之间移动数据时就不需要进行昂贵的序列化。
下图说明了多层存储架构 #1.2.1.内存段
每个数据区均以初始大小开始并具有可以增长到的最大大小。该区域通过分配连续的内存段扩展到其最大大小。
内存段是从操作系统分配的物理内存的连续字节数组该数组被拆分为固定大小的页面。该段中可以存在几种类型的页面如下图所示。 #1.2.2.数据页面
数据页面存储从应用端写入缓存的条目。
通常每个数据页面持有多个键值条目以便尽可能高效地使用内存并避免内存碎片。将新条目添加到缓存后Ignite会寻找一个适合整个键-值条目的最佳页面。
但是如果一个条目的总大小超过了DataStorageConfiguration.setPageSize(..)属性配置的页面大小则该条目将占用多个数据页面。
提示
如果有许多缓存条目无法容纳在单个页面中那么增加页面大小配置参数是有必要的。
如果在更新期间条目大小扩大并超过了其数据页面的剩余可用空间则Ignite会搜索新的空间足够的数据页面并将其移到那里。
#1.2.3.内存碎片整理
Ignite自动执行内存碎片整理不需要用户干预。
随着时间的推移每个数据页面可能会通过不同的CRUD操作多次更新这会导致页面和整体内存碎片化。为了最大程度地减少内存碎片只要页面碎片过多Ignite都会使用页面压缩。
压缩的数据页面如下图所示 页面具有一个头部其存储内部使用所需的元信息。所有键-值条目总是从右到左添加。在上图中页面中存储了三个条目分别为1、2和3。这些条目可能具有不同的大小。
页面内条目位置的偏移量或引用从左到右存储并且始终为固定大小。偏移量用于在页面中查找键-值条目的指针。
中间的空间是可用空间每当将更多数据推入集群时该空间就会被填充。
接下来假设随着时间的推移条目2被删除这导致页面中的非连续可用空间 这就是碎片化页面的样子。
但是当需要页面的整个可用空间或达到某个碎片阈值时压缩过程会对页面进行碎片整理使其变为上面第一张图片中所示的状态其中该连续空间是连续的。此过程是自动的不需要用户干预。
#1.3.持久化
Ignite提供了许多功能可以将数据持久化磁盘上同时还保持一致性。可以在不丢失数据的前提下重启集群可以应对故障并在内存不足时为数据提供存储。启用原生持久化后Ignite会将所有数据保存在磁盘上并将尽可能多的数据加载到内存中进行处理。更多信息请参考Ignite持久化章节的内容。
#2.配置数据区
#2.1.概述
Ignite使用数据区的概念来控制可用于缓存的内存数量数据区是缓存数据存储在内存中的逻辑可扩展区域。可以控制数据区的初始值及其可以占用的最大值除了大小之外数据区还控制缓存的持久化配置。
Ignite有一个默认的数据区最多可占用该节点20的内存并且创建的所有缓存均位于该数据区中但是也可以添加任意多个数据区创建多个数据区的原因有
可以通过不同数据区分别配置缓存对应的可用内存量持久化参数是按数据区配置的。如果要同时具有纯内存缓存和持久化缓存则需要配置两个或多个具有不同持久化参数的数据区一个用于纯内存缓存一个用于持久化缓存部分内存参数比如退出策略是按照数据区进行配置的。
下面的章节会演示如何更改默认数据区的参数或配置多个数据区。
#2.2.配置默认数据区
新的缓存默认会添加到默认的数据区中可以在数据区配置中更改默认数据区的属性
XMLJavaC#/.NET
DataStorageConfiguration storageCfg new DataStorageConfiguration();DataRegionConfiguration defaultRegion new DataRegionConfiguration();
defaultRegion.setName(Default_Region);
defaultRegion.setInitialSize(100 * 1024 * 1024);storageCfg.setDefaultDataRegionConfiguration(defaultRegion);IgniteConfiguration cfg new IgniteConfiguration();cfg.setDataStorageConfiguration(storageCfg);// Start the node.
Ignite ignite Ignition.start(cfg);#2.3.添加自定义数据区
除了默认的数据区还可以使用自定义配置定义更多个数据区在下面的示例中配置了一个数据区占用40MB空间然后使用了Random-2-LRU退出策略注意在进一步的缓存配置中在该数据区中创建了一个缓存。
XMLJavaC#/.NET
DataStorageConfiguration storageCfg new DataStorageConfiguration();DataRegionConfiguration defaultRegion new DataRegionConfiguration();
defaultRegion.setName(Default_Region);
defaultRegion.setInitialSize(100 * 1024 * 1024);storageCfg.setDefaultDataRegionConfiguration(defaultRegion);
// 40MB memory region with eviction enabled.
DataRegionConfiguration regionWithEviction new DataRegionConfiguration();
regionWithEviction.setName(40MB_Region_Eviction);
regionWithEviction.setInitialSize(20 * 1024 * 1024);
regionWithEviction.setMaxSize(40 * 1024 * 1024);
regionWithEviction.setPageEvictionMode(DataPageEvictionMode.RANDOM_2_LRU);storageCfg.setDataRegionConfigurations(regionWithEviction);IgniteConfiguration cfg new IgniteConfiguration();cfg.setDataStorageConfiguration(storageCfg);CacheConfiguration cache1 new CacheConfiguration(SampleCache);
//this cache will be hosted in the 40MB_Region_Eviction data region
cache1.setDataRegionName(40MB_Region_Eviction);cfg.setCacheConfiguration(cache1);// Start the node.
Ignite ignite Ignition.start(cfg);#2.4.缓存预热策略
集群启动组网成功之后Ignite本身并不需要对内存进行预热应用就可以在其上执行计算和查询。但是对于需要低延迟的系统还是希望在查询数据之前先将数据加载到内存中的。
当前Ignite的预热策略是从索引开始将数据加载到所有或者指定的数据区直到用完可用空间为止。可以为所有的数据区进行配置默认也可以单独为某个数据区进行配置。
要预热所有数据区需将配置参数LoadAllWarmUpStrategy传递给DataStorageConfiguration#setDefaultWarmUpConfiguration如下所示
XMLJava
IgniteConfiguration cfg new IgniteConfiguration();DataStorageConfiguration storageCfg new DataStorageConfiguration();//Changing the default warm-up strategy for all data regions
storageCfg.setDefaultWarmUpConfiguration(new LoadAllWarmUpConfiguration());cfg.setDataStorageConfiguration(storageCfg);要预热某个数据区需将配置参数LoadAllWarmUpStrategy传递给DataStorageConfiguration#setWarmUpConfiguration如下所示
XMLJava
IgniteConfiguration cfg new IgniteConfiguration();DataStorageConfiguration storageCfg new DataStorageConfiguration();//Setting another warm-up strategy for a custom data region
DataRegionConfiguration myNewDataRegion new DataRegionConfiguration();myNewDataRegion.setName(NewDataRegion);//You can tweak the initial size as well as other settings
myNewDataRegion.setInitialSize(100 * 1024 * 1024);//Performing data loading from disk in DRAM on restarts.
myNewDataRegion.setWarmUpConfiguration(new LoadAllWarmUpConfiguration());//Enabling Ignite persistence. Ignite reads data from disk when queried for tables/caches from this region.
myNewDataRegion.setPersistenceEnabled(true);//Applying the configuration.
storageCfg.setDataRegionConfigurations(myNewDataRegion);cfg.setDataStorageConfiguration(storageCfg);要停止预热所有数据区请将配置参数NoOpWarmUpStrategy传递给DataStorageConfiguration#setDefaultWarmUpConfiguration如下所示
XMLJava
IgniteConfiguration cfg new IgniteConfiguration();DataStorageConfiguration storageCfg new DataStorageConfiguration();storageCfg.setDefaultWarmUpConfiguration(new NoOpWarmUpConfiguration());cfg.setDataStorageConfiguration(storageCfg);要停止预热某个数据区请将配置参数NoOpWarmUpStrategy传递给DataStorageConfiguration#setWarmUpConfiguration如下所示
XMLJava
IgniteConfiguration cfg new IgniteConfiguration();DataStorageConfiguration storageCfg new DataStorageConfiguration();//Setting another warm-up strategy for a custom data region
DataRegionConfiguration myNewDataRegion new DataRegionConfiguration();myNewDataRegion.setName(NewDataRegion);//You can tweak the initial size as well as other settings
myNewDataRegion.setInitialSize(100 * 1024 * 1024);//Skip data loading from disk in DRAM on restarts.
myNewDataRegion.setWarmUpConfiguration(new NoOpWarmUpConfiguration());//Enabling Ignite persistence. Ignite reads data from disk when queried for tables/caches from this region.
myNewDataRegion.setPersistenceEnabled(true);//Applying the configuration.
storageCfg.setDataRegionConfigurations(myNewDataRegion);cfg.setDataStorageConfiguration(storageCfg);还可以使用control.sh和JMX停止缓存预热过程。
要使用control.sh停止预热
LinuxWindows
control.sh --warm-up --stop --yes要使用JMX停止预热可使用下面的方法
org.apache.ignite.mxbean.WarmUpMXBean#stopWarmUp#3.退出策略
如果关闭了Ignite原生持久化Ignite会在堆外内存中存储所有的缓存条目当有新的数据注入会进行页面的分配。如果达到了内存的限制Ignite无法分配页面时部分数据就必须从内存中删除以避免内存溢出这个过程叫做退出退出保证系统不会内存溢出但是代价是内存数据丢失以及如果需要数据还需要重新加载。
退出策略用于下面的场景
关闭原生持久化之后的堆外内存整合外部存储后的堆外内存堆内缓存近缓存如果启用。
如果开启了原生持久化当Ignite无法分配新的页面时会有一个叫做页面替换的简单过程来进行堆外内存的释放不同点在于数据并没有丢失因为其存储于持久化存储因此不用担心数据丢失而要关注效率。页面替换由Ignite自动处理用户无法进行配置。
#3.1.堆外内存退出
堆外内存退出的实现方式如下
当内存使用超过预设限制时Ignite使用预配置的算法之一来选择最适合退出的内存页面。然后将页面中的每个缓存条目从页面中删除但是会保留被事务锁定的条目。因此整个页面或大块页面都是空的可以再次使用。 堆外内存的退出默认是关闭的这意味着内存使用量会一直增长直到达到限值。如果要开启退出需要在数据区配置中指定页面退出模式。注意堆外内存退出是数据区级的如果没使用数据区那么需要给默认的数据区显式地增加参数来配置退出。
默认情况下当某个数据区的内存消耗量达到90%时退出就开始了如果希望更早或者更晚地发起退出可以配置DataRegionConfiguration.setEvictionThreshold(...)参数。
Ignite支持两种页面选择算法
Random-LRURandom-2-LRU
两者的不同下面会说明。
#3.1.1.Random-LRU
要启用Random-LRU退出算法配置方式如下所示
XMLJavaC#/.NET
// Node configuration.
IgniteConfiguration cfg new IgniteConfiguration();// Memory configuration.
DataStorageConfiguration storageCfg new DataStorageConfiguration();// Creating a new data region.
DataRegionConfiguration regionCfg new DataRegionConfiguration();// Region name.
regionCfg.setName(20GB_Region);// 500 MB initial size (RAM).
regionCfg.setInitialSize(500L * 1024 * 1024);// 20 GB max size (RAM).
regionCfg.setMaxSize(20L * 1024 * 1024 * 1024);// Enabling RANDOM_LRU eviction for this region.
regionCfg.setPageEvictionMode(DataPageEvictionMode.RANDOM_LRU);// Setting the data region configuration.
storageCfg.setDataRegionConfigurations(regionCfg);// Applying the new configuration.
cfg.setDataStorageConfiguration(storageCfg);Random-LRU算法工作方式如下
当一个数据区配置了内存策略时就会分配一个堆外数组它会跟踪每个数据页面的最后使用时间戳当数据页面被访问时跟踪数组的时间戳就会被更新当到了退出页面时间时算法会从跟踪数组中随机地选择5个索引然后退出最近的时间戳对应的页面如果部分索引指向非数据页面索引或者系统页面算法会选择其它的页面。
#3.1.2.Random-2-LRU
Random-2-LRU退出算法是Random-LRU算法的抗扫描版配置方式如下所示
XMLJavaC#/.NET
// Ignite configuration.
IgniteConfiguration cfg new IgniteConfiguration();// Memory configuration.
DataStorageConfiguration storageCfg new DataStorageConfiguration();// Creating a new data region.
DataRegionConfiguration regionCfg new DataRegionConfiguration();// Region name.
regionCfg.setName(20GB_Region);// 500 MB initial size (RAM).
regionCfg.setInitialSize(500L * 1024 * 1024);// 20 GB max size (RAM).
regionCfg.setMaxSize(20L * 1024 * 1024 * 1024);// Enabling RANDOM_2_LRU eviction for this region.
regionCfg.setPageEvictionMode(DataPageEvictionMode.RANDOM_2_LRU);// Setting the data region configuration.
storageCfg.setDataRegionConfigurations(regionCfg);// Applying the new configuration.
cfg.setDataStorageConfiguration(storageCfg);在Random-2-LRU算法中每个数据页面会存储两个最近访问时间戳退出时算法会随机地从跟踪数组中选择5个索引值然后两个最近时间戳中的最小值会被用来和另外4个候选页面中的最小值进行比较。
Random-2-LRU比Random-LRU要好因为它解决了昙花一现的问题即一个页面很少被访问但是偶然地被访问了一次然后就会被退出策略保护很长时间。
#3.2.堆内缓存退出
关于如何为堆内缓存配置退出策略的介绍请参见堆内缓存配置退出策略章节的内容。 配置持久化
#1.Ignite持久化
#1.1.概述
Ignite持久化或者说原生持久化是旨在提供持久化存储的一组功能。启用后Ignite会将所有数据存储在磁盘上并将尽可能多的数据加载到内存中进行处理。例如如果有100个条目而内存仅能存储20个则所有100个都存储在磁盘上而内存中仅缓存20个以获得更好的性能。
如果关闭原生持久化并且不使用任何外部存储时Ignite就是一个纯内存存储。
启用持久化后每个服务端节点只会存储整个数据的一个子集即只包含分配给该节点的分区如果启用了备份也包括备份分区。
原生持久化基于以下特性
在磁盘上存储数据分区预写日志检查点操作系统交换的使用。
启用持久化后Ignite会将每个分区存储在磁盘上的单独文件中分区文件的数据格式与保存在内存中的数据格式相同。如果启用了分区备份则也会保存在磁盘上除了数据分区Ignite还存储索引和元数据。 可以在配置中修改数据文件的默认位置。
#1.2.启用持久化存储
原生持久化是配置在数据区上的。要启用持久化存储需要在数据区配置中将persistenceEnabled属性设置为true可以同时有纯内存数据区和持久化数据区。
以下是如何为默认数据区启用持久化存储的示例
XMLJavaC#/.NET
IgniteConfiguration cfg new IgniteConfiguration();//data storage configuration
DataStorageConfiguration storageCfg new DataStorageConfiguration();storageCfg.getDefaultDataRegionConfiguration().setPersistenceEnabled(true);cfg.setDataStorageConfiguration(storageCfg);Ignite ignite Ignition.start(cfg);#1.3.配置持久化存储目录
启用持久化之后节点就会在{IGNITE_WORK_DIR}/db目录中存储用户的数据、索引和WAL文件该目录称为存储目录。通过配置DataStorageConfiguration的storagePath属性可以修改存储目录。
每个节点都会在存储目录下维护一个子目录树来存储缓存数据、WAL文件和WAL存档文件。
子目录名描述{WORK_DIR}/db/{nodeId}该目录中包括了缓存的数据和索引{WORK_DIR}/db/wal/{nodeId}该目录中包括了WAL文件{WORK_DIR}/db/wal/archive/{nodeId}该目录中包括了WAL存档文件
这里的nodeId要么是节点的一致性ID如果在节点配置中定义要么是自动生成的节点ID它用于确保节点目录的唯一性。如果多个节点共享同一工作目录则它们将使用不同的子目录。
如果工作目录包含多个节点的持久化文件存在多个具有不同nodeId的{nodeId}子目录则该节点将选择第一个未使用的子目录。为了确保节点即使重启也始终使用固定的子目录即指定数据分区需要在节点配置中将IgniteConfiguration.setConsistentId设置为集群范围内的唯一值。
修改存储目录的代码如下所示
XMLJavaC#/.NET
IgniteConfiguration cfg new IgniteConfiguration();//data storage configuration
DataStorageConfiguration storageCfg new DataStorageConfiguration();storageCfg.getDefaultDataRegionConfiguration().setPersistenceEnabled(true);storageCfg.setStoragePath(/opt/storage);cfg.setDataStorageConfiguration(storageCfg);Ignite ignite Ignition.start(cfg);还可以将WAL和WAL存档路径指向存储目录之外的目录。详细信息后面章节会介绍。
#1.4.预写日志
预写日志是节点上发生的所有数据修改操作包括删除的日志。在内存中更新页面时更新不会直接写入分区文件而是会附加到WAL的末尾。
预写日志的目的是为单个节点或整个集群的故障提供一个恢复机制。如果发生故障或重启则可以依靠WAL的内容将集群恢复到最近成功提交的事务。
WAL由几个文件称为活动段和一个存档组成。活动段按顺序填充然后循环覆盖。第一个段写满后其内容将复制到WAL存档中请参见下面的WAL存档章节。在复制第一段时第二段会被视为激活的WAL文件并接受来自应用端的所有更新活动段默认有10个。
#1.4.1.WAL模式
WAL模式有几种每种模式对性能的影响方式不同并提供不同的一致性保证
WAL模式描述一致性保证FSYNC保证每个原子写或者事务性提交都会持久化到磁盘。数据更新不会丢失不管是任何的操作系统或者进程故障甚至是电源故障。LOG_ONLY默认模式对于每个原子写或者事务性提交保证会刷新到操作系统的缓冲区缓存或者内存映射文件。默认会使用内存映射文件方式并且可以通过将IGNITE_WAL_MMAP系统属性配置为false将其关闭。如果仅仅是进程崩溃数据更新会保留。BACKGROUND如果打开了IGNITE_WAL_MMAP属性默认该模式的行为类似于LOG_ONLY模式如果关闭了内存映射文件方式变更会保持在节点的内部缓冲区缓冲区刷新到磁盘的频率由walFlushFrequency参数定义。如果打开了IGNITE_WAL_MMAP属性默认该模式提供了与LOG_ONLY模式一样的保证否则如果进程故障或者其它的故障发生时最近的数据更新可能丢失。NONEWAL被禁用只有在节点优雅地关闭时变更才会正常持久化使用Ignite#active(false)可以冻结集群然后停止节点。可能出现数据丢失如果节点在更新操作期间突然终止则磁盘上存储的数据很可能出现不同步或损坏。
#1.4.2.WAL存档
WAL存档用于保存故障后恢复节点所需的WAL段。存档中保存的段的数量应确保所有段的总大小不超过WAL存档的既定大小。
WAL存档的最大大小在磁盘上占用的总空间定义为检查点缓冲区大小的4倍可以在配置中更改该值。
警告
将WAL存档大小配置为小于默认值可能影响性能用于生产之前需要进行测试。
#1.4.3.修改WAL段大小
在高负载情况下默认的WAL段大小64MB可能效率不高因为它会导致WAL过于频繁地在段之间切换并且切换/轮转是一项昂贵的操作。更大的WAL段大小有助于提高高负载下的性能但代价是增加WAL文件和WAL存档文件的总大小。
可以在数据存储配置中更改WAL段文件的大小该值必须介于512KB和2GB之间。
XMLJava
IgniteConfiguration cfg new IgniteConfiguration();
DataStorageConfiguration storageCfg new DataStorageConfiguration();
storageCfg.getDefaultDataRegionConfiguration().setPersistenceEnabled(true);storageCfg.setWalSegmentSize(128 * 1024 * 1024);cfg.setDataStorageConfiguration(storageCfg);Ignite ignite Ignition.start(cfg);#1.4.4.禁用WAL
警告
禁用或启用WAL只能在稳定的拓扑上进行即所有基线节点都应该在线在此操作期间不应该有节点加入或离开集群。否则缓存可能会陷入不一致状态。如果发生这种情况建议销毁受影响的缓存。
在某些情况下禁用WAL以获得更好的性能是合理的做法。例如在初始数据加载期间禁用WAL并在预加载完成后启用WAL就是个好的做法。
JavaC#/.NETSQL
IgniteConfiguration cfg new IgniteConfiguration();
DataStorageConfiguration storageCfg new DataStorageConfiguration();
storageCfg.getDefaultDataRegionConfiguration().setPersistenceEnabled(true);cfg.setDataStorageConfiguration(storageCfg);Ignite ignite Ignition.start(cfg);ignite.cluster().state(ClusterState.ACTIVE);String cacheName myCache;ignite.getOrCreateCache(cacheName);ignite.cluster().disableWal(cacheName);//load data
ignite.cluster().enableWal(cacheName);警告
如果禁用WAL并重启节点则将从该节点上的持久化存储中删除所有数据。之所以这样实现是因为如果没有WAL则无法保证节点故障或重启时的数据一致性。
#1.4.5.WAL存档压缩
可以启用WAL存档压缩以减少WAL存档占用的空间。WAL存档默认包含最后20个检查点的段此数字是可配置的。启用压缩后则将所有1个检查点之前的已存档段压缩为ZIP格式如果需要这些段例如在节点之间再平衡数据则会将其解压缩为原始格式。
关于如何启用WAL存档压缩请参见下面的配置属性章节。
#1.4.6.WAL记录压缩
如设计文档中所述在确认用户操作之前代表数据更新的物理和逻辑记录已写入WAL文件Ignite可以先将WAL记录压缩到内存中然后再写入磁盘以节省空间。
WAL记录压缩要求引入ignite-compress模块具体请参见启用模块。
WAL记录压缩默认是禁用的如果要启用需要在数据存储配置中设置压缩算法和压缩级别
IgniteConfiguration cfg new IgniteConfiguration();DataStorageConfiguration dsCfg new DataStorageConfiguration();
dsCfg.getDefaultDataRegionConfiguration().setPersistenceEnabled(true);//WAL page compression parameters
dsCfg.setWalPageCompression(DiskPageCompression.LZ4);
dsCfg.setWalPageCompressionLevel(8);cfg.setDataStorageConfiguration(dsCfg);
Ignite ignite Ignition.start(cfg);DiskPageCompression中列出了支持的压缩算法。
#1.4.7.禁用WAL存档
有时可能想要禁用WAL存档比如减少与将WAL段复制到存档文件有关的开销当Ignite将数据写入WAL段的速度快于将段复制到存档文件的速度时这样做就有用因为这样会导致I/O瓶颈从而冻结节点的操作如果遇到了这样的问题就可以尝试关闭WAL存档。
通过将WAL路径和WAL存档路径配置为同一个值可以关闭存档。这时Ignite就不会将段复制到存档文件而是只是在WAL文件夹中创建新的段。根据WAL存档大小设置旧段将随着WAL的增长而删除。
#1.5.检查点
检查点是一个将脏页面从内存复制到磁盘上的分区文件的过程脏页面是指页面已经在内存中进行了更新但是还没有写入对应的分区文件只是添加到了WAL中。
创建检查点后所有更改都将保存到磁盘并且在节点故障并重启后将生效。
检查点和预写日志旨在确保数据的持久化和节点故障时的恢复能力。 这个过程通过在磁盘上保持页面的最新状态而节省更多的磁盘空间检查点完成后就可以在WAL存档中删除检查点执行前创建的WAL段。
具体请参见相关的文档
检查点操作监控调整检查点缓冲区大小。
#1.6.配置属性
下表列出了DataStorageConfiguration的主要参数
属性名描述默认值persistenceEnabled将该属性配置为true可以开启原生持久化。falsestoragePath数据存储路径。${IGNITE_HOME}/work/db/node{IDX}-{UUID}walPathWAL活动段存储路径。${IGNITE_HOME}/work/db/wal/walArchivePathWAL存档路径。${IGNITE_HOME}/work/db/wal/archive/walCompactionEnabled将该属性配置为true可以开启WAL存档压缩。falsewalSegmentSizeWAL段文件大小字节。64MBwalMode预写日志模式。LOG_ONLYwalCompactionLevelWAL压缩级别1表示速度最快9表示最高的压缩率。1maxWalArchiveSizeWAL存档占用空间最大值字节。检查点缓冲区大小的4倍
#2.外部存储
#2.1.概述
Ignite可以做为已有数据库之上的一个缓存层包括RDBMS或者NoSQL数据库比如Apache Cassandra或者MongoDB等该场景通过内存计算来对底层数据库进行加速。
Ignite可以与Apache Cassandra直接集成但是暂时还不支持其他NoSQL数据库但是开发自己的CacheStore接口实现。
使用外部存储的两个主要场景是
作为已有数据库的缓存层这时可以通过将数据加载到内存来优化处理速度还可以为不支持SQL的数据库带来SQL支持能力数据全部加载到内存希望将数据持久化到外部数据库而不是单一的原生持久化。 CacheStore接口同时扩展了javax.cache.integration.CacheLoader和javax.cache.integration.CacheWriter相对应的分别用于通读和通写。也可以单独实现每个接口然后在缓存配置中单独配置。
提示
除了键-值操作Ignite的通写也支持SQL的INSERT、UPDATE和MERGE但是SELECT查询语句不会从外部数据库通读数据。
#2.1.1.通读和通写
通读是指如果缓存中不存在则从底层持久化存储中读取数据。注意这仅适用于通过键-值API进行的get操作SELECT查询不会从外部数据库查询数据。要执行SELECT查询必须通过调用loadCache()方法将数据从数据库预加载到缓存中。
通写是指数据在缓存中更新后会自动持久化。所有的通读和通写操作都参与缓存事务然后作为整体提交或回滚。
#2.1.2.后写缓存
在一个简单的通写模式中每个缓存的put和remove操作都会涉及一个持久化存储的请求因此整个缓存更新的持续时间可能是相对比较长的。另外密集的缓存更新频率也会导致非常高的存储负载。
对于这种情况可以启用后写模式它会以异步的方式执行更新操作。这个方式的主要概念是累积更新操作然后作为一个批量异步刷入持久化存储。数据的刷新可以基于时间的事件数据条目驻留在队列中的时间是有限的来触发也可以基于队列大小的事件如果队列大小达到限值会被刷新触发或者两者先发生者优先。
性能和一致性
启用后写缓存可以通过异步更新来提高性能但这可能会导致一致性下降因为某些更新可能由于节点故障或崩溃而丢失。
对于后写的方式只有数据的最后一次更新会被写入底层存储。如果键为key1的缓存数据分别被依次更新为值value1、value2和value3那么只有(key1,value3)对这一个存储请求会被传播到持久化存储。
更新性能
批量的存储操作通常比按顺序的单一操作更有效率因此可以通过开启后写模式的批量操作来利用这个特性。简单类型put和remove的简单顺序更新操作可以被组合成一个批量操作。比如连续地往缓存中写入(key1,value1)、(key2,value2)、(key3,value3)可以通过一个单一的CacheStore.putAll(...)操作批量处理。
#2.2.RDBMS集成
要将RDBMS作为底层存储可以使用下面的CacheStore实现之一
CacheJdbcPojoStore使用反射将对象存储为一组字段如果在现有数据库之上添加Ignite并希望使用底层表中的部分字段或所有字段请使用此实现CacheJdbcBlobStore将对象以Blob格式存储在底层数据库中当将外部数据库作为持久化存储并希望以简单格式存储数据时可以用此实现。
下面是CacheStore两种实现的配置示例
#2.2.1.CacheJdbcPojoStore
使用CacheJdbcPojoStore可以将对象存储为一组字段并可以配置表列和对象字段之间的映射。 将CacheConfiguration.cacheStoreFactory属性设置为org.apache.ignite.cache.store.jdbc.CacheJdbcPojoStoreFactory并提供以下属性 dataSourceBean数据库连接凭据URL、用户、密码dialect实现与数据库兼容的SQL方言的类。Ignite为MySQL、Oracle、H2、SQLServer和DB2数据库提供了现成的实现。这些方言位于org.apache.ignite.cache.store.jdbc.dialect包中types此属性用于定义数据库表和相应的POJO之间的映射请参见下面的POJO配置示例。 可选如果要在缓存上执行SQL查询请配置查询实体。
以下示例演示了如何在MySQL表之上配置Ignite缓存。该映射到Person类对象的表有2列idINTEGER和nameVARCHAR。
可以通过XML或Java代码配置CacheJdbcPojoStore。
XMLJava
IgniteConfiguration igniteCfg new IgniteConfiguration();CacheConfigurationInteger, Person personCacheCfg new CacheConfiguration();personCacheCfg.setName(PersonCache);
personCacheCfg.setCacheMode(CacheMode.PARTITIONED);
personCacheCfg.setAtomicityMode(CacheAtomicityMode.ATOMIC);personCacheCfg.setReadThrough(true);
personCacheCfg.setWriteThrough(true);CacheJdbcPojoStoreFactoryInteger, Person factory new CacheJdbcPojoStoreFactory();
factory.setDialect(new MySQLDialect());
factory.setDataSourceFactory((FactoryDataSource)() - {MysqlDataSource mysqlDataSrc new MysqlDataSource();mysqlDataSrc.setURL(jdbc:mysql://[host]:[port]/[database]);mysqlDataSrc.setUser(YOUR_USER_NAME);mysqlDataSrc.setPassword(YOUR_PASSWORD);return mysqlDataSrc;
});JdbcType personType new JdbcType();
personType.setCacheName(PersonCache);
personType.setKeyType(Integer.class);
personType.setValueType(Person.class);
// Specify the schema if applicable
// personType.setDatabaseSchema(MY_DB_SCHEMA);
personType.setDatabaseTable(PERSON);personType.setKeyFields(new JdbcTypeField(java.sql.Types.INTEGER, id, Integer.class, id));personType.setValueFields(new JdbcTypeField(java.sql.Types.INTEGER, id, Integer.class, id)),new JdbcTypeField(java.sql.Types.VARCHAR, name, String.class, name));factory.setTypes(personType);personCacheCfg.setCacheStoreFactory(factory);QueryEntity qryEntity new QueryEntity();qryEntity.setKeyType(Integer.class.getName());
qryEntity.setValueType(Person.class.getName());
qryEntity.setKeyFieldName(id);SetString keyFields new HashSet();
keyFields.add(id);
qryEntity.setKeyFields(keyFields);LinkedHashMapString, String fields new LinkedHashMap();
fields.put(id, java.lang.Integer);
fields.put(name, java.lang.String);qryEntity.setFields(fields);personCacheCfg.setQueryEntities(Collections.singletonList(qryEntity));igniteCfg.setCacheConfiguration(personCacheCfg);Person类
class Person implements Serializable {private static final long serialVersionUID 0L;private int id;private String name;public Person() {}public String getName() {return name;}public void setName(String name) {this.name name;}public int getId() {return id;}public void setId(int id) {this.id id;}
}#2.2.2.CacheJdbcBlobStore
CacheJdbcBlobStore将对象以Blob格式存储于底层数据库中它会创建一张表名为ENTRIES有名为key和val的列类型都为binary。
可以通过提供自定义的建表语句和DML语句分别用于加载、更新、删除数据来修改默认的定义具体请参见CacheJdbcBlobStore的javadoc。
在下面的示例中Person类的对象存储于单一列的字节数组中。
XMLJava
IgniteConfiguration igniteCfg new IgniteConfiguration();CacheConfigurationInteger, Person personCacheCfg new CacheConfiguration();
personCacheCfg.setName(PersonCache);CacheJdbcBlobStoreFactoryInteger, Person cacheStoreFactory new CacheJdbcBlobStoreFactory();cacheStoreFactory.setUser(USER_NAME);MysqlDataSource mysqlDataSrc new MysqlDataSource();
mysqlDataSrc.setURL(jdbc:mysql://[host]:[port]/[database]);
mysqlDataSrc.setUser(USER_NAME);
mysqlDataSrc.setPassword(PASSWORD);cacheStoreFactory.setDataSource(mysqlDataSrc);personCacheCfg.setCacheStoreFactory(cacheStoreFactory);personCacheCfg.setWriteThrough(true);
personCacheCfg.setReadThrough(true);igniteCfg.setCacheConfiguration(personCacheCfg);#2.3.加载数据
缓存存储配置完成并启动集群后就可以使用下面的代码从数据库加载数据了
// Load data from person table into PersonCache.
IgniteCacheInteger, Person personCache ignite.cache(PersonCache);personCache.loadCache(null);#2.4.NoSQL数据库集成
通过实现CacheStore接口可以将Ignite与任何NoSQL数据库集成。
警告
虽然Ignite支持分布式事务但是并不会使NoSQL数据库具有事务性除非数据库本身直接支持事务。
#2.4.1.Cassandra集成
Ignite通过CacheStore实现直接支持将Apache Cassandra用作持久化存储。其利用Cassandra的异步查询来提供loadAll()、writeAll()和deleteAll()等高性能批处理操作并自动在Cassandra中创建所有必要的表和命名空间。
具体请参见Cassandra集成章节的介绍。
#3.交换空间
#3.1.概述
如果使用纯内存存储随着数据量的大小逐步达到物理内存大小可能导致内存溢出。如果不想使用原生持久化或者外部存储还可以开启交换这时Ignite会将内存中的数据移动到磁盘上的交换空间注意Ignite不会提供自己的交换空间实现而是利用了操作系统OS提供的交换功能。
打开交换空间之后Ignite会将数据存储在内存映射文件MMF中操作系统会根据内存使用情况将其内容交换到磁盘但是这时数据访问的性能会下降。另外还没有数据持久性保证这意味着交换空间中的数据只在节点在线期间才可用。一旦存在交换空间的节点停止所有数据都会丢失。因此应该将交换空间作为内存的扩展以留出足够的时间向集群中添加更多的节点让数据重新分布并避免集群未及时扩容导致内存溢出的错误OOM发生。
注意
虽然交换空间位于磁盘上但是其不能替代原生持久化。交换空间中的数据只有在节点在线时才有效一旦节点关闭数据将丢失。为了确保数据一直可用应该启用原生持久化或使用外部存储。
#3.2.启用交换
数据区的maxSize定义了区域的整体最大值如果数据量达到了maxSize然后既没有使用原生持久化也没有使用外部存储那么就会抛出内存溢出异常。使用交换可以避免这种情况的发生做法是
配置maxSize的值大于内存大小这时操作系统就会使用交换启用数据区的交换如下所示。
XMLJavaC#/.NET
// Node configuration.
IgniteConfiguration cfg new IgniteConfiguration();// Durable Memory configuration.
DataStorageConfiguration storageCfg new DataStorageConfiguration();// Creating a new data region.
DataRegionConfiguration regionCfg new DataRegionConfiguration();// Region name.
regionCfg.setName(500MB_Region);// Setting initial RAM size.
regionCfg.setInitialSize(100L * 1024 * 1024);// Setting region max size equal to physical RAM size(5 GB)
regionCfg.setMaxSize(5L * 1024 * 1024 * 1024);// Enable swap space.
regionCfg.setSwapPath(/path/to/some/directory);// Setting the data region configuration.
storageCfg.setDataRegionConfigurations(regionCfg);// Applying the new configuration.
cfg.setDataStorageConfiguration(storageCfg);#4.实现自定义CacheStore
可以实现自己的自定义CacheStore并将其作为缓存的底层数据存储IgniteCache中读写数据的方法将会调用CacheStore实现中相应的方法。
下表描述了CacheStore接口中的方法
方法描述loadCache()调用IgniteCache.loadCache(…)时就会调用该方法通常用于从数据库预加载数据。此方法在驻有缓存的所有节点上执行要加载单个节点的数据需要在该节点上调用IgniteCache.localLoadCache()方法。load()、write()、delete()当调用IgniteCache接口的get()、put()、remove()方法时会分别调用这3个方法这些方法用于单条数据的通读和通写。loadAll()、writeAll()、deleteAll()当调用IgniteCache接口的getAll()、putAll()、removeAll()方法时会分别调用这3个方法这些方法用于处理多条数据的通读和通写通常以批量的形式实现以提高性能。
#4.1.CacheStoreAdapter
CacheStoreAdapter是CacheStore的扩展提供了批量操作的默认实现如loadAll(Iterable)、writeAll(Collection)和deleteAll(Collection)其会迭代所有条目并在每个条目上调用对应的load()、write()和delete()方法。
#4.2.CacheStoreSession
CacheStoreSession用于持有多个操作之间的上下文主要用于提供事务支持。一个事务中的多个操作是在同一个数据库连接中执行的并在事务提交时提交该连接。通过GridCacheStoreSessionResource注解可以将其注入CacheStore实现中。
关于如何实现事务化的CacheStore可以参见GitHub上的示例。
#4.3.示例
下面是一个CacheStore的非事务化实现的示例
public class CacheJdbcPersonStore extends CacheStoreAdapterLong, Person {// This method is called whenever the get(...) methods are called on IgniteCache.Overridepublic Person load(Long key) {try (Connection conn connection()) {try (PreparedStatement st conn.prepareStatement(select * from PERSON where id?)) {st.setLong(1, key);ResultSet rs st.executeQuery();return rs.next() ? new Person(rs.getInt(1), rs.getString(2)) : null;}} catch (SQLException e) {throw new CacheLoaderException(Failed to load: key, e);}}Overridepublic void write(Entry? extends Long, ? extends Person entry) throws CacheWriterException {try (Connection conn connection()) {// Syntax of MERGE statement is database specific and should be adopted for your database.// If your database does not support MERGE statement then use sequentially// update, insert statements.try (PreparedStatement st conn.prepareStatement(merge into PERSON (id, name) key (id) VALUES (?, ?))) {Person val entry.getValue();st.setLong(1, entry.getKey());st.setString(2, val.getName());st.executeUpdate();}} catch (SQLException e) {throw new CacheWriterException(Failed to write entry ( entry ), e);}}// This method is called whenever the remove(...) method are called on IgniteCache.Overridepublic void delete(Object key) {try (Connection conn connection()) {try (PreparedStatement st conn.prepareStatement(delete from PERSON where id?)) {st.setLong(1, (Long) key);st.executeUpdate();}} catch (SQLException e) {throw new CacheWriterException(Failed to delete: key, e);}}// This method is called whenever the loadCache() and localLoadCache()// methods are called on IgniteCache. It is used for bulk-loading the cache.// If you dont need to bulk-load the cache, skip this method.Overridepublic void loadCache(IgniteBiInClosureLong, Person clo, Object... args) {if (args null || args.length 0 || args[0] null)throw new CacheLoaderException(Expected entry count parameter is not provided.);final int entryCnt (Integer) args[0];try (Connection conn connection()) {try (PreparedStatement st conn.prepareStatement(select * from PERSON)) {try (ResultSet rs st.executeQuery()) {int cnt 0;while (cnt entryCnt rs.next()) {Person person new Person(rs.getInt(1), rs.getString(2));clo.apply(person.getId(), person);cnt;}}}} catch (SQLException e) {throw new CacheLoaderException(Failed to load values from cache store., e);}}// Open JDBC connection.private Connection connection() throws SQLException {// Open connection to your RDBMS systems (Oracle, MySQL, Postgres, DB2, Microsoft SQL, etc.)Connection conn DriverManager.getConnection(jdbc:mysql://[host]:[port]/[database], YOUR_USER_NAME, YOUR_PASSWORD);conn.setAutoCommit(true);return conn;}
}#5.集群快照
#5.1.概述
对于开启了原生持久化的集群Ignite提供了创建集群完整快照的功能。一个Ignite快照包括了整个集群中所有存盘数据的完整一致副本以及用于恢复过程必需的其他一些文件。
快照的结构除了一些例外类似于Ignite原生持久化存储目录的布局以下面的快照为例看一下结构
work
└── snapshots└── backup23012020└── db├── binary_meta│ ├── node1│ ├── node2│ └── node3├── marshaller│ ├── node1│ ├── node2│ └── node3├── node1│ └── my-sample-cache│ ├── cache_data.dat│ ├── part-3.bin│ ├── part-4.bin│ └── part-6.bin├── node2│ └── my-sample-cache│ ├── cache_data.dat│ ├── part-1.bin│ ├── part-5.bin│ └── part-7.bin└── node3└── my-sample-cache├── cache_data.dat├── part-0.bin└── part-2.bin快照位于work\snapshots目录下名为backup23012020这里work是Ignite的工作目录创建快照的集群有3个节点所有节点都在同一台主机上运行。在此示例中节点分别名为node1、node2和node3而实际上它们的名字是节点的一致性ID快照保留了my-sample-cache缓存的副本db文件夹在part-N.bin和cache_data.dat文件中保留数据记录的副本。预写日志和检查点不在快照中因为这些在当前的恢复过程中并不需要binary_meta和marshaller目录存储了和元数据和编组器有关的信息。
通常快照分布于整个集群
上面的示例显示为同一个集群创建的快照运行于同一台物理机因此整个快照位于一个位置上。实际上集群中不同主机的所有节点上都会有快照数据。每个节点都持有快照的一段即归属于该节点的数据快照恢复过程会说明恢复时如何将所有段合并在一起。
#5.2.配置快照目录
快照默认存储在相应Ignite节点的工作目录中并和Ignite持久化保存数据、索引、WAL和其他文件使用相同的存储介质。因为快照消耗了和持久化相当的空间然后和Ignite持久化进程共享磁盘IO从而会影响应用的性能因此建议将快照和持久化文件存储在不同的存储介质上。
可以通过更改持久化文件的存储目录或覆盖快照的默认位置来避免Ignite原生持久化和快照之间的这种干扰如下所示
XMLJava
IgniteConfiguration cfg new IgniteConfiguration();File exSnpDir U.resolveWorkDirectory(U.defaultWorkDirectory(), ex_snapshots, true);cfg.setSnapshotPath(exSnpDir.getAbsolutePath());#5.3.创建快照
Ignite提供了几个API来进行快照的创建下面看下所有的选项
#5.3.1.使用控制脚本
Ignite自带的控制脚本支持和快照有关的操作如下所示
#Create a cluster snapshot:
control.(sh|bat) --snapshot create snapshot_name#Cancel a running snapshot:
control.(sh|bat) --snapshot cancel snapshot_name#Kill a running snapshot:
control.(sh|bat) --kill SNAPSHOT snapshot_name#5.3.2.使用JMX
使用SnapshotMXBean接口可以通过JMX执行和快照有关的过程
方法描述createSnapshot(String snpName)创建快照createSnapshot(String snpName)在创建快照的发起节点取消快照
#5.3.3.使用Java API
也可以通过Java API通过编程式创建快照
CacheConfigurationLong, String ccfg new CacheConfigurationLong, String(snapshot-cache);try (IgniteCacheLong, String cache ignite.getOrCreateCache(ccfg)) {cache.put(1, Maxim);// Start snapshot operation.ignite.snapshot().createSnapshot(snapshot_02092020).get();
}
finally {ignite.destroyCache(ccfg);
}#5.4.从快照恢复
当前数据恢复过程必须手动执行。简而言之需要停止集群用快照中的数据替换持久化数据和其他文件然后重启节点。
详细过程如下
停止要恢复的集群从检查点目录$IGNITE_HOME/work/cp中删除所有文件在每个节点上执行以下操作单独清理db/{node_id}目录如果他不在Ignite的work目录下 从$IGNITE_HOME/work/db/binary_meta目录中删除和{nodeId}有关的文件从$IGNITE_HOME/work/db/marshaller目录中删除和{nodeId}有关的文件从$IGNITE_HOME/work/db目录中删除和{nodeId}有关的文件和子目录将快照中属于{node_id}节点的文件复制到$IGNITE_HOME/work/目录中。如果db/{node_id}目录不在Ignite的工作目录下则应在对应目录复制数据文件。重启集群。
在不同拓扑的集群上恢复
有时可能在N个节点的集群上创建快照但是需要在有M个节点的集群上进行恢复下表说明了支持的选项
条件描述NM建议方案在拓扑一致的集群上创建和使用快照。NM在M个节点的集群上首先启动N个节点应用快照然后将剩余的M-N个集群节点加入拓扑然后等待数据再平衡和索引重建。NM不支持。
#5.5.一致性保证
在Ignite的持久化文件、索引、模式、二进制元数据、编组器以及节点的其他文件上的并发操作以及正在进行的修改所有的快照都是完全一致的。
集群范围的快照一致性是通过触发分区映射交换过程实现的通过这样做集群最终达到的状态是所有之前发起的事务全部完成新的事务全部暂停这个过程结束之后集群就会发起快照创建过程PME过程会确保快照以一致的状态包括了主快照和备份快照。
Ignite持久化文件与其快照副本之间的一致性是通过将原始文件复制到目标快照目录并跟踪所有正在进行的更改来实现的跟踪更改可能需要额外的空间。
#5.6.当前的限制
快照过程有若干限制如果要用于生产环境需要事先了解
不支持某个表/缓存的快照只能创建整个集群的快照未开启原生持久化的表/缓存不支持快照加密的缓存不包括在快照中同时只能执行一个快照操作如果一个服务端节点离开集群快照过程会被中断快照只能在具有相同节点ID的同一集群拓扑中恢复目前还不支持自动化的恢复过程只能手工执行。
#6.磁盘压缩
磁盘压缩是指将数据页面写入磁盘时对其进行压缩的过程以减小磁盘空间占用。这些页面在内存中是不压缩的但是当将数据刷新到磁盘时将使用配置的算法对其进行压缩。这仅适用于开启原生持久化的数据页面并且不会压缩索引或WAL记录的数据页WAL记录压缩是可以单独启用的。
磁盘页面压缩是在每个缓存的配置中设定的缓存必须在持久化的数据区中。目前没有选项可全局启用磁盘页面压缩此外还必须必须满足以下的条件
将持久化配置中的pageSize属性设置为文件系统页面大小的至少2倍这意味着页面大小必须为8K或16K启用ignite-compress模块。
要为某个缓存启用磁盘页面压缩需要在缓存配置中提供一种可用的压缩算法如下所示
XMLJava
DataStorageConfiguration dsCfg new DataStorageConfiguration();//set the page size to 2 types of the disk page size
dsCfg.setPageSize(4096 * 2);//enable persistence for the default data region
dsCfg.setDefaultDataRegionConfiguration(new DataRegionConfiguration().setPersistenceEnabled(true));IgniteConfiguration cfg new IgniteConfiguration();
cfg.setDataStorageConfiguration(dsCfg);CacheConfiguration cacheCfg new CacheConfiguration(myCache);
//enable disk page compression for this cache
cacheCfg.setDiskPageCompression(DiskPageCompression.LZ4);
//optionally set the compression level
cacheCfg.setDiskPageCompressionLevel(10);cfg.setCacheConfiguration(cacheCfg);Ignite ignite Ignition.start(cfg);#6.1.支持的算法
支持的压缩算法包括
ZSTD支持从-131072到22的压缩级别默认值3LZ4支持从0到17的压缩级别默认值0SNAPPYSKIP_GARBAGE该算法仅从半填充页面中提取有用的数据而不压缩数据。
#7.持久化调优
本章节总结了Ignite原生持久化调优的最佳实践。
#7.1.调整页面大小
Ignite的页面大小DataStorageConfiguration.pageSize不要小于存储设备SSD、闪存、HDD等的页面大小以及操作系统缓存页面的大小默认值为4KB。
操作系统的缓存页面大小很容易就可以通过系统工具和参数获取到。
存储设备比如SSD的页面大小可以在设备的说明上找到如果厂商未提供这些信息可以运行SSD的基准测试来算出这个数值如果还是难以拿到这个数值可以使用4KB作为Ignite的页面大小。很多厂商为了适应4KB的随机写工作负载不得不调整驱动因为很多标准基准测试都是默认使用4KB来自英特尔的白皮书也确认4KB足够了。
选定最优值之后可以将其用于集群的配置
XMLJavaC#/.NET
IgniteConfiguration cfg new IgniteConfiguration();// Durable memory configuration.
DataStorageConfiguration storageCfg new DataStorageConfiguration();// Changing the page size to 8 KB.
storageCfg.setPageSize(8192);cfg.setDataStorageConfiguration(storageCfg);#7.2.单独保存WAL
考虑为数据文件以及预写日志WAL使用单独的磁盘设备。Ignite会主动地写入数据文件以及WAL文件下面的示例会显示如何为数据存储、WAL以及WAL存档配置单独的路径
XMLJavaC#/.NET
IgniteConfiguration cfg new IgniteConfiguration();// Configuring Native Persistence.
DataStorageConfiguration storeCfg new DataStorageConfiguration();// Sets a path to the root directory where data and indexes are to be persisted.
// Its assumed the directory is on a separated SSD.
storeCfg.setStoragePath(/ssd/storage);// Sets a path to the directory where WAL is stored.
// Its assumed the directory is on a separated HDD.
storeCfg.setWalPath(/wal);// Sets a path to the directory where WAL archive is stored.
// The directory is on the same HDD as the WAL.
storeCfg.setWalArchivePath(/wal/archive);cfg.setDataStorageConfiguration(storeCfg);// Starting the node.
Ignite ignite Ignition.start(cfg);#7.3.增加WAL段大小
WAL段的默认大小64MB在高负载情况下可能是低效的因为它导致WAL在段之间频繁切换并且切换/轮转是昂贵的操作。将段大小设置为较大的值最多2GB可能有助于减少切换操作的次数不过这将增加预写日志的占用空间。
具体请参见修改WAL段大小。
#7.4.调整WAL模式
考虑其它WAL模式替代默认模式。每种模式在节点故障时提供不同程度的可靠性并且可靠性与速度成反比即WAL模式越可靠则速度越慢。因此如果具体业务不需要高可靠性那么可以切换到可靠性较低的模式。
具体可以看WAL模式的相关内容。
#7.5.禁用WAL
有时禁用WAL也会改进性能。
#7.6.页面写入限流
Ignite会定期地启动检查点进程以在内存和磁盘间同步脏页面。脏页面是已在内存中更新但是还未写入对应的分区文件的页面更新只是添加到了WAL。这个进程在后台进行对应用没有影响。
但是如果计划进行检查点的脏页面在写入磁盘前被更新它之前的状态会被复制进某个区域叫做检查点缓冲区。如果这个缓冲区溢出那么在检查点处理过程中Ignite会停止所有的更新。因此写入性能可能降为0直至检查点过程完成如下图所示 当检查点处理正在进行中时如果脏页面数达到阈值同样的情况也会发生这会使Ignite强制安排一个新的检查点执行并停止所有的更新操作直到第一个检查点执行完成。
当磁盘较慢或者更新过于频繁时这两种情况都会发生要减少或者防止这样的性能下降可以考虑启用页面写入限流算法。这个算法会在检查点缓冲区填充过快或者脏页面占比过高时将更新操作的性能降低到磁盘的速度。
页面写入限流剖析
要了解更多的信息可以看相关的Wiki页面。
下面的示例显示了如何开启页面写入限流
XMLJavaC#/.NET
IgniteConfiguration cfg new IgniteConfiguration();// Configuring Native Persistence.
DataStorageConfiguration storeCfg new DataStorageConfiguration();// Enabling the writes throttling.
storeCfg.setWriteThrottlingEnabled(true);cfg.setDataStorageConfiguration(storeCfg);
// Starting the node.
Ignite ignite Ignition.start(cfg);#7.7.调整检查点缓冲区大小
前述章节中描述的检查点缓冲区大小是检查点处理的触发器之一。
缓冲区的默认大小是根据数据区大小计算的。
数据区大小默认检查点缓冲区大小 1GBMIN (256 MB, 数据区大小)1GB ~ 8GB数据区大小/4 8GB2GB
默认的缓冲区大小并没有为写密集型应用进行优化因为在大小接近标称值时页面写入限流算法会降低写入的性能因此在正在进行检查点处理时还希望保持写入性能可以考虑增加DataRegionConfiguration.checkpointPageBufferSize并且开启写入限流来阻止性能的下降
XMLJavaC#/.NET
IgniteConfiguration cfg new IgniteConfiguration();// Configuring Native Persistence.
DataStorageConfiguration storeCfg new DataStorageConfiguration();// Enabling the writes throttling.
storeCfg.setWriteThrottlingEnabled(true);// Increasing the buffer size to 1 GB.
storeCfg.getDefaultDataRegionConfiguration().setCheckpointPageBufferSize(1024L * 1024 * 1024);cfg.setDataStorageConfiguration(storeCfg);// Starting the node.
Ignite ignite Ignition.start(cfg);在上例中默认数据区的检查点缓冲区大小配置为1GB。
#7.8.启用直接I/O
通常当应用访问磁盘上的数据时操作系统拿到数据后会将其写入一个文件缓冲区缓存写操作也是同样操作系统首先将数据写入缓存然后才会传输到磁盘要消除这个过程可以打开直接IO这时数据会忽略文件缓冲区缓存直接从磁盘进行读写。
Ignite中的直接I/O插件用于加速检查点进程它的作用是将内存中的脏页面写入磁盘建议将直接IO插件用于写密集型负载环境中。
注意
注意无法专门为WAL文件开启直接I/O但是开启直接I/O可以为WAL文件带来一点好处就是WAL数据不会在操作系统的缓冲区缓存中存储过长时间它会在下一次页面缓存扫描中被刷新依赖于WAL模式然后从页面缓存中删除。
要启用直接I/O插件需要在二进制包中将{IGNITE_HOME}/libs/optional/ignite-direct-io文件夹上移一层至libs/optional/ignite-direct-io文件夹或者也可以作为一个Maven构件引入具体请参见这里的介绍。
通过IGNITE_DIRECT_IO_ENABLED系统属性也可以在运行时启用/禁用该插件。
相关的Wiki页面有更多的细节。
#7.9.购买产品级SSD
限于SSD的操作特性在经历几个小时的高强度写入负载之后Ignite原生持久化的性能可能会下降因此需要考虑购买快速的产品级SSD来保证高性能或者切换到非易失性内存设备比如Intel Optane持久化内存。
#7.10.SSD预留空间
由于SSD预留空间的原因50%使用率的磁盘的随机写性能要好于90%使用率的磁盘因此需要考虑购买高预留空间比率的SSD然后还要确保厂商能提供工具来进行相关的调整。
Intel 3D XPoint
考虑使用3D XPoint驱动器代替常规SSD以避免由SSD级别上的低预留空间设置和恒定垃圾收集造成的瓶颈。具体可以看这里。 配置缓存
#1.缓存配置
本章节介绍如何设定缓存的配置参数缓存创建之后这些参数将无法修改。
Ignite中的缓存和表
缓存驱动的配置方式是配置选项之一还可以使用CREATE TABLE这样的标准SQL命令来配置缓存/表具体请参见键-值缓存与SQL表章节的内容以了解Ignite中缓存和表的关系。
#1.1.配置示例
下面是缓存配置的示例
XMLJavaC#/.NETSQL
CacheConfiguration cacheCfg new CacheConfiguration(myCache);cacheCfg.setCacheMode(CacheMode.PARTITIONED);
cacheCfg.setBackups(2);
cacheCfg.setRebalanceMode(CacheRebalanceMode.SYNC);
cacheCfg.setWriteSynchronizationMode(CacheWriteSynchronizationMode.FULL_SYNC);
cacheCfg.setPartitionLossPolicy(PartitionLossPolicy.READ_ONLY_SAFE);IgniteConfiguration cfg new IgniteConfiguration();
cfg.setCacheConfiguration(cacheCfg);// Start a node.
Ignition.start(cfg);完整的参数列表请参见CacheConfiguration的javadoc。
参数描述默认值name缓存名cacheMode该参数定义了数据在集群中的分布方式。在默认的PARTITIONED模式中整体数据集被拆分为分区然后所有的分区再以平衡的方式分布于相关的节点上。而在REPLICATED模式中所有的数据在所有的节点上都复制一份具体请参见分区/复制模式章节的介绍。PARTITIONEDwriteSynchronizationMode写同步模式具体请参见配置分区备份章节的内容。PRIMARY_SYNCrebalanceMode该参数控制再平衡过程的执行方式。可选值包括SYNC所有缓存操作都会被阻塞直到再平衡结束ASYNC再平衡在后台执行NONE再平衡不会被触发。ASYNCbackups缓存的备份分区数量。0partitionLossPolicy分区丢失策略IGNOREreadFromBackup如果本地的备份分区可用则从备份分区读取数据而不是向主分区请求数据可能位于远程节点。truequeryPrallelism单节点在缓存上执行SQL查询的线程数具体请参见性能优化的查询并行度相关章节的内容。1
#1.2.缓存模板
缓存模板是在集群中注册的CacheConfiguration实例然后用作后续创建新缓存或SQL表的基础一个从模板创建的缓存或表会继承该模板的所有属性。
当使用CREATE TABLE命令建表时模板非常有用因为该命令并不支持所有的缓存参数。
提示
当前CREATE TABLE和REST命令支持模板。
创建模板时需要定义一个缓存配置然后将其加入Ignite实例中如下所示。如果希望在XML配置文件中定义缓存模板需要在模板名后面加一个*号这个是用于标示该配置是一个模板而不是实际的缓存。
XMLJavaC#/.NET
IgniteConfiguration igniteCfg new IgniteConfiguration();try (Ignite ignite Ignition.start(igniteCfg)) {CacheConfiguration cacheCfg new CacheConfiguration(myCacheTemplate);cacheCfg.setBackups(2);cacheCfg.setCacheMode(CacheMode.PARTITIONED);// Register the cache templateignite.addCacheConfiguration(cacheCfg);
}缓存模板在集群中注册之后就可以用相同的配置创建其他缓存了。
#2.配置分区备份
Ignite默认为每个分区持有一个副本整个数据集的一个副本这时如果一个或者多个节点故障存储于这些节点上的分区就会丢失为了避免这种情况可以配置Ignite维护分区的备份副本。
提示
备份默认是禁用的。
备份副本是缓存表级的配置如果配置了2个备份副本集群会为每个分区维护3个副本。其中一个分区称为主分区其他2个称为备份分区。扩展来说具有主分区的节点称为该分区中存储的数据的主节点备份分区对应的节点称为备份节点。
当某些数据对应的主分区所在的节点离开集群Ignite会触发分区映射交换PME过程PME会标记这些数据对应的某个已配置的备份分区为主分区。
备份分区增加了数据的可用性和某些场景的数据读取速度因为如果本地节点的备份分区可用Ignite会从备份分区读取数据这是默认的行为但是可以禁用。但是增加了内存的消耗或者持久化存储的大小如果开启。
#2.1.配置备份
在缓存配置中配置backups属性可以配置备份副本的数量。
XMLJavaC#/.NET
CacheConfiguration cacheCfg new CacheConfiguration();cacheCfg.setName(cacheName);
cacheCfg.setCacheMode(CacheMode.PARTITIONED);
cacheCfg.setBackups(1);IgniteConfiguration cfg new IgniteConfiguration();cfg.setCacheConfiguration(cacheCfg);// Start the node.
Ignite ignite Ignition.start(cfg);#2.2.同步和异步备份
通过指定写同步模式可以配置更新在主备副本之间是同步模式还是异步模式如下所示
XMLJavaC#/.NET
CacheConfiguration cacheCfg new CacheConfiguration();cacheCfg.setName(cacheName);
cacheCfg.setBackups(1);
cacheCfg.setWriteSynchronizationMode(CacheWriteSynchronizationMode.FULL_SYNC);
IgniteConfiguration cfg new IgniteConfiguration();cfg.setCacheConfiguration(cacheCfg);// Start the node.
Ignition.start(cfg);写同步模式有如下的可选值
值描述FULL_SYNC客户端节点会等待所有相关的远程节点主和备写入或者提交完成。FULL_ASYNC客户端节点不会等待来自相关节点的响应这时远程节点会在缓存写入或者事务提交方法完成之后稍晚些收到状态更新。PRIMARY_SYNC默认模式客户端节点会等待主节点的写入或者提交完成但是不会等待备份的更新。
#3.分区丢失策略
在整个集群的生命周期中由于分区的主节点和备份节点的故障可能出现分区丢失的情况这会导致部分数据丢失需要根据场景进行处理。
当一个分区的主副本和所有备份副本均不在线即该分区的主节点和备份节点全部故障时该分区将丢失。这意味着对于给定的缓存能承受的节点故障数不能超过缓存备份数。
当集群拓扑发生变更时Ignite会检查变更是否导致分区丢失并根据配置的分区丢失策略和基线自动调整设置允许或禁止对缓存进行操作具体请参见下一章节的介绍。
对于纯内存缓存当分区丢失时除非数据重新加载否则分区中的数据将无法恢复。对于持久化缓存数据不会物理丢失因为它已被持久化到磁盘上。当发生故障或断开连接的节点回到集群时重启后将从磁盘上加载数据。这时需要重置丢失的分区的状态才能继续使用数据具体请参见处理分区丢失章节的介绍。
#3.1.配置分区丢失策略
Ignite支持以下的分区丢失策略
策略描述IGNORE分区丢失将被忽略。集群将丢失的分区视为空分区。当请求该分区的数据时将返回空值就好像数据从不存在一样。此策略只能在纯内存集群中使用且是默认值这个模式中启用了基线自动调整且超时为0。在所有其他配置集群中只要有一个数据区开启了持久化中IGNORE策略都会被READ_WRITE_SAFE替代即使在缓存配置中显式指定也不行。READ_WRITE_SAFE缓存丢失分区的读写尝试都会抛出异常但是在线分区的读写是正常的。READ_ONLY_SAFE缓存处于只读状态缓存的写操作会抛出异常丢失分区的读操作也会抛出异常具体请参见处理分区丢失章节的介绍。
分区丢失策略是缓存级的配置。
XMLJava
CacheConfiguration cacheCfg new CacheConfiguration(myCache);cacheCfg.setPartitionLossPolicy(PartitionLossPolicy.READ_ONLY_SAFE);#3.2.监听分区丢失事件
当发生分区丢失时可以监听EVT_CACHE_REBALANCE_PART_DATA_LOST事件的通知。每个丢失的分区都会触发该事件其包含了丢失的分区号以及持有该分区的节点ID。只有使用READ_WRITE_SAFE或READ_ONLY_SAFE策略时才会触发分区丢失事件。
首先需要在集群的配置中启用事件具体参见启用事件的介绍。
Ignite ignite Ignition.start();IgnitePredicateEvent locLsnr evt - {CacheRebalancingEvent cacheEvt (CacheRebalancingEvent) evt;int lostPart cacheEvt.partition();ClusterNode node cacheEvt.discoveryNode();System.out.println(lostPart);return true; // Continue listening.
};ignite.events().localListen(locLsnr, EventType.EVT_CACHE_REBALANCE_PART_DATA_LOST);关于其他和分区再平衡有关的事件可以参见分区再平衡事件章节的介绍。
#3.3.处理分区丢失
如果数据没有物理丢失可以将该节点恢复然后重置丢失分区的状态这样就可以继续处理该数据通过控制脚本或者在特定的缓存上调用Ignite.resetLostPartitions(cacheNames)可以重置分区的状态。
ignite.resetLostPartitions(Arrays.asList(myCache));控制脚本命令为
control.sh --cache reset_lost_partitions myCache如果不重置丢失的分区根据缓存策略的配置从丢失分区的读写操作可能会抛出CacheException通过分析上层的触发原因可以检查该异常是否由分区状态导致
IgniteCacheInteger, Integer cache ignite.cache(myCache);try {Integer value cache.get(3);System.out.println(value);
} catch (CacheException e) {if (e.getCause() instanceof CacheInvalidStateException) {System.out.println(e.getCause().getMessage());} else {e.printStackTrace();}
}通过IgniteCache.lostPartitions()可以拿到缓存丢失分区的列表
IgniteCacheInteger, String cache ignite.cache(myCache);cache.lostPartitions();#3.4.从丢失分区中恢复
下面的章节会介绍根据集群的不同配置如何从分区丢失中恢复。
#3.4.1.IGNORE策略的纯内存集群
在此配置中该IGNORE策略仅适用于启用基线自动调整且超时为0的场景这也是纯内存集群的默认设置。这时将忽略分区丢失缓存仍然可以操作丢失的分区会被视为空分区。
当禁用基线自动调整或超时时间大于0时IGNORE策略会被替换为READ_WRITE_SAFE。
#3.4.2.READ_WRITE_SAFE或READ_ONLY_SAFE策略的纯内存集群
重置丢失的分区之前对缓存的操作将被阻止。重置后缓存可以继续使用但是数据将丢失。
禁用基线自动调整或超时大于0时必须在重置丢失的分区之前将节点每个分区至少一个分区所有者恢复到基线拓扑。否则Ignite.resetLostPartitions(cacheNames)会抛出一个消息为Cannot reset lost partitions because no baseline nodes are online [cachesomeCahe, partitionsomeLostPart]的ClusterTopologyCheckedException表明无法安全恢复。如果由于某种原因例如硬件故障而无法恢复节点需要在重置丢失的分区之前将它们从基线拓扑中手动删除。
#3.4.3.开启持久化的集群
如果所有的数据区都开启了持久化没有纯内存数据区那么有两种从丢失分区中恢复的方式只要数据没有物理损坏
让所有的节点返回到基线重置丢失的分区所有的缓存调用Ignite.resetLostPartitions(…)。
或者
停止所有的节点启动包括故障节点在内的所有节点然后激活集群。
如果某些节点无法返回在尝试重置丢失分区状态前需要将他们从基线拓扑中删除。
#3.4.4.同时有纯内存和持久化缓存的集群
如果集群同时有纯内存的数据区和持久化的数据区那么纯内存的缓存会和配置为READ_WRITE_SAFE的纯内存集群的处理方式一致而持久化的缓存会和持久化的集群的处理方式一致。
#4.原子化模式
缓存默认仅支持原子操作而批量操作例如putAll()或removeAll()则按顺序单独执行写入和删除。但是也可以启用事务支持并将一个或多个缓存操作可能对应一个或者多个键分组为单个原子事务。这些操作在没有任何其他交叉操作的情况下执行或全部成功或全部失败没有部分成功的状态。
要启用缓存的事务支持需要将缓存配置中的atomicityMode参数设置为TRANSACTIONAL。
警告
如果在一个缓存组中配置了多个缓存这些缓存的原子化模式应全部相同不能有的是TRANSACTIONAL有的是ATOMIC。
Ignite支持3种原子化模式如下表所示
原子化模式描述ATOMIC默认模式所有操作都会原子化地执行一次一个不支持事务。ATOMIC模式通过避免事务锁提供了最好的性能同时为每个单个操作提供了数据原子性和一致性。比如putAll(…)以及removeAll(…)方法这样的批量操作并不以事务方式执行可能部分失败如果发生了这种情况会抛出CachePartialUpdateException异常其中包含了更新失败的键列表。TRANSACTIONAL在键-值API层面开启了符合ACID的事务支持但是SQL不支持事务。该模式的事务支持不同的并发模型和隔离级别。如果确实需要符合ACID操作才建议开启这个模式因为事务会导致性能下降。具体请参见执行事务。TRANSACTIONAL_SNAPSHOT多版本并发控制MVCC的试验性实现。其同时支持键-值事务和SQL事务更多的信息以及限制请参见多版本并发控制。**注意**MVCC实现目前还处于测试阶段不建议用于生产。
可以在缓存配置中为缓存开启事务支持
XMLJavaC#/.NET
CacheConfiguration cacheCfg new CacheConfiguration();cacheCfg.setName(cacheName);cacheCfg.setAtomicityMode(CacheAtomicityMode.TRANSACTIONAL);IgniteConfiguration cfg new IgniteConfiguration();cfg.setCacheConfiguration(cacheCfg);// Optional transaction configuration. Configure TM lookup here.
TransactionConfiguration txCfg new TransactionConfiguration();cfg.setTransactionConfiguration(txCfg);// Start a node
Ignition.start(cfg);#5.过期策略
#5.1.概述
过期策略指定了在缓存条目过期之前必须经过的时间量时间可以从创建、最后访问或者修改时间开始计算。
根据内存配置过期策略将从内存或磁盘上删除条目
内存模式数据仅保存在内存中过期的条目会完全从内存中清除内存Ignite持久化过期的条目会完全从内存和磁盘上删除注意过期策略会从磁盘上的分区文件中删除数据但是不会释放空间该空间会在后续的数据写入中重用内存外部存储过期的条目仅仅从内存Ignite中删除外部存储RDBMS、NoSQL以及其它数据库中的数据会保持不变内存交换空间过期的条目会同时从内存和交换空间中删除。
过期策略可以通过任何标准的javax.cache.expiry.ExpiryPolicy实现或自定义实现进行设置
#5.2.配置
下面是过期策略的配置示例
XMLJavaC#/.NET
CacheConfigurationInteger, String cfg new CacheConfigurationInteger, String();
cfg.setName(myCache);
cfg.setExpiryPolicyFactory(CreatedExpiryPolicy.factoryOf(Duration.FIVE_MINUTES));也可以为单独的缓存操作配置或者修改过期策略该策略会影响返回的缓存实例上调用的每个操作
CacheConfigurationInteger, String cacheCfg new CacheConfigurationInteger, String(myCache);ignite.createCache(cacheCfg);IgniteCache cache ignite.cache(myCache).withExpiryPolicy(new CreatedExpiryPolicy(new Duration(TimeUnit.MINUTES, 5)));// if the cache does not contain key 1, the entry will expire after 5 minutes
cache.put(1, first value);#5.3.Eager TTL热生存时间
过期的条目从缓存中删除既可以马上删除也可以在缓存操作对其访问时再删除。只要有一个缓存启用了热生存时间Ignite就会创建一个线程在后台清理过期的数据。
如果该属性配置为false过期的条目不会被马上删除而是在执行缓存操作时由执行该操作的线程将其删除。
热生存时间可以通过CacheConfiguration.eagerTtl属性启用或者禁用默认值是true。
XMLJavaC#/.NET
CacheConfigurationInteger, String cfg new CacheConfigurationInteger, String();
cfg.setName(myCache);cfg.setEagerTtl(true);#6.堆内缓存
Ignite在Java堆外部使用堆外内存来分配数据区但是可以通过配置CacheConfiguration.setOnheapCacheEnabled(true)来开启堆内缓存。
对于在使用二进制形式的缓存条目或调用缓存条目的反序列化的服务器节点上进行大量缓存读取的场景堆内缓存很有用。例如当分布式计算或部署的服务从缓存中获取数据进行进一步处理时可能会发生这种情况。
XMLJavaC#/.NET
CacheConfiguration cfg new CacheConfiguration();
cfg.setName(myCache);
cfg.setOnheapCacheEnabled(true);#6.1.配置退出策略
启用堆内缓存后可以使用堆内缓存退出策略来管理不断增长的堆内缓存。
退出策略控制缓存的堆内存中可以存储的最大数据量当到达最大值后条目会从Java堆中退出。
提示
堆内退出策略只会从Java堆中删除缓存条目堆外数据区中存储的数据不受影响。
退出策略支持基于批次的退出和基于内存大小限制的退出。如果开启了基于批次的退出那么当缓存的数量比缓存最大值多出batchSize个条目时退出就开始了这时batchSize个条目就会被退出。如果开启了基于内存大小限制的退出那么当缓存条目的大小字节数大于最大值时退出就会被触发。
注意
只有未配置最大内存限制时才会支持基于批次的退出。
Ignite中退出策略是可插拔的可以通过EvictionPolicy接口进行控制退出策略的实现定义了从堆内缓存选择待退出条目的算法然后当缓存发生改变时就会收到通知。
#6.1.1.最近最少使用LRU
LRU退出策略基于最近最少使用算法它会确保最近最少使用的数据即最久没有被访问的数据会被首先退出。
注意
LRU退出策略适用于堆内缓存的大多数使用场景不确定时可以优先使用。
这个退出策略通过CacheConfiguration进行配置支持基于批次的退出以及基于内存大小限制的退出如下所示
XMLJavaC#/.NET
CacheConfiguration cacheCfg new CacheConfiguration();cacheCfg.setName(cacheName);// Enabling on-heap caching for this distributed cache.
cacheCfg.setOnheapCacheEnabled(true);// Set the maximum cache size to 1 million (default is 100,000).
cacheCfg.setEvictionPolicyFactory(() - new LruEvictionPolicy(1000000));IgniteConfiguration cfg new IgniteConfiguration();cfg.setCacheConfiguration(cacheCfg);#6.1.2.先进先出FIFO
FIFO退出策略基于先进先出FIFO算法它确保缓存中保存时间最久的数据会被首先退出它与LruEvictionPolicy不同因为它忽略了数据的访问顺序。
这个策略通过CacheConfiguration进行配置支持基于批次的退出以及基于内存大小限制的退出。
XMLJavaC#/.NET
CacheConfiguration cacheCfg new CacheConfiguration();cacheCfg.setName(cacheName);// Enabling on-heap caching for this distributed cache.
cacheCfg.setOnheapCacheEnabled(true);// Set the maximum cache size to 1 million (default is 100,000).
cacheCfg.setEvictionPolicyFactory(() - new FifoEvictionPolicy(1000000));IgniteConfiguration cfg new IgniteConfiguration();cfg.setCacheConfiguration(cacheCfg);#6.1.3.有序
有序退出策略和FIFO退出策略很像不同点在于通过默认或者用户定义的比较器定义了数据的顺序然后确保最小的数据即排序数值最小的数据会被退出。
默认的比较器用缓存条目的键作为比较器它要求键必须实现Comparable接口。也可以提供自定义的比较器实现可以通过键值或者两者都用来进行条目的比较。
这个策略通过CacheConfiguration进行配置支持基于批次的退出以及基于内存大小限制的退出。
XMLJava
CacheConfiguration cacheCfg new CacheConfiguration();cacheCfg.setName(cacheName);// Enabling on-heap caching for this distributed cache.
cacheCfg.setOnheapCacheEnabled(true);// Set the maximum cache size to 1 million (default is 100,000).
cacheCfg.setEvictionPolicyFactory(() - new SortedEvictionPolicy(1000000));IgniteConfiguration cfg new IgniteConfiguration();cfg.setCacheConfiguration(cacheCfg);#7.缓存组
对于集群中的缓存来说总有一个开销即缓存被拆分为分区后其状态必须在每个集群节点上进行跟踪以满足系统的需要。
如果开启了Ignite的原生持久化那么对于每个分区来说都会在磁盘上打开一个文件进行读写因此如果有更多的缓存和分区
分区映射就会占用更多的Java堆每个缓存都有自己的分区映射新节点加入集群就会花费更多的时间节点离开集群也会因为再平衡花费更多的时间打开中的分区文件就会更多从而影响检查点的性能。
通常如果只有几十甚至几百个缓存时不用担心这些问题但是如果增长到上千时这类问题就会凸显。
要避免这个影响可以考虑使用缓存组一个组内的缓存会共享各种内部数据结构比如上面提到的分区映射这样会提高拓扑事件处理的效率以及降低整体的内存使用量。注意从API上来看缓存是不是组的一部分并没有什么区别。
通过配置CacheConfiguration的groupName属性可以创建一个缓存组示例如下
XMLJavaC#/.NET
// Defining cluster configuration.
IgniteConfiguration cfg new IgniteConfiguration();// Defining Person cache configuration.
CacheConfigurationInteger, Person personCfg new CacheConfigurationInteger, Person(Person);personCfg.setBackups(1);// Group the cache belongs to.
personCfg.setGroupName(group1);// Defining Organization cache configuration.
CacheConfiguration orgCfg new CacheConfiguration(Organization);orgCfg.setBackups(1);// Group the cache belongs to.
orgCfg.setGroupName(group1);cfg.setCacheConfiguration(personCfg, orgCfg);// Starting the node.
Ignition.start(cfg);在上面的示例中Person和Organization缓存都属于group1。
如何区分键-值对
如果将缓存分配给一个缓存组则其数据存储在共享分区的内部结构中。写入缓存的每个键都会附加其所属的缓存的唯一ID。该ID是从缓存名派生的。这些都是透明的并允许将不同缓存的数据存储在相同的分区和B树结构中。
对缓存进行分组的原因很简单如果决定对1000个缓存进行分组则存储分区数据、分区映射和打开分区文件的结构将减少为原来的千分之一。
缓存组是否应一直使用
虽然有这么多的好处但是它可能影响读操作和索引的性能这是由于所有的数据和索引都混合在一个共享的数据结构分区映射、B树中查询的时间变长导致的。 因此如果集群有数十个和数百个节点和缓存并且由于内部结构、检查点性能下降和/或节点到集群的连接速度较慢而遇到Java堆使用增加的情况可以考虑使用缓存组。
#8.近缓存
近缓存是一种本地缓存用于在本地节点上存储最近或最常访问的数据。假设应用启动了一个客户端节点并定期查询数据例如国家/地区代码。因为客户端节点不存储数据所以这些查询总是从远程节点获取数据。这时可以配置近缓存以在应用运行时将国家/地区代码保留在本地节点上这样可以提高性能。
近缓存为特定的常规缓存配置并且仅保留该缓存的数据。
近缓存将数据存储在堆内存中可以为近缓存条目配置缓存的最大值和退出策略。
提示
近缓存是完全事务性的并且每当服务端节点上的数据更改时它们都会自动更新或失效。
#8.1.配置近缓存
可以在缓存配置中为某个缓存配置近缓存
XMLJavaC#/.NET
// Create a near-cache configuration for myCache.
NearCacheConfigurationInteger, Integer nearCfg new NearCacheConfiguration();// Use LRU eviction policy to automatically evict entries
// from near-cache whenever it reaches 100_000 entries
nearCfg.setNearEvictionPolicyFactory(new LruEvictionPolicyFactory(100_000));CacheConfigurationInteger, Integer cacheCfg new CacheConfigurationInteger, Integer(myCache);cacheCfg.setNearConfiguration(nearCfg);// Create a distributed cache on server nodes
IgniteCacheInteger, Integer cache ignite.getOrCreateCache(cacheCfg);以这种方式配置后就在从底层缓存请求数据的任何节点包括服务端节点和客户端节点上创建近缓存。如以下示例所示当拿到缓存的实例时数据将通过近缓存获得
IgniteCacheInteger, Integer cache ignite.cache(myCache);int value cache.get(1);CacheConfiguration中与近缓存有关的大部分参数都会继承于底层缓存的配置比如如果底层缓存有一个ExpiryPolicy配置近缓存中的条目也会基于同样的策略。
下表中列出的参数是不会从底层配置中继承的
参数描述默认值nearEvictionPolicy近缓存退出策略无nearStartSize近缓存初始大小可持有的条目数375,000
#8.2.客户端节点动态创建近缓存
从客户端节点向尚未配置近缓存的缓存发出请求时可以为该缓存动态创建近缓存通过在客户端本地存储“热”数据来提高性能。此缓存仅在创建它的节点上生效。
为此创建一个近缓存配置并将其作为参数传递给获取缓存实例的方法
JavaC#/.NET
// Create a near-cache configuration
NearCacheConfigurationInteger, String nearCfg new NearCacheConfiguration();// Use LRU eviction policy to automatically evict entries
// from near-cache, whenever it reaches 100_000 in size.
nearCfg.setNearEvictionPolicyFactory(new LruEvictionPolicyFactory(100_000));// get the cache named myCache and create a near cache for it
IgniteCacheInteger, String cache ignite.getOrCreateNearCache(myCache, nearCfg);String value cache.get(1);数据再平衡
#1.数据再平衡
#1.1.概述
当一个新节点加入集群时部分分区会被分配至新的节点以使整个集群的数据保持平均分布这个过程称为数据再平衡。
如果现有节点永久离开集群并且未配置备份则会丢失此节点上存储的分区。配置备份后丢失分区的备份副本之一将成为主分区并开始再平衡过程。
警告
数据再平衡由基线拓扑的变化触发。在纯内存集群中默认行为是在节点离开或加入集群时基线拓扑自动更改立即开始再平衡。在开启持久化的集群中默认必须手动更改基线拓扑或者在启用基线自动调整后可以自动更基线拓扑。
再平衡是缓存级的配置。
#1.2.配置再平衡模式
Ignite支持同步和异步的再平衡在同步模式中再平衡结束前缓存的任何操作都会被阻塞。在异步模式中再平衡过程以异步的模式执行也可以为某个缓存禁用再平衡。
如果要修改再平衡模式可以在缓存配置中配置如下的值
SYNC同步再平衡模式再平衡结束前缓存的任何操作都会被阻塞ASYNC异步再平衡模式缓存直接可用然后在后台会从其它节点加载所有必要的数据NONE该模式下不会发生再平衡这意味着要么在访问数据时从持久化存储载入要么数据被显式地填充。
XMLJavaC#/.NET
IgniteConfiguration cfg new IgniteConfiguration();CacheConfiguration cacheCfg new CacheConfiguration(mycache);cacheCfg.setRebalanceMode(CacheRebalanceMode.SYNC);cfg.setCacheConfiguration(cacheCfg);// Start a node.
Ignite ignite Ignition.start(cfg);#1.3.配置再平衡线程池
默认一个节点只会有一个线程用于再平衡这意味着在一个特定的时间点只有一个线程用于从一个节点到另一节点传输批量数据或者处理来自远端的批量数据。
可以从系统线程池中拿到更多的线程数用于再平衡。每当节点需要将一批数据发送到远端节点或需要处理来自远端节点的一批数据时都会从池中获取系统线程批次处理完成后该线程会被释放。
XMLJava
IgniteConfiguration cfg new IgniteConfiguration();cfg.setRebalanceThreadPoolSize(4);CacheConfiguration cacheCfg new CacheConfiguration(mycache);
cfg.setCacheConfiguration(cacheCfg);// Start a node.
Ignite ignite Ignition.start(cfg);警告
在内部系统线程池广泛用于和缓存有关的所有操作putget等SQL引擎和其它模块因此将再平衡线程池设置为一个很大的值会显著提高再平衡的性能但是会影响应用的吞吐量。
#1.4.再平衡消息限流
当数据从一个节点传输到另一个节点时整个数据集会被拆分为多个批次然后将每一个批次作为一个单独的消息进行发送批次的大小和节点在消息之间的等待时间都是可以配置的。
XMLJavaC#/.NET
IgniteConfiguration cfg new IgniteConfiguration();CacheConfiguration cacheCfg new CacheConfiguration(mycache);cfg.setRebalanceBatchSize(2 * 1024 * 1024);
cfg.setRebalanceThrottle(100);cfg.setCacheConfiguration(cacheCfg);// Start a node.
Ignite ignite Ignition.start(cfg);#1.5.其他配置
下表列出了CacheConfiguration中和再平衡有关的属性
属性描述默认值rebalanceBatchSize单个再平衡消息的大小字节在每个节点再平衡算法会将数据拆分为多个批次然后再将其发送给其他节点。512KBrebalanceDelay当节点加入或者离开集群时再平衡过程启动的延迟时间毫秒如果打算在节点离开拓扑后重启节点或者打算在同时/一个个启动多个节点的过程中所有节点都启动完成之前不进行重新分区或者再平衡也可以推迟。0无延迟rebalanceOrder完成再平衡的顺序只有SYNC和ASYNC再平衡模式的缓存才可以将再平衡顺序设置为非0值具有更小值的缓存再平衡会被首先完成再平衡默认是无序的。0rebalanceThrottle请参见再平衡消息限流0限流禁用rebalanceTimeout节点间交换再平衡消息的挂起超时。10秒
#1.6.再平衡过程监控
通过JMX可以监控缓存的再平衡过程。