主从配置技术求助:生产环境主节点故障后数据同步异常
生产环境Ignite主从集群数据同步故障排查与解决
问题场景
3节点Ignite主从集群:1台主节点连接数据库同步数据,2台从节点同步主节点数据。主节点离线数分钟恢复后,未从数据库加载最新数据,反而同步了从节点的陈旧数据。
已尝试操作
- 将主节点缓存重平衡模式设为
NONE,从节点设为ASYNC - 通过代码
ignite.services().deployClusterSingleton("DatabaseSyncService", new DatabaseSyncService(vertx, routerService, emiService))尝试动态主节点分配 - 备份方案因主节点故障时数据库连接中断失效
故障原因分析
- Ignite默认集群数据同步逻辑优先从集群内节点拉取分区数据,主节点恢复时会优先同步从节点的陈旧数据,而非主动从数据库加载最新数据
- 缓存重平衡配置调整未针对主节点恢复后的数据源优先级做设置
- 集群单例服务未触发主节点恢复后的数据库全量同步逻辑
解决方案
1. 强制主节点启动时优先加载数据库数据
修改主节点缓存配置与启动逻辑,确保主节点完成数据库全量加载后再加入集群同步:
- 主节点缓存配置保持
CacheRebalanceMode.NONE,避免启动时拉取集群旧数据 - 主节点启动完成后,手动执行数据库全量数据加载,再开启重平衡同步到从节点:
// 主节点启动后执行数据库全量数据加载 Cache<String, YourDataModel> cache = ignite.cache("target-cache"); List<YourDataModel> latestDbData = yourDbService.fetchAllLatestData(); cache.putAll(latestDbData.stream() .collect(Collectors.toMap(YourDataModel::getUniqueKey, Function.identity()))); // 加载完成后开启重平衡,同步最新数据到从节点 cache.configuration().setRebalanceMode(CacheRebalanceMode.ASYNC); ignite.rebalance("target-cache");
2. 优化集群单例服务的触发逻辑
更新DatabaseSyncService,增加主节点恢复后的自动同步机制:
- 在服务初始化时,判断当前节点是否为主节点,若是则立即触发数据库同步
- 监听集群节点加入事件,当主节点恢复并加入集群时,自动执行数据刷新:
// 监听节点加入事件,触发主节点数据同步 ignite.events().localListen(event -> { if (event.type() == EventType.EVT_NODE_JOINED) { ClusterNode localNode = ignite.cluster().localNode(); // 验证当前节点为主节点且是刚恢复的节点 if (isDesignatedMaster(localNode) && event.node().id().equals(localNode.id())) { this.syncDbToCache(); // 自定义数据库到缓存的同步方法 } } return true; }, EventType.EVT_NODE_JOINED);
3. 配置主节点分区独占策略
通过缓存亲和性配置,确保主节点持有所有数据的主副本,避免恢复时从从节点拉取数据:
- 调整缓存亲和性配置,让主节点独占主分区:
CacheConfiguration<String, YourDataModel> cacheCfg = new CacheConfiguration<>("target-cache"); // 设置主节点持有所有主分区,从节点仅存备份 cacheCfg.setAffinity(new RendezvousAffinityFunction(false, 1)); cacheCfg.setBackups(2); // 对应2台从节点的备份数量
4. 配合持久化优化数据加载顺序
若启用Ignite持久化,需确保主节点启动时用数据库数据覆盖本地持久化的陈旧数据:
- 主节点启动前,清理本地持久化的旧数据目录,或在启动时执行数据库数据全量覆盖:
// 启动前清理持久化数据(需谨慎操作,仅主节点执行) DataStorageConfiguration storageCfg = ignite.configuration().getDataStorageConfiguration(); File storageDir = new File(storageCfg.getStoragePath()); if (storageDir.exists()) { Files.walkFileTree(storageDir.toPath(), new SimpleFileVisitor<Path>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { Files.delete(file); return FileVisitResult.CONTINUE; } @Override public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { Files.delete(dir); return FileVisitResult.CONTINUE; } }); }
内容的提问来源于stack exchange,提问作者Kartikey Srivastava
相关产品推荐
相关产品推荐

