如何修复 Ignite 性能问题?

How to fix Ignite performance issues?

我们在服务器和客户端模式下都使用 Ignite 2.7.6:两个服务器和六个客户端。

起初,每个带有客户端Ignite的应用程序节点都有2G堆。每个 Ignite 服务器节点有 24G offheap 和 2G heap。

在上次应用更新中,我们引入了新功能,该功能需要 20 个整体(用户组)的大约 2000 个缓存。缓存条目的大小很小,最多 10 个整数。 这些缓存是通过 ignite.getOrCreateCache(name) 方法创建的,因此它们具有默认缓存配置(堆外、分区)。

但是在更新后的一个小时内,我们在服务器节点上出现 OOM 错误:

[00:59:55,628][SEVERE][sys-#44759][GridDhtPartitionsExchangeFuture] Failed to notify listener: o.a.i.i.processors.cache.distributed.dht.preloader.GridDhtPartitionsExchangeFuture@3287dcbd
java.lang.OutOfMemoryError: Java heap space

堆现在在 Ignite 服务器节点上增加到 16G,在应用程序节点上增加到 12G。

正如我们所见,所有服务器节点现在都具有高 CPU 负载约 250%(更新前为 20%)并且 G1 Young Gen 暂停长达 5 毫秒(更新前为 300 微秒)。

服务器配置为:

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
  <bean id="grid.cfg" class="org.apache.ignite.configuration.IgniteConfiguration">
    <property name="workDirectory" value="/opt/qwerty/ignite/data"/>
    <property name="gridLogger">
      <bean class="org.apache.ignite.logger.log4j2.Log4J2Logger">
        <constructor-arg type="java.lang.String" value="config/ignite-log4j2.xml"/>
      </bean>
    </property>
    <property name="dataStorageConfiguration">
      <bean class="org.apache.ignite.configuration.DataStorageConfiguration">
        <property name="defaultDataRegionConfiguration">
          <bean class="org.apache.ignite.configuration.DataRegionConfiguration">
            <property name="maxSize" value="#{24L * 1024 * 1024 * 1024}"/>
            <property name="pageEvictionMode" value="RANDOM_LRU"/>
          </bean>
        </property>
      </bean>
    </property>
    <property name="discoverySpi">
      <bean class="org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi">
        <property name="localAddress" value="host-1.qwerty.srv"/>
        <property name="ipFinder">
          <bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder">
            <property name="addresses">
              <list>
                <value>host-1.qwerty.srv:47500</value>
                <value>host-2.qwerty.srv:47500</value>
              </list>
            </property>
          </bean>
        </property>
      </bean>
    </property>
    <property name="communicationSpi">
      <bean class="org.apache.ignite.spi.communication.tcp.TcpCommunicationSpi">
        <property name="localAddress" value="host-1.qwerty.srv"/>
      </bean>
    </property>
  </bean>
</beans>

在 Ignite 服务器节点的内存转储中,我们看到很多 org.apache.ignite.internal.marshaller.optimized.OptimizedObjectStreamRegistry$StreamHolder 的 21Mb

内存泄漏报告显示:

Problem Suspect 1

One instance of "org.apache.ignite.internal.processors.cache.GridCachePartitionExchangeManager" loaded by "jdk.internal.loader.ClassLoaders$AppClassLoader @ 0x400000100" occupies 529 414 776 (10,39 %) bytes. The memory is accumulated in one instance of "java.util.LinkedList" loaded by "<system class loader>".

Keywords
jdk.internal.loader.ClassLoaders$AppClassLoader @ 0x400000100
java.util.LinkedList
org.apache.ignite.internal.processors.cache.GridCachePartitionExchangeManager

Problem Suspect 2

384 instances of "org.apache.ignite.thread.IgniteThread", loaded by "jdk.internal.loader.ClassLoaders$AppClassLoader @ 0x400000100" occupy 3 023 380 000 (59,34 %) bytes. 

Keywords
org.apache.ignite.thread.IgniteThread
jdk.internal.loader.ClassLoaders$AppClassLoader @ 0x400000100

Problem Suspect 3

1 023 instances of "org.apache.ignite.internal.processors.cache.CacheGroupContext", loaded by "jdk.internal.loader.ClassLoaders$AppClassLoader @ 0x400000100" occupy 905 077 824 (17,76 %) bytes. 

Keywords
jdk.internal.loader.ClassLoaders$AppClassLoader @ 0x400000100
org.apache.ignite.internal.processors.cache.CacheGroupContext

问题是我们做错了什么?我们能调什么?可能是我们代码的问题,但是如何判断出在哪里呢?

2000 个缓存很多。一个cache大概需要40M的数据结构。

我建议至少对具有相似目的和组成的所有缓存使用相同的 cacheGroup,以共享其中一些数据结构。