В настоящее время я работаю над созданием Apache Ignite в качестве слоя кэширования. Мое требование — загрузить 10 миллионов данных на сервер при запуске. После кэширования 400 000 записей я столкнулся с ошибкой «Превышены служебные данные GC». Я проверил наличие утечек памяти, и мой код выглядит нормально. Может ли эта проблема быть связана с оперативной памятью моей системы (8 ГБ)?
Я попытался увеличить начальный размер кучи, настроив эти JAVA_OPTS.
-Xms512m -Xmx4g -Xmn2048m -XX:+UseParallelGC
После такой настройки я могу обрабатывать до 800 000 записей, но сразу после этого моя IDE выходит из строя. Мне пришлось перезагрузить систему.
Конфигурация сервера:
IgniteConfiguration cfg = new IgniteConfiguration();
cfg.setIgniteInstanceName("Instance");
cfg.setConsistentId("Node");
// Create TCP Communication SPI
TcpCommunicationSpi commSpi = new TcpCommunicationSpi();
// Set the socketWriteTimeout to 5 seconds (5000 milliseconds)
commSpi.setSocketWriteTimeout(5000);
// Data storage configuration
DataStorageConfiguration storageCfg = new DataStorageConfiguration();
DataRegionConfiguration regionCfg = new DataRegionConfiguration();
regionCfg.setName("500MB_Region");
regionCfg.setPersistenceEnabled(true);
regionCfg.setInitialSize(1024L * 1024 * 1024); // 1GB initial size
regionCfg.setMaxSize(6L * 1024 * 1024 * 1024); // 6GB maximum size
regionCfg.setMetricsEnabled(true); // Enable metrics for monitoring
// Data region configuration
regionCfg.setPageEvictionMode(DataPageEvictionMode.RANDOM_LRU);
regionCfg.setPageReplacementMode(PageReplacementMode.RANDOM_LRU);
storageCfg.setDefaultDataRegionConfiguration(regionCfg);
CacheConfiguration<String, String> marksCacheCfg = new CacheConfiguration<>();
marksCacheCfg.setName("poswavierCache");
marksCacheCfg.setAtomicityMode(CacheAtomicityMode.TRANSACTIONAL);
marksCacheCfg.setCacheMode(CacheMode.REPLICATED);
marksCacheCfg.setWriteSynchronizationMode(CacheWriteSynchronizationMode.FULL_ASYNC);
cfg.setCacheConfiguration(marksCacheCfg);
cfg.setPeerClassLoadingEnabled(true);
cfg.setDataStorageConfiguration(storageCfg);
Ignite igniteServer = Ignition.start(cfg);
igniteServer.cluster().state(ClusterState.ACTIVE);
IgniteCache<String, String> marksCache = igniteServer.getOrCreateCache("poswavierCache");
igniteServer.resetLostPartitions(Arrays.asList("poswavierCache"));
igniteServer.cluster().baselineAutoAdjustEnabled(true);
Вот как я передаю данные на свой сервер с помощью datastreamer:
private static final int BATCH_SIZE = 100000;
@Autowired
private IgniteCacheService igniteCacheService;
@Autowired
private ProductLinesRepo productLinesRepo;
public CompletableFuture<Void> processAllRecords() {
long startTime = System.currentTimeMillis();
int pageNumber = 0;
Page<ProductLines> page;
CompletableFuture<Void> future = CompletableFuture.completedFuture(null);
do {
page = productLinesRepo.findRecordsWithPanNotNull(PageRequest.of(pageNumber++, BATCH_SIZE));
List<ProductLines> records = page.getContent();
if (!records.isEmpty()) {
int finalPageNumber = pageNumber;
future = future.thenCompose(result ->
CompletableFuture.runAsync(() -> {
igniteCacheService.streamBulkData("poswavierCache", records);
logger.info("Processed {} records", (finalPageNumber - 1) * BATCH_SIZE + records.size());
}));
}
} while (page.hasNext());
long endTime = System.currentTimeMillis();
long totalTime = endTime - startTime;
logger.info("Total time taken for processing all records: {} milliseconds", totalTime);
return future;
}
DataStreamer:
public void streamBulkData(String cacheName, List<ProductLines> records) {
try (IgniteDataStreamer<String, ProductLines> streamer = ignite.dataStreamer(cacheName)) {
streamer.allowOverwrite(true);
for (ProductLines record : records) {
String key = record.getPan_no();
if (key != null) {
streamer.addData(key, record);
} else {
System.err.println("Skipping record with null key: " + record);
}
}
streamer.flush();
} catch (CacheException e) {
System.err.println("Error streaming data to cache: " + e.getMessage());
e.printStackTrace();
}
}




Здесь нет одной очевидной вещи, указывающей на проблему, но есть несколько вещей, которые ничего не стоят.
Во-первых, из того, что вы написали, похоже, что у вас одна нода с 8Гб памяти. Для распределенной базы данных в памяти это не очень много.
Во-вторых, похоже, вы перегружаете свою машину. Поскольку Ignite находится в памяти, никогда нельзя разрешать его переключение на диск. Однако вы настроили 4 ГБ кучи и 6 ГБ вне кучи. Это уже превышает 8 ГБ вашей памяти, даже если не учитывать операционную систему, Java и любые другие накладные расходы.
Это не повлияет на использование памяти, но вы настроили вытеснение и сохранение. Выберите один — возможно, настойчивость.
Я не уверен, что понимаю, где находится ваш стример данных (я предполагаю, что это клиент), но он будет использовать много памяти. Вы действительно хотите передавать данные в потоковом режиме, а не копировать их все в память и публиковать в большом пакете.
Наконец, вы настроили Java для использования параллельного сборщика мусора, который очень старый и плохо подходит для многоядерных систем с большим объемом памяти, таких как Ignite. Общая рекомендация — использовать G1. Подробнее в документации.
Я не уверен, о чем ты спрашиваешь? В своем ответе я никогда не упоминаю внешнюю базу данных.
Да жаль. Но мой вариант использования заключается в том, что мне нужно загрузить в кеш 10 миллионов записей из внешней базы данных при запуске. Извините за путаницу
Пожалуйста, задайте это как отдельный вопрос. Похоже, это не связано с этим вопросом.
Что вы подразумеваете под копированием данных в память из внешней базы данных на кеширующий сервер?? Можете ли вы объяснить это, пожалуйста?