Ignite 数据网格快速学习(二)
来源:互联网 发布:剑灵洪门崛起mac 编辑:程序博客网 时间:2024/06/03 12:49
1. IgniteDataStream数据流
后面会单独的做数据存储的快速学习章节,这里只是带着体验下数据流网格
public static void main(String[] args) { checkMinMemory(MIN_MEMORY); try(Ignite ignite = Ignition.start("examples/config/example-ignite.xml")){ try(IgniteCache<Integer,Integer> cache = ignite.getOrCreateCache(CACHE_NAME)){ try(IgniteDataStreamer<Object, Object> stream = ignite.dataStreamer(CACHE_NAME)){ stream.perNodeBufferSize(1024); stream.perNodeParallelOperations(8); for(int i = 1 ; i <= ENTRY_COUNT ;i++){ stream.addData(i, i); if(i % 100000 == 0){ System.out.println(i + "条数据已经被缓存!"); } } } } } }
代码并不长,首行只是保护代码,避免内存不足问题。
1.1 IgniteDataStream
代码中,我们通过Ignite#dataStream(cacheName)方法,获取与给定的缓存名称关联的IgniteDataStream数据流的新实例。数据流对象负责将外部数据加载到内存数据网格中。它通过适当的缓冲更新、正确地将键映射到负责该数据的存储的节点,以确保尽可能少的数据移动和最优的网络和内存利用率。注意:streamer将通过多个内部线程并发地流数据,所以数据可能以不同于我们添加数据的顺序而进入远程节点。IgniteDataStream并不是唯一的将数据加载进内存的方式。你可以使用IgniteCache#loadCache(IgniteBiPredicate, Object...)方法来讲数据从底层数据存储加载到内存里。你还可以使用标准的put()/putAll()方法.但时不会比通过数据流的性能来的快。数据可以从底层数据存储需求,添加访问时不需要显式的数据添加步骤。配置:(直接放在方法里讲吧)
1.2 添加数据 addData
2. 持续查询 ContinuousQuery
public static void main(String[] args) { try(Ignite ignite = Ignition.start("examples/config/example-ignite.xml")){ try(IgniteCache<Integer,Integer> cache = ignite.getOrCreateCache("DEFAULT_CACHE")){ for(int i = -10;i < 10;i++){ cache.put(i, i); } ContinuousQuery<Integer, Integer> continuousQuery = new ContinuousQuery<>(); //第一步:设置初始化查询(值取偶数) continuousQuery.setInitialQuery(new ScanQuery<>(new IgniteBiPredicate<Integer, Integer>(){ @Override public boolean apply(Integer key, Integer val) { if(key > 0 && key % 2 == 0){ return true; } return false; } })); //第二步:远程过滤器(只返回大于0的数值) continuousQuery.setRemoteFilterFactory(new Factory<CacheEntryEventFilter<Integer, Integer>>(){ @Override public CacheEntryEventFilter<Integer, Integer> create() { return new CacheEntryEventSerializableFilter<Integer, Integer>() { @Override public boolean evaluate(CacheEntryEvent<? extends Integer, ? extends Integer> evts) throws CacheEntryListenerException { if(evts.getKey() > 0){ return true; } return false; } }; } }); //第三步:设置本地监听器 continuousQuery.setLocalListener(new CacheEntryUpdatedListener<Integer, Integer>() { @Override public void onUpdated(Iterable<CacheEntryEvent<? extends Integer, ? extends Integer>> evts) throws CacheEntryListenerException { for (CacheEntryEvent<? extends Integer, ? extends Integer> entry : evts) { Integer key = entry.getKey(); Integer value = entry.getValue(); System.out.println("此时,键 : " + key + "的值变为了: " + value); } } }); //第四步:执行查询 QueryCursor<Entry<Integer, Integer>> crusor = cache.query(continuousQuery); for (Entry<Integer, Integer> entry : crusor) { System.out.println("初始化时候:键: " + entry.getKey() + "的值是: " + entry.getValue() ); } //因为数据的更改以及添加可以影响本地监听器,所以我们做一次测试 for (int i = -10; i < 20; i++) cache.put(i, i + 10); } } }
输出打印
初始化时候:键: 2的值是: 2初始化时候:键: 4的值是: 4初始化时候:键: 6的值是: 6初始化时候:键: 8的值是: 8此时,键 : 1的值变为了: 11此时,键 : 2的值变为了: 12此时,键 : 3的值变为了: 13此时,键 : 4的值变为了: 14此时,键 : 5的值变为了: 15此时,键 : 6的值变为了: 16此时,键 : 7的值变为了: 17此时,键 : 8的值变为了: 18此时,键 : 9的值变为了: 19此时,键 : 10的值变为了: 20此时,键 : 11的值变为了: 21此时,键 : 12的值变为了: 22此时,键 : 13的值变为了: 23此时,键 : 14的值变为了: 24此时,键 : 15的值变为了: 25此时,键 : 16的值变为了: 26此时,键 : 17的值变为了: 27此时,键 : 18的值变为了: 28此时,键 : 19的值变为了: 29
除了apidiamante之外,其他代码难度并不高。首先对获取到的缓存,插入键为-10~10的键值对。然后我们初始化一个持续查询的ContiousQuery对象。对于该Query子对象,我们重点需要做的就是三步: 1.初始化查询:即在query(Query query)方法执行时候率先执行的查询操作,我们从输出打印可看到,我们存储了-10~9的数字,但是初始化时候返回的却只有2,4,6,8这四行,说明这个操作时在查询时候执行的。 2.远程过滤器:通过代码可以看到,我们最后,对-10到19做了一次插入,由于-10~9是原本就存在的,那么既然被修改了,就需要给我们这边监听器发送报告,但是日志显示,负数的-10~-1都没有,因为被这个远程过滤器过滤了。 3.本地监听:这个比较易懂,当我们所监听的数据被新建或者修改了,且通过了远程监听器的谓词过滤,那么就会输出到本地监听器上。你可以想象成rabbitMQ中,我们监听自己所感兴趣的队列是一个道理。
2.1 setInitialQuery 设置初始化查询
这个方法会在持续监听器被注册之前就执行,因此,它并不会触发本地监听。在这里需要传给入参一个Query对象。Query查询在下面会讲,这里先不多言。可以参考Query接口的实现类,共有多种查询对象。我们在这里使用的是Query接口的实现类:ScanQuery.它对缓存条目进行扫描查询。如果没有设置谓词,将接受所有的条目。
2.2 setRemoteFilterFactory 设置远程拦截器工厂
首先呢,其实这里是有一个简便方法的,就是setRemoteFilter方法,但是它已经过时了,因此暂时不讲它了。该方法是设置可选的键值过滤工厂。这个工厂生产过滤器在缓存条目被发送到主节点之前都被调用。该方法的入参需要一个Factory的对象,匿名实现它,我们需要实现其create方法
@Override public CacheEntryEventFilter<Integer, String> create() {}
这个create方法的目的是创建一个过滤器,而这个过滤器也是一个接口,即:CacheEntryEventFilter。需要我们自己实现它ignite有一个注解,叫做@IgniteAsyncCallback,上面我们自己实现的CacheEntryEventFilter实现类,如果我们以这个注解对该类注解,那么就是异步操作。否则就是同步操作,下一个小节我们会将异步持续查询。
2.3 setLocalListener 本地监听
设置本地回调。这个回调只在接收新更新时在本地节点中调用。如果本地监听器被@IgniteAsyncCallback注解,那么他会异步执行。异步回调池可以以IgniteConfiguration#getAsyncCallbackPoolSize()方法查看。
3. 异步持续查询
基本步骤与上面类似,只做部分讲解
public static void main(String[] args) { try(Ignite ignite = Ignition.start("examples/config/example-ignite.xml")){ try(IgniteCache<Integer,Integer> cache = ignite.getOrCreateCache("DEFAULT_CACHE")){ for(int i = -10;i < 10;i++){ cache.put(i, i); } ContinuousQuery<Integer, Integer> continuousQuery = new ContinuousQuery<>(); int asyncCallbackPoolSize = ignite.configuration().getAsyncCallbackPoolSize(); System.out.println("异步池的大小为:" + asyncCallbackPoolSize); //step1:初始化查询(全部都查询) continuousQuery.setInitialQuery(new ScanQuery<>(new IgniteBiPredicate<Integer, Integer>() { @Override public boolean apply(Integer key, Integer val) { return true; } })); //step2:远程过滤器 continuousQuery.setRemoteFilterFactory( new Factory<CacheEntryEventFilter<Integer, Integer>>(){ @Override public CacheEntryEventFilter<Integer, Integer> create() { return new MyCacheEntryEventFilter(); } }); //step3:本地监听器 continuousQuery.setLocalListener(new MyCacheEntryUpdatedListener()); QueryCursor<Entry<Integer, Integer>> cursor = cache.query(continuousQuery); for (Entry<Integer, Integer> entry : cursor) { System.out.println("初始化查询结果:key : " + entry.getKey() + " 值:" + entry.getValue()); } //做数据修改 for(int i = -10;i < 10;i++){ cache.put(i, i + 10); } } } }
@IgniteAsyncCallback public static class MyCacheEntryEventFilter implements CacheEntryEventFilter<Integer,Integer>{ @Override public boolean evaluate(CacheEntryEvent<? extends Integer, ? extends Integer> evts) throws CacheEntryListenerException { if(evts.getKey() > 0){ return true; }else{ return false; } } }
@IgniteAsyncCallback public static class MyCacheEntryUpdatedListener implements CacheEntryUpdatedListener<Integer,Integer>{ @Override public void onUpdated(Iterable<CacheEntryEvent<? extends Integer, ? extends Integer>> evts) throws CacheEntryListenerException { for (CacheEntryEvent<? extends Integer, ? extends Integer> entry : evts) { System.out.println("更改后,key:" + entry.getKey() + "的值变为了==》" + entry.getValue()); } } }
输出日志:
异步池的大小为:8初始化查询结果:key : -1 值:-1初始化查询结果:key : 0 值:0初始化查询结果:key : -2 值:-2初始化查询结果:key : 1 值:1初始化查询结果:key : -3 值:-3初始化查询结果:key : 2 值:2初始化查询结果:key : -4 值:-4初始化查询结果:key : 3 值:3初始化查询结果:key : -5 值:-5初始化查询结果:key : 4 值:4初始化查询结果:key : -6 值:-6初始化查询结果:key : 5 值:5初始化查询结果:key : -7 值:-7初始化查询结果:key : 6 值:6初始化查询结果:key : -8 值:-8初始化查询结果:key : 7 值:7初始化查询结果:key : -9 值:-9初始化查询结果:key : 8 值:8初始化查询结果:key : -10 值:-10初始化查询结果:key : 9 值:9更改后,key:2的值变为了==》12更改后,key:1的值变为了==》11更改后,key:3的值变为了==》13更改后,key:5的值变为了==》15更改后,key:6的值变为了==》16更改后,key:4的值变为了==》14更改后,key:7的值变为了==》17更改后,key:8的值变为了==》18更改后,key:9的值变为了==》19
通过日志可以看到我们的持续查询起作用了。至于异步操作,我们在上一节讲了,只需一个注解:@IgniteAsyncCallback
4. 过期策略Expiry Policies
过期策略指定在考虑缓存条目过期之前必须经过的时间。时间可以从创建、最后访问或修改时间计数开始计算。可以使用任何ExpiryPolicy预定义的实现设置过期策略:
public static void main(String[] args) { //一共5种回收策略,故分5个缓存对象来测试 try (Ignite ignite = Ignition.start("examples/config/example-ignite.xml")) { System.out.println(); System.out.println(">>> Cache continuous query example started."); CacheConfiguration cfg1 = new CacheConfiguration<Integer, Integer>(CACHE_NAME1); cfg1.setExpiryPolicyFactory(CreatedExpiryPolicy.factoryOf(new Duration(TimeUnit.SECONDS,10))); CacheConfiguration cfg2 = new CacheConfiguration<Integer, Integer>(CACHE_NAME2); cfg2.setExpiryPolicyFactory(AccessedExpiryPolicy.factoryOf(new Duration(TimeUnit.SECONDS,10))); CacheConfiguration cfg3 = new CacheConfiguration<Integer, Integer>(CACHE_NAME3); cfg3.setExpiryPolicyFactory(ModifiedExpiryPolicy.factoryOf(new Duration(TimeUnit.SECONDS,10))); CacheConfiguration cfg4 = new CacheConfiguration<Integer, Integer>(CACHE_NAME4); cfg4.setExpiryPolicyFactory(TouchedExpiryPolicy.factoryOf(new Duration(TimeUnit.SECONDS,10))); CacheConfiguration cfg5 = new CacheConfiguration<Integer, Integer>(CACHE_NAME5); cfg5.setExpiryPolicyFactory(EternalExpiryPolicy.factoryOf()); // Auto-close cache at the end of the example. try (IgniteCache<Integer, Integer> cache1 = ignite.getOrCreateCache(cfg1); IgniteCache<Integer, Integer> cache2 = ignite.getOrCreateCache(cfg2); IgniteCache<Integer, Integer> cache3 = ignite.getOrCreateCache(cfg3); IgniteCache<Integer, Integer> cache4 = ignite.getOrCreateCache(cfg4); IgniteCache<Integer, Integer> cache5 = ignite.getOrCreateCache(cfg5)) { createdExpiryPolicyTest(cache1); accessedExpiryPolicyTest(cache2); modifiedExpiryPolicyTest(cache3); touchedExpiryPolicyTest(cache4); eternalExpiryPolicyTest(cache5); } } }
public static void thread(int second){ try { Thread.sleep(second * 1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
public static void createdExpiryPolicyTest(IgniteCache<Integer, Integer> cache1) { cache1.put(1, 1); thread(5); //中间穿插修改和读取操作 cache1.put(1, 11); Integer v1 = cache1.get(1); assert v1 == 11; //到首次插入数据已经11秒了,我们设置的是10秒的存在时间,而且策略是created,因此是读不出数据的 thread(6); v1 = cache1.get(1); assert v1 == null; }
public static void accessedExpiryPolicyTest(IgniteCache<Integer, Integer> cache2) { cache2.put(2, 2); thread(5); //中间穿插着读取操作 Integer v2 = cache2.get(2); assert v2 == 2; thread(6); //两次睡眠时间超了10秒,但是因为期间发生了一次读取,那么现在读取的话,还是有值的 v2 = cache2.get(2); assert v2 == 2; thread(5); //在上次读取后,穿插一次修改操作 cache2.put(2, 22); thread(6); //同上次的修改操作过了6秒,但是同上次读取过了11秒,而且是accessed策略,所以读取不到值了。 v2 = cache2.get(2); assert v2 == null; }
public static void modifiedExpiryPolicyTest(IgniteCache<Integer, Integer> cache3) { cache3.put(3, 3); thread(5); //期间做了一次修改操作 cache3.put(3, 33); thread(6); //距离新建数据已经过了11秒,但是期间做了一次修改操作,且是modified模式,所以现在是可以取到值的 Integer v3 = cache3.get(3); assert v3 == 33; thread(5); //期间做一次查询 v3 = cache3.get(3); assert v3 == 33; thread(6); //距离上次查询过去了5秒,但是距离上次修改过了11,因为是modified模式,所以现在取不到值 v3 = cache3.get(3); assert v3 == null; }
public static void touchedExpiryPolicyTest(IgniteCache<Integer, Integer> cache4) { cache4.put(4, 4); thread(5); //期间做了一次修改操作 cache4.put(4, 44); thread(6); //距离新建数据已经过了11秒,但是期间做了一次修改操作,且是touched模式,所以现在是可以取到值的 Integer v4 = cache4.get(4); assert v4 == 44; thread(5); //期间做一次查询 v4 = cache4.get(4); assert v4 == 44; thread(6); //距离上次查询过去了5秒,距离上次修改过了11,因为是touched模式,所以现在还是可以取到值 v4 = cache4.get(4); assert v4 == 44; }
public static void eternalExpiryPolicyTest(IgniteCache<Integer, Integer> cache5) { //永久存在的,所以没怎么写 cache5.put(5, 5); Integer v5 = cache5.get(5); System.out.println(v5); }
4.1 ExpiryPolicy 过期策略(接口)
下面是ignite所支持的过期策略
解释下上面的表。标识使用的,标识该策略支持。我们以AccessedExpiryPolicy策略为例子:PS:我们就以样例为准(10秒的过期时间)该模式支持创建时间和最后访问时间两种,不支持最后修改时间。也就是说: 1.一个cache新建一条缓存后,期间什么也没做,10秒后,是要过期的。 2.一个cache新建一条缓存后,在10秒内发生了访问,那么过期时间就重置 3.一个cache新建一条缓存后,在10秒内发生了修改,那么不影响过期时间过期策略可以在CacheConfiguration设置。此策略将用于缓存内的所有条目
cfg.setExpiryPolicyFactory(CreatedExpiryPolicy.factoryOf(Duration.ZERO))
此外,可以更改或设置缓存中的单个操作的过期策略,我们所配置的的策略将用于在返回的缓存实例上所调用的每个操作。。
IgniteCache<Object, Object> cache = cache.withExpiryPolicy( new CreatedExpiryPolicy(new Duration(TimeUnit.SECONDS, 5)));
4.2. Factory< ? extends ExpiryPolicy>
我们现在所讲的,是CacheConfiguration#setExpiryPolicyFactory方法的入参,这个工厂我们并不陌生,他在上面的持续查询中也出现了。好在ignite提供了一系列的工具类,比如我们所使用的CreatedExpiryPolicy、AccessedExpiryPolicy、ModifiedExpiryPolicy、TouchedExpiryPolicy、EternalExpiryPolicy这些都是一些实现了ExpiryPolicy接口的实现类,我们可以使用其factory方法,实例化一个相应策略的工厂对象出来,但是factory方法需要一个过期时间对象,就是下面讲的Duration。注意L如果确实有特殊需要,我们可以自定义这个Factory,下面以自定义自己的CreatedExpiryPolicy为例:
cfg1.setExpiryPolicyFactory(new Factory<ExpiryPolicy>() { @Override public ExpiryPolicy create() { return new ExpiryPolicy() { @Override public Duration getExpiryForUpdate() { // TODO Auto-generated method stub return null; } @Override public Duration getExpiryForCreation() { // TODO Auto-generated method stub return new Duration(TimeUnit.SECONDS,10); } @Override public Duration getExpiryForAccess() { // TODO Auto-generated method stub return null; } }; } });
4.3 javax.cache.expiry.Duration
该类是一个时间对象,用来表示过期的事件是多久。该类有一些静态属性,方便使用,比如说Duration.FIVE_MINUTES、Duration.ZERO等,如果要自定义过期时间,那么可以通过创建对象来做,我们的代码就是这么做的。
4.4 Eager TTL
因为不是很值得作为一块讲,就放在这里一起讲了。过期的条目可以从缓存中删除,或者当它们被不同的缓存操作访问时。如果至少有一个配置为Eager TTL的缓存,那么ignite将创建一个线程来清除后台的过期条目。Eager TTL可以通过cacheconfiguration启用或禁用。eagerTtl属性(默认值为true):
//xml版本<bean class="org.apache.ignite.configuration.CacheConfiguration"> <property name="eagerTtl" value="true"/></bean>
//java版本 cacheConfiguration.setEagerTtl(false)
5. 回收策略
Apache ignite支持两种不同的数据清除策略:1.page-based eviction,该方式处理栈外内存页2.cache entries based eviction:该方式处理栈内内存页
5.1 page-based eviction
该回收策略是配置MemoryPolicyConfiguration来做的,我们在Ignite架构篇有讲。虚拟内存是由一个或者多个的由MemoryPolicyConfigurations所配置的内存区域所组成的。默认情况下,一个区域不断的增长它的大小,直到到达我们所配置的最大内存为止。为了避免可能的区域耗竭,您可能需要设置一个数据页清除模式--通过设置MemoryPolicyConfiguration.setPageEvictionMode(...)的参数,或者是random-LRU,或者是Random-2-LRU。回收策略会追踪数据页的使用,根据模式的实现将其中一些删除。
5.1.1 random-LRU
//xml版本<bean class="org.apache.ignite.configuration.MemoryConfiguration"> <!-- Defining additional memory poolicies. --> <property name="memoryPolicies"> <list> <!-- 定义一个20GB内存区域的策略,以RANDOM_LRU 为其回收策略 --> <bean class="org.apache.ignite.configuration.MemoryPolicyConfiguration"> <property name="name" value="20GB_Region_Eviction"/> <!-- 初始化大小为5GB. --> <property name="initialSize" value="#{5 * 1024 * 1024 * 1024}"/> <!-- 最大内存大小为 20 GB. --> <property name="maxSize" value="#{20 * 1024 * 1024 * 1024}"/> <!-- 启用回收策略,并设置为 RANDOM_LRU . --> <property name="pageEvictionMode" value="RANDOM_LRU"/> </bean> </list> ... </property> ...</bean>
// 定义额外的内存策略.MemoryConfiguration memCfg = new MemoryConfiguration();// 定义一个20GB内存区域的策略,以RANDOM_LRU 为其回收策略MemoryPolicyConfiguration memPlc = new MemoryPolicyConfiguration();memPlc.setName("20GB_Region_Eviction");// 初始化大小为5GB.memPlc.setInitialSize(5L * 1024 * 1024 * 1024);//最大内存大小为 20 GB.memPlc.setMaxSize(20L * 1024 * 1024 * 1024);// 启用回收策略,并设置为 RANDOM_LRU .memPlc.setPageEvictionMode(DataPageEvictionMode.RANDOM_LRU);// 设置新的内存策略memCfg.setMemoryPolicies(memPlc);
Random-LRU算法工作如下:
1.一旦配置了内存策略定义的内存区域,就会分配一个非堆数组来跟踪每个单独的数据页的“最后一次使用”时间戳。
2.当访问数据页时,它的时间戳会在跟踪数组中更新。
3.当需要将内存页回收时,该算法从跟踪数组中随机选择5个索引,根据距离当前最近的时间戳将内存页进行回收。
如果有的索引指向了非数据页(即索引页或者系统页),那么算法就跳向另外一个页继续算法操作。
5.1.2 Random-2-LRU
该算法是一个scan-resistant版本的Random-LRU, 如果想启用它,那么就需要将DataPageEvictionMode.RANDOM_2_LRU传递给MemoryPolicyConfiguration,基本与上面相似。在Random-2-LRU 中,最近的两个访问时间戳存储在每个数据页中。当被回收的时候,该算法从跟踪数组中随机选择5个索引,并在两个最新的时间戳中进行最小值,以进一步与被选为回收候选的其他4个页面的最小值进行比较。在处理one-hit-wonder问题时候,Random-2-LRU优于LRU算法,one-hit-wonder问题就是:如果一个数据页面很少被访问,但偶尔访问一次,它就会被长期保护。
5.1.3 Random-LRU vs. Random-2-LRU
在Random-LRU中,最近的时间戳保存在数据页里。但是在Random-2-LRU,则是保存最近的两次访问的时间戳。PS:Eviction Triggering默认情况下,当总内存区域的消耗达到90%时,将触发数据页面的驱逐算法。使用MemoryPolicyConfiguration.setEvictionThreshold(…),参数是一个double值,就是你希望的当内存达到多少时候启动回收算法。
5.2 On-Heap Cache Entries Based Eviction
如果通过 CacheConfiguration.setOnheapCacheEnabled(...)设置启动了on-heap缓存特性,那么ignite的内存是允许你在Java堆中存储热数据的。一旦 on-heap 缓存开启了,您可以使用一个缓存条目回收策略来管理不断增长的堆缓存。回收策略控制可以存储在缓存的堆内存中的元素的最大数量。当到达最大的堆缓存大小时,将从Java堆中逐出条目。PS:清除策略只从Java堆中删除缓存条目。存储在非堆页内存中的条目保持不变。一些驱逐策略支持通过内存大小限制进行批量回收。如果启动了批量回收策略,那么当缓存大小变成了batchSize的那个数值的时候,就会启动回收。如果根据内存大小限制的驱逐策略被启动了,那么当缓存条目超了这个数值时候,内存回收旧会开始。PS:只有在不设置最大内存限制的情况下,才支持批量清除。在Apache回收策略是可插拔的,并通过EvictionPolicy接口控制。每个缓存更改都通知回收策略的实现,并定义了从页面内存的堆缓存中选择要回收的条目的算法。EvictionPolicy接口的实现: FifoEvictionPolicy LruEvictionPolicy SortedEvictionPolicy IgfsPerBlockLruEvictionPolicy
5.2.1 Least Recently Used (LRU)
LRU回收策略基于最近使用的(LRU)算法,确保最近最少使用的条目(即在最长时间内未被触摸的条目)被首先逐出。PS:LRU回收策略很好地满足了堆缓存的大部分用例。在拿不准的时候使用它。这种驱逐策略由LruEvictionPolicy 实现,可以通过CacheConfiguration配置。它支持通过内存大小限制进行批量驱逐。
<bean class="org.apache.ignite.cache.CacheConfiguration"> <property name="name" value="myCache"/> <!-- Enabling on-heap caching for this distributed cache. --> <property name="onheapCacheEnabled" value="true"/> <property name="evictionPolicy"> <!-- LRU eviction policy. --> <bean class="org.apache.ignite.cache.eviction.lru.LruEvictionPolicy"> <!-- Set the maximum cache size to 1 million (default is 100,000). --> <property name="maxSize" value="1000000"/> </bean> </property> ...</bean>
5.2.2 First In First Out (FIFO)
FIFO收回策略基于先出先出(FIFO)算法,确保在堆缓存中最长时间的条目将首先被逐出。它与LruEvictionPolicy 不同,因为它忽略了条目的访问顺序。这一驱逐策略是通过FifoEvictionPolicy 实现的,可以通过CacheConfiguration配置。它支持通过内存大小限制进行批量回收。
<bean class="org.apache.ignite.cache.CacheConfiguration"> <property name="name" value="myCache"/> <!-- Enabling on-heap caching for this distributed cache. --> <property name="onheapCacheEnabled" value="true"/> <property name="evictionPolicy"> <!-- FIFO eviction policy. --> <bean class="org.apache.ignite.cache.eviction.fifo.FifoEvictionPolicy"> <!-- Set the maximum cache size to 1 million (default is 100,000). --> <property name="maxSize" value="1000000"/> </bean> </property> ...</bean>
5.2.3 Sorted
排序驱逐策略类似于FIFO驱逐策略,其区别在于,条目的顺序是由默认的或由用户定义的comparator定义的,并且确保最小的条目(即具有最小值的整数键的条目)首先被逐出。默认的比较器使用缓存条目的键来进行比较,这就要求缓存的类需要实现Comparable 接口。用户可以提供他们自己的比较器实现,可以使用键、值或两者进行条目比较。这种驱逐策略由SortedEvictionPolicy实现,可以通过CacheConfiguration配置。它支持通过内存大小限制进行批量驱逐。
<bean class="org.apache.ignite.cache.CacheConfiguration"> <property name="name" value="myCache"/> <!-- Enabling on-heap caching for this distributed cache. --> <property name="onheapCacheEnabled" value="true"/> <property name="evictionPolicy"> <!-- Sorted eviction policy. --> <bean class="org.apache.ignite.cache.eviction.sorted.SortedEvictionPolicy"> <!-- Set the maximum cache size to 1 million (default is 100,000) and use default comparator. --> <property name="maxSize" value="1000000"/> </bean> </property> ...</bean>
6.Data Rebalancing
预加载来自其他网格节点的数据以维护数据一致性。
//第一台服务器 public static void main(String[] args) throws IgniteException { Ignition.start("examples/config/example-ignite.xml"); while(true){ } }
//第二台服务器 public static void main(String[] args) { try(Ignite ignite = Ignition.start("examples/config/example-ignite.xml")){ //设置缓存配置对象 CacheConfiguration<Integer, Integer> cacheConfiguration = new CacheConfiguration<Integer, Integer>("REBALANCE_CACHE"); cacheConfiguration.setRebalanceMode(CacheRebalanceMode.SYNC); IgniteCache<Integer, Integer> cache = ignite.getOrCreateCache(cacheConfiguration); List<Integer> l = new ArrayList<>(); for(int i = 0;i < 10;i++){ l.add(i); cache.put(i, i + 100); } try { Thread.sleep(4000); } catch (InterruptedException e) { e.printStackTrace(); } Affinity<Object> affinity = ignite.<Object>affinity("REBALANCE_CACHE"); Map<ClusterNode, Collection<Object>> mapKeysToNodes = new HashMap<>(); System.out.println(mapKeysToNodes.size()); boolean flag = true; while(flag){ mapKeysToNodes = affinity.mapKeysToNodes(l); System.out.println("节点个数" + mapKeysToNodes.size()); if(mapKeysToNodes.size() > 1){ flag = false; } } Set<Entry<ClusterNode, Collection<Object>>> entrySet = mapKeysToNodes.entrySet(); for (Entry<ClusterNode, Collection<Object>> entry : entrySet) { ClusterNode node = entry.getKey(); System.out.println("=============start: " + node.hostNames() + "==================="); Collection<Object> values = entry.getValue(); for (Object object : values) { System.out.println(object); } System.out.println("==================end==================="); } while(true){ } } }
//第三台服务器 public static void main(String[] args) { try(Ignite ignite = Ignition.start("examples/config/example-ignite.xml")){ //设置缓存配置对象 List<Integer> l = new ArrayList<>(); for(int i = 0;i < 10;i++){ l.add(i); } Affinity<Object> affinity = ignite.<Object>affinity("REBALANCE_CACHE"); boolean flag = true; Map<ClusterNode, Collection<Object>> mapKeysToNodes = new HashMap<>(); while(flag){ mapKeysToNodes = affinity.mapKeysToNodes(l); if(mapKeysToNodes.size() >= 3){ flag = false; } } Set<Entry<ClusterNode, Collection<Object>>> entrySet = mapKeysToNodes.entrySet(); for (Entry<ClusterNode, Collection<Object>> entry : entrySet) { ClusterNode node = entry.getKey(); System.out.println("=============start: " + node.hostNames() + "==================="); Collection<Object> values = entry.getValue(); for (Object object : values) { System.out.println(object); } System.out.println("==================end==================="); } while(true){ } } }
PS:顺序启动
//服务器日志1[16:05:17] Topology snapshot [ver=1, servers=1, clients=0, CPUs=4, heap=0.87GB][16:07:59] Topology snapshot [ver=2, servers=2, clients=0, CPUs=4, heap=1.7GB][16:09:19] Topology snapshot [ver=3, servers=3, clients=0, CPUs=4, heap=2.6GB]
[16:08:00] Topology snapshot [ver=2, servers=2, clients=0, CPUs=4, heap=1.7GB]0节点个数2=============start: [10.10.18.75, IT-201606271616]===================035678==================end================================start: [10.10.18.75, IT-201606271616]===================1249==================end===================[16:09:19] Topology snapshot [ver=3, servers=3, clients=0, CPUs=4, heap=2.6GB]
[16:09:20] Topology snapshot [ver=3, servers=3, clients=0, CPUs=4, heap=2.6GB]=============start: [10.10.18.75, IT-201606271616]===================0167==================end================================start: [10.10.18.75, IT-201606271616]===================358==================end================================start: [10.10.18.75, IT-201606271616]===================249==================end===================
本来是要以事件的模式,来验证这个数据重新平衡的,但是开启了平衡事件,却一直报错。有大神知道话,望不吝赐教。讲解下上述代码: 代码是以分区模式存储的缓存~~~~~~ 1.第一段代码只是启动一台服务器 2.第二段代码首先创建缓存对象,然后为其设置数据再平衡策略:CacheRebalanceMode.SYNC,然后插入数据。因为有短暂延迟,所以我们做了个while循环,知道取到的映射的数量大于1个了才表示这时数据已经完全准备好了。然后通过打印我们可以看到,两个节点均分了我们的测试数据。 3.第三段代码启动了后,就while循环,等到rebalance结束,结束的标识其实就是我们的键的映射集合的size = 3(一共就三个节点嘛).然后通过日志,验证了rebalance(其实也不用验证,怎么可能不存在嘛)。需要注意的是:rebalance是需要时间,并不是服务器启动了,数据也就rebalance完了。我们代码中以while循环,一直在等待rebalance结束。
6.1 CacheRebalanceMode
在CacheRebalanceMode enum中定义了再平衡模式。
在我们的样例中,使用的是同步的缓存平衡模式,
6.2 Rebalance Thread Pool Tuning
IgniteConfiguration 提供了setRebalanceThreadPoolSize 方法,它允许设置从ignite系统的线程池中取出,用于重新平衡需要的线程数量。 每当一个节点需要发送一个批数据到一个远程节点上时候,就会从系统线程池中取出一个线程,这个节点可能是分区额主或者备份,或者是需要处理来自相反方向的批处理。每次发送或接收和处理该批处理时,线程都会交出给线程池。
默认下,只有一个线程被用来做再平衡操作。它意味着在一个特定的时间点,只有一个线程将会被用于从一个节点到另一个节点进行批量转移,或从远端处理批次。作为一个例子,如果该集群有两个节点和一个缓存,然后所有的缓存分区将依次重新平衡,一个接一个。如果集群有两个节点和两个不同的缓存,那么这些缓存将以并行的方式重新平衡,但是在特定的时间点,只会处理属于某个特定缓存的批处理,如上所述。
PS:每个缓存的分区数不影响平衡性能。有意义的是数据的总数,平衡线程池大小和下面各部分列出的其他参数。
根据系统中缓存的数量和存储在缓存中的数据量的不同,如果平衡线程池的大小等于1,那么在所有数据重新平衡到一个节点之前,可以花费大量的时间。为了加速预加载过程,可以增加IgniteConfiguration.setRebalanceThreadPoolSize值适用于你的情况。
假设IgniteConfiguration.setRebalanceThreadPoolSize设置为4,考虑上面的示例中,那么再平衡的行为将会像如下这样:
1.如果集群有两个节点和一个缓存,那么缓存的分区将被逻辑地放置在4个不同的组中,这些组将被4个线程中的一个重新平衡。属于某个特定组的分区将按顺序重新平衡。
2.如果集群有两个节点和两个不同的缓存,那么每个缓存的分区将被逻辑地放入4个不同的组(每个缓存将有自己的4个组,总共提供8组),并且这些组将被4个不同的线程并行地重新平衡。然而,在一个特定的时间点,只有属于一个组(总共8个)的批次将被处理,如上所述。
系统线程池在所有缓存相关操作(put,get,etc .)、SQL引擎和其他模块中广泛使用。设置IgniteConfiguration。setRebalanceThreadPoolSize为一个大值可能会显著增加平衡性能,影响您的应用程序吞吐量
6.3 Rebalance Message Throttling
当再平衡器将数据从一个节点转移到另一个节点时,它将整个数据集分解成批,并将每个批发送到一个单独的消息中。如果你的数据集很大,有很多信息要发送,CPU或网络可以被过度消耗。在这种情况下,在重新平衡的消息之间等待是合理的,这样,再平衡过程导致的负面性能影响最小化。这个时间间隔由CacheConfiguration的rebalanceThrottle 配置属性控制。它的默认值为0,这意味着消息之间不会有暂停。注意,单个消息的大小也可以由rebalanceBatchSize配置属性定制(默认大小是512K)。
例如,如果您希望rebalancer以100 ms间隔发送2MB的数据,您应该提供以下配置:
CacheConfiguration cacheCfg = new CacheConfiguration();cacheCfg.setRebalanceBatchSize(2 * 1024 * 1024);cacheCfg.setRebalanceThrottle(100);IgniteConfiguration cfg = new IgniteConfiguration();cfg.setCacheConfiguration(cacheCfg);// Start Ignite node.Ignition.start(cfg);
6.4 Configuration
缓存再平衡行为可以通过设置以下配置属性来定制:
CacheConfiguration
IgniteConfiguration
数据网格到此讲的七七八八,下一个主题是SQL网格
- Ignite 数据网格快速学习(二)
- Ignite SQL网格快速学习(二)
- Ignite 数据网格快速学习(一)
- Ignite SQL网格快速学习(一)
- Ignite的数据网格(重点!!)
- Ignite SQL网格
- Mesh网格篇(二)网格的快速切割
- Apache Ignite 学习
- Bootstrap快速学习笔记(3)网格系统
- 网格(二)
- 网格计算(二)
- DirectD3D-网格(二)
- D3D网格(二)
- 内存数据组织Apache Ignite
- Ignite
- Ignite学习笔记——Ignite的安装与配置
- mybatis快速学习(二)
- jQuery&HTML5 UI框架Ignite UI 13.2新功能大揭秘(二)
- 从浏览器到服务器 网站是如何工作的
- unsigned int与int相加如何转化问题
- 员工评估系统的流程简单分析
- 一周项目知识点总结
- oracle安装步骤
- Ignite 数据网格快速学习(二)
- 基于Ti的SDK u-boot添加自己的板子支持(2)
- HFP cover art feature
- python代理脚本实现期望数据与实际数据的比对(V1.0)
- How does the lead number of inverse circular function be verified?
- Enable/Disable Multicast on Interface of a Linux Machine
- 合并链表和求1+2+...+n不用循环、乘除法、循环、条件判断、选择相关的关键字
- POJ 1511 双向单源最短路 SPFA+邻接表
- 用Kotlin开发android平台语音识别,语义理解应用(olamisdk)