Apache Ignite 2.3使用问题总结


声明:本文转载自https://my.oschina.net/jet1987/blog/1601337,转载目的在于传递更多信息,仅供学习交流之用。如有侵权行为,请联系我,我会及时删除。

一、写在开头

  1. 我们的系统采用了Apache Ignite 2.3.0版本,主要用到功能:分布式,堆外内存,SQL语法,持久化,数据过期,可内嵌。功能多,方便后期扩展。

  2. Ignite基础学习,请参看https://www.zybuluo.com/liyuj/note/230739,或者官网https://ignite.apache.org/

  3. 本文列出一些使用上遇到的问题与总结。


我们使用了Ignite的如下特性

  1. 使用off-heap缓存:避免gc停顿。

  2. 固化内存可设置堆外内存使用阈值,随着历史地址的增多,系统内存被耗尽时,因持久化特性,固化内存会在内存中保留热数据,自动地将冷数据移出内存到磁盘

  3. 使用持久化:宕机无须担心历史数据丢失,重启无须初始化。

  4. 使用集群:可自动同步节点中的数据,可自动发现新机器,方便扩展,提升性能。

  5. 使用固化内存的“复制”模式,只查询本机,占用内存大,但是性能好。

  6. H2内置数据库,以标准SQL语法存储和查询数据,且能自定义SQL函数,便于应付不同规则。

  7. 对于客户环境,程序能自检出建议调优的系统配置项


二、分布式

SQL表ID自增

1)  分布式如何做到id的自增?

IgniteCacheAtomicSequence接口提供的分布式原子性序列类似于分布式原子性的long类型,但是它的值只能增长。他也支持预留一定范围的序列值,来避免每次一个序列必须提供下一个值时导致的昂贵的网络消耗以及缓存更新。

2.2版本测试时,发现IgniteCacheAtomicSequence会出现数据不同步的问题,2.3版本未测试出来,已经解决。请注意它在close时会删除缓存的。springboot中可设置这个bean的destroyMethod = ""

通讯队列

Message queue limit is set to 0 which may lead to potential OOMEs

when running cache operations in FULL_ASYNC or PRIMARY_SYNC modes

due to message queues growth on sender and receiver sides.

我的理解:可以不管这个警告,如果运维中发现负载高,导致队列大,占用内存多,应该考虑分流出去,而不是去限制队列大小,来牺牲接口延时或者负载能力。现实情况是,客户可能提供内存刚刚够用的机器,那么如果要保证系统稳定运维,必须设置这个参数,来避免突然的访问洪流导致系统的奔溃。

看了源码,可设置如下,其值使用时需要32*128=4096,这是最小队列值。先做下测试观察下

ipCom.setMessageQueueLimit(32);//same as DFLT_ACK_SND_THRESHOLD

 

slowClientQueueLimit

慢节点的数据同步队列阈值,如果配置了该值,且待发送数据大于该值,则会丢弃远端节点。

默认不开启该功能

静态IP查找和组播查找

TcpDiscoveryMulticastIpFinder();//组播查找

TcpDiscoveryVmIpFinder();//静态ip查找

使用细节说明:

  1. 组播查找:是会自动发现并连接上网络可达的Ignite节点。Ignite的默认IP搜索器。服务器网络有可能组播被禁用的情况,这时候只能使用静态ip查找。对应想隔离的Ignite集群,也需要使用静态ip查找。

  2. 静态ip查找:只需知道节点ip和port就能连接组成集群了。

  3. 对于本地端口侦听LocalPortRange,建议设置0,默认是20,它会从setLocalPort开启尝试绑定端口,那么每次启动服务可能用于连接的port会不同,并且原生持久化的文件夹会不同,导致无法重用之前的持久化数据。 

三、固化内存

过期策略

过期策略可以在CacheConfiguration中进行设置,这个策略可以用于缓存内的所有条目。

cfg.setExpiryPolicyFactory(CreatedExpiryPolicy.factoryOf(Duration.ZERO));


也可以在对缓存进行单独操作时对过期策略进行设置或者修改。

IgniteCache<Object, Object> cache = cache.withExpiryPolicy(new CreatedExpiryPolicy(new Duration(TimeUnit.SECONDS, 5)));

注意:过期策略,如果开启了持久化,那么第二种方式在重启系统后,会把之前过期退出内存的数据,但是在磁盘的数据重新load到缓存,并且不会退出。而第一种方式,在重启后是不会load这部分过期数据的。

 

设置固化内存大小(堆外内存)

当没有开启持久化时,内存不足时会抛出异常。

Not enough memory allocated (consider increasing memory policy size or enabling evictions) [policyName=default, size=100.0 MB]

解决方案:1.调大固化内存;2.设置setPageEvictionMode;3.开启持久化。

 

当开启原生持久化时,多出的数据会被写入磁盘,出现如下信息,说明堆外内存已经存不下:

WARN 10996 --- [io-17103-exec-1] o.a.i.i.p.c.p.pagemem.PageMemoryImpl     : Page evictions started, this will affect storage performance (consider increasing MemoryConfiguration#setPageCacheSize).

解决方案:1.无视它;2.调大固化内存; 3.关闭持久化,设置setPageEvictionMode 

-XX:MaxDirectMemorySize

启动参数无法限制程序内设置堆外内存最大值,原因未知。官方文档里没有配置该参数,暗示可能不限制。我建议配置上,避免程序启动时报“Performance suggestions for grid”。Set max direct memory size if getting 'OOME: Direct buffer memory' (add '-XX:MaxDirectMemorySize=<size>[g|G|m|M|k|K]' to JVM options)

四、持久化

性能差的原因与调优

WAL mode

There several levels of guarantees (WALMode)

image.png

可选择LOG_ONY,性能相对DEFAULT好很多。

 

checkpointing

(The checkpointing is started if either dirty pages count goes beyond totalPages * 2 / 3 value or DataRegionConfiguration.checkpointPageBufferSize is reached)。

下面是CheckpointPageBufferSized:

long res = regCfg.getCheckpointPageBufferSize();
        if (res == 0L) {
            if (regCfg.getMaxSize() < GB)
                res = Math.min(DFLT_MIN_CHECKPOINTING_PAGE_BUFFER_SIZE, regCfg.getMaxSize());
            else if (regCfg.getMaxSize() < 8 * GB)
                res = regCfg.getMaxSize() / 4;
            else
                res = DFLT_MAX_CHECKPOINTING_PAGE_BUFFER_SIZE;
        }


如果日志中发现频繁的触发Checkpoint,

可尝试开启setWriteThrottlingEnabled(true);

或再尝试同时调大drCfg.setCheckpointPageBufferSize(); 

CacheStore

1) get当缓存中未找到时才会去调用通读load接口。

2) 集群时,某节点put操作,缓存数据会同步到所有节点,但只有某一个节点会触发write接口

3) 如果是三方数据库存储,集群时的操作如下:第一个启动节点需要加载历史数据,其他节点需要等待第一个节点初始化完成再启动,并且需要关闭初始化功能。 

硬件

选购SSD,闪存。

 

其他注意项

  1. 备份数据是放在内存的,默认不开启备份。复制模式可不用设置备份,分区模式需设置备份。cacheCfg.setBackups(1);

  2. 默认是会从备份点读取数据的,而设置false时,它会先从主节点读取数据,当主节点挂掉时,它也依然能从备份数据中读取数据。需要注意主备同步时的时间差。cacheCfg.setReadFromBackup(false);

  3. WARN 4929 --- [        Timer-0] o.a.i.s.c.noop.NoopCheckpointSpi         : Checkpoints are disabled (to enable configure any GridCheckpointSpi implementation)  Checkpoint SPI provides an ability to save an intermediate job state. It can be useful when long running jobs need to store some intermediate state to protect from system or application failures

  4. WARN 43115 --- [        Timer-0] o.a.i.i.m.c.GridCollisionManager         : Collision resolution is disabled (all jobs will be activated upon arrival).

    作业被提交到一个线程池然后随机地执行,如果要对作业执行顺序进行细粒度控制的话,需要启用CollisionSpi 

日志问题解决

Logger logger = LoggerFactory.getLogger("org.apache.ignite");
igniteCfg.setGridLogger(new Slf4jLogger(logger));

 

五、性能问题

测试服务器说明:4核8线程服务器

1)  仅查询缓存数据

单节点,10线程查询缓存:

100万数据sql查询,建索引,查询4个字段,查到1万条,提取1万,http qps 200/s

100万数据sql查询,建索引,查询4个字段,查到1000条,提取数据1000,http qps 1.2k/s

100万数据sql查询,建索引,查询4个字段,查到100条,提取数据100,http qps 11k/s

100万数据sql查询,建索引,查询4个字段,查到1条,提取1条,http qps 23k/s

1000万数据sql查询,建索引,like查询1个字段,查到1000条,提取数据1000,http qps 1.2k/s

//查询到1000条,30K/s

addrCache.query(query);

 

//查询到1000条,4.5k/s

QueryCursor<List<?>> cursor = addrCache.query(query);

 

//查询到100条,14k/s

QueryCursor<List<?>> cursor = addrCache.query(query);

 

查询到1000条记录,返回到用户空间,会占用很大空间,导致性能低下。


试过如下方式:

//查询到1000条,3k/s
Iterable<Entry<Long, JetAddress>> iterator = addrCache.localEntries(CachePeekMode.OFFHEAP);
        Iterator<Entry<Long, JetAddress>> iter = iterator.iterator();
        while(iter.hasNext()) {
            iter.next();
        }
//查询到1000条,3K/s
        Iterator<List<?>> iter = addrCache.query(query).iterator();
        while(iter.hasNext()) {
            JetAddress jetAddress = addrCache.localPeek((Long)iter.next().get(0), CachePeekMode.OFFHEAP);
            a = (int) jetAddress.getId();
            j++;
        }

六、SQL查询

1)表对象里字段类型可以是List和数组,这种类型加上@QuerySqlField,工具查询会报错,但是程序内部可以查询。

2)使用new SqlQuery(BsAddress.class, "prefix = ? and matchFun(preResNode, ?) = 1");这种方式查询出的是整个对象,包括没有@QuerySqlField的字段

 

七、例子

可去github下载 https://github.com/Jet-Yuen/Ignite2.3-example

 

本文发表于2018年01月03日 18:33
(c)注:本文转载自https://my.oschina.net/jet1987/blog/1601337,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如有侵权行为,请联系我们,我们会及时删除.

阅读 15318 讨论 0 喜欢 1

抢先体验

扫码体验
趣味小程序
文字表情生成器

闪念胶囊

你要过得好哇,这样我才能恨你啊,你要是过得不好,我都不知道该恨你还是拥抱你啊。

直抵黄龙府,与诸君痛饮尔。

那时陪伴我的人啊,你们如今在何方。

不出意外的话,我们再也不会见了,祝你前程似锦。

这世界真好,吃野东西也要留出这条命来看看

快捷链接
网站地图
提交友链
Copyright © 2016 - 2021 Cion.
All Rights Reserved.
京ICP备2021004668号-1