即使分配了 8GB space,Hazelcast 最终也会出现堆 space 内存不足错误
Hazelcast endup in heap space out of memory error even if 8GB space allocated
我的项目有一个要求,要将 900 万个数据从 oracle 数据库缓存到 Hazelcast。但显然 Hazelcast 消耗的堆 space 比它应该消耗的多。我已经为应用程序分配了 8bg 堆space,但我仍然遇到内存不足错误。
下面是我的数据加载器 class .
public class CustomerProfileLoader implements ApplicationContextAware, MapLoader<Long, CustomerProfile> {
private static CustomerProfileRepository customerProfileRepository;
@Override
public CustomerProfile load(Long key) {
log.info("load({})", key);
return customerProfileRepository.findById(key).get();
}
@Override
public Map<Long, CustomerProfile> loadAll(Collection<Long> keys) {
log.info("load all in loader executed");
Map<Long, CustomerProfile> result = new HashMap<>();
for (Long key : keys) {
CustomerProfile customerProfile = this.load(key);
if (customerProfile != null) {
result.put(key, customerProfile);
}
}
return result;
}
@Override
public Iterable<Long> loadAllKeys() {
log.info("Find all keys in loader executed");
return customerProfileRepository.findAllId();
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
customerProfileRepository = applicationContext.getBean(CustomerProfileRepository.class);
}
}
下面是存储库查询。如果我更改以下查询,使其限制为 200 万个数据,那么一切正常。
@Query("SELECT b.id FROM CustomerProfile b ")
Iterable<Long> findAllId();
下面是我在 hazelcast.xml
文件中的地图配置。在这里,我将 backup count
设为 zero
,之前它是 1,但这没有任何区别。
<?xml version="1.0" encoding="UTF-8"?>
<hazelcast
xsi:schemaLocation="http://www.hazelcast.com/schema/config
http://www.hazelcast.com/schema/config/hazelcast-config-3.11.xsd"
xmlns="http://www.hazelcast.com/schema/config"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<!-- Use port 5701 and upwards on this machine one for cluster members -->
<network>
<port auto-increment="true">5701</port>
<join>
<multicast enabled="false"/>
<tcp-ip enabled="true">
<interface>127.0.0.1</interface>
</tcp-ip>
</join>
</network>
<map name="com.sample.hazelcast.domain.CustomerProfile">
<indexes>
<!-- custom attribute without an extraction parameter -->
<index ordered="false">postalCode</index>
</indexes>
<backup-count>0</backup-count>
<map-store enabled="true" initial-mode="EAGER">
<class-name>com.sample.hazelcast.CustomerProfileLoader</class-name>
</map-store>
</map>
</hazelcast>
数据库Table结构:
ID NOT NULL NUMBER(19)
LOGIN_ID NOT NULL VARCHAR2(32 CHAR)
FIRSTNAME VARCHAR2(50 CHAR)
LASTNAME VARCHAR2(50 CHAR)
ADDRESS_LINE1 VARCHAR2(50 CHAR)
ADDRESS_LINE2 VARCHAR2(50 CHAR)
CITY VARCHAR2(30 CHAR)
postal_code VARCHAR2(20 CHAR)
COUNTRY VARCHAR2(30 CHAR)
CREATION_DATE NOT NULL DATE
UPDATED_DATE NOT NULL DATE
REGISTER_NUM NOT NULL VARCHAR2(10 CHAR)
其他要点:
- 我现在只有一个 hazelcast 服务器 运行 实例,其中
分配的堆 space 为 8GB
JAVA_OPTS=-Xmx8192m
。之前是4gb
但是当我得到堆 space 错误时,我增加到 8GB,但没有运气。
- 暂时在访问地图时执行地图加载器
第一次。
- 特定的 table (customer_profile) 有 6 列
它没有任何二进制类型。它只有基本的价值观,比如
名字姓氏。
- 使用的 hazelcast 版本是 3.8
我现在面临的问题是:
当它获取所有数据和将其加载到地图上。现在table里面有900万条数据。
加载数据也需要很多时间,也许我可以通过 运行 多个 hazelcast 服务器实例来解决这个问题。
我是 hazelcast 的新手,非常感谢任何帮助:)
在我看来真正的问题是你有太多数据无法保存在 8GB 堆中。
你说平均每行有 100 个字节的数据表示为字符串数据。
这里有一些估计1 space 需要将 9,000,000 行数据表示为 HashMap
。假设有9个字符串,2个日期和一个int
.
- 在 64 位 JVM 中,字符串的开销为 48 字节 + 每个字符 2 字节。所以 9 Java 个字符串代表大约 100 个字节的字符数据,总计大约 650 个字节。
- A
Date
是 32 字节 x 2 -> 64 字节
- 代表 9 个字符串、2 个日期和 1 个整数的记录将是 112 个字节。
- 一个密钥(比如
Integer
)将是 24 个字节。
- 一个 HashMap 条目将是 40 个字节。
- (650 + 64 + 112 + 24 + 40) x 9,000,000 -> ~8,000,000,000 字节
- HashMap 的主数组将是 2^24 x 8 字节 == ~128,000,000 字节
如您所见,实际数据超过 8GB。然后考虑到 Java 堆需要大量工作 space 的事实;至少说30%。
您得到 OOME 一点也不奇怪。我的估计是您的堆需要大 50%...并且假定您对每行 100 字节的估计是准确的。
这完全基于您的 loadAll
方法,该方法似乎将数据库中的所有行具体化为常规 HashMap
。它不考虑 Hazelcast 用于缓存的堆 space 或其他内存。
虽然您可以只扩展堆,但我认为更改代码更有意义,这样它就不会像那样具体化行。目前尚不清楚这是否有意义。这将取决于地图的使用方式。
1 - 我假设您使用的是 Java 8.
我的项目有一个要求,要将 900 万个数据从 oracle 数据库缓存到 Hazelcast。但显然 Hazelcast 消耗的堆 space 比它应该消耗的多。我已经为应用程序分配了 8bg 堆space,但我仍然遇到内存不足错误。
下面是我的数据加载器 class .
public class CustomerProfileLoader implements ApplicationContextAware, MapLoader<Long, CustomerProfile> {
private static CustomerProfileRepository customerProfileRepository;
@Override
public CustomerProfile load(Long key) {
log.info("load({})", key);
return customerProfileRepository.findById(key).get();
}
@Override
public Map<Long, CustomerProfile> loadAll(Collection<Long> keys) {
log.info("load all in loader executed");
Map<Long, CustomerProfile> result = new HashMap<>();
for (Long key : keys) {
CustomerProfile customerProfile = this.load(key);
if (customerProfile != null) {
result.put(key, customerProfile);
}
}
return result;
}
@Override
public Iterable<Long> loadAllKeys() {
log.info("Find all keys in loader executed");
return customerProfileRepository.findAllId();
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
customerProfileRepository = applicationContext.getBean(CustomerProfileRepository.class);
}
}
下面是存储库查询。如果我更改以下查询,使其限制为 200 万个数据,那么一切正常。
@Query("SELECT b.id FROM CustomerProfile b ")
Iterable<Long> findAllId();
下面是我在 hazelcast.xml
文件中的地图配置。在这里,我将 backup count
设为 zero
,之前它是 1,但这没有任何区别。
<?xml version="1.0" encoding="UTF-8"?>
<hazelcast
xsi:schemaLocation="http://www.hazelcast.com/schema/config
http://www.hazelcast.com/schema/config/hazelcast-config-3.11.xsd"
xmlns="http://www.hazelcast.com/schema/config"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<!-- Use port 5701 and upwards on this machine one for cluster members -->
<network>
<port auto-increment="true">5701</port>
<join>
<multicast enabled="false"/>
<tcp-ip enabled="true">
<interface>127.0.0.1</interface>
</tcp-ip>
</join>
</network>
<map name="com.sample.hazelcast.domain.CustomerProfile">
<indexes>
<!-- custom attribute without an extraction parameter -->
<index ordered="false">postalCode</index>
</indexes>
<backup-count>0</backup-count>
<map-store enabled="true" initial-mode="EAGER">
<class-name>com.sample.hazelcast.CustomerProfileLoader</class-name>
</map-store>
</map>
</hazelcast>
数据库Table结构:
ID NOT NULL NUMBER(19)
LOGIN_ID NOT NULL VARCHAR2(32 CHAR)
FIRSTNAME VARCHAR2(50 CHAR)
LASTNAME VARCHAR2(50 CHAR)
ADDRESS_LINE1 VARCHAR2(50 CHAR)
ADDRESS_LINE2 VARCHAR2(50 CHAR)
CITY VARCHAR2(30 CHAR)
postal_code VARCHAR2(20 CHAR)
COUNTRY VARCHAR2(30 CHAR)
CREATION_DATE NOT NULL DATE
UPDATED_DATE NOT NULL DATE
REGISTER_NUM NOT NULL VARCHAR2(10 CHAR)
其他要点:
- 我现在只有一个 hazelcast 服务器 运行 实例,其中
分配的堆 space 为 8GB
JAVA_OPTS=-Xmx8192m
。之前是4gb 但是当我得到堆 space 错误时,我增加到 8GB,但没有运气。 - 暂时在访问地图时执行地图加载器 第一次。
- 特定的 table (customer_profile) 有 6 列 它没有任何二进制类型。它只有基本的价值观,比如 名字姓氏。
- 使用的 hazelcast 版本是 3.8
我现在面临的问题是:
当它获取所有数据和将其加载到地图上。现在table里面有900万条数据。
加载数据也需要很多时间,也许我可以通过 运行 多个 hazelcast 服务器实例来解决这个问题。
我是 hazelcast 的新手,非常感谢任何帮助:)
在我看来真正的问题是你有太多数据无法保存在 8GB 堆中。
你说平均每行有 100 个字节的数据表示为字符串数据。
这里有一些估计1 space 需要将 9,000,000 行数据表示为 HashMap
。假设有9个字符串,2个日期和一个int
.
- 在 64 位 JVM 中,字符串的开销为 48 字节 + 每个字符 2 字节。所以 9 Java 个字符串代表大约 100 个字节的字符数据,总计大约 650 个字节。
- A
Date
是 32 字节 x 2 -> 64 字节 - 代表 9 个字符串、2 个日期和 1 个整数的记录将是 112 个字节。
- 一个密钥(比如
Integer
)将是 24 个字节。 - 一个 HashMap 条目将是 40 个字节。
- (650 + 64 + 112 + 24 + 40) x 9,000,000 -> ~8,000,000,000 字节
- HashMap 的主数组将是 2^24 x 8 字节 == ~128,000,000 字节
如您所见,实际数据超过 8GB。然后考虑到 Java 堆需要大量工作 space 的事实;至少说30%。
您得到 OOME 一点也不奇怪。我的估计是您的堆需要大 50%...并且假定您对每行 100 字节的估计是准确的。
这完全基于您的 loadAll
方法,该方法似乎将数据库中的所有行具体化为常规 HashMap
。它不考虑 Hazelcast 用于缓存的堆 space 或其他内存。
虽然您可以只扩展堆,但我认为更改代码更有意义,这样它就不会像那样具体化行。目前尚不清楚这是否有意义。这将取决于地图的使用方式。
1 - 我假设您使用的是 Java 8.