SpringBoot原生实现分布式MapReduce计算

一、架构设计调整

核心组件替换方案:

1、注册中心

→ 数据库注册表

2、任务队列

→ 数据库任务表

3、分布式锁

→ 数据库行级锁

4、节点通信

→ HTTP REST接口

二、数据库表结构设计

java 复制代码
 节点注册表
CREATETABLE compute_nodes (
    node_id VARCHAR(36)PRIMARYKEY,
    last_heartbeat TIMESTAMP,
    statusENUM('ACTIVE','DOWN')
);
java 复制代码
-- 任务分片表
CREATETABLE task_shards (
    shard_id INTAUTO_INCREMENTPRIMARYKEY,
    data_range VARCHAR(100),-- 例如:1-10000
    statusENUM('PENDING','PROCESSING','COMPLETED'),
    locked_by VARCHAR(36),
    locked_at TIMESTAMP
);

三、核心实现代码

1. 节点自注册实现

java 复制代码
@Scheduled(fixedRate =3000)
public void nodeRegistration(){
    jdbcTemplate.update(
        "INSERT INTO compute_nodes VALUES (?, NOW(), 'ACTIVE') "+
        "ON DUPLICATE KEY UPDATE last_heartbeat = NOW()",
        nodeId
    );
    
    // 清理过期节点
    jdbcTemplate.update(
        "DELETE FROM compute_nodes WHERE last_heartbeat < ?",
        LocalDateTime.now().minusSeconds(10)
    );
}

2. 任务分片抢占式调度

java 复制代码
@Scheduled(fixedDelay =1000)
public void acquireTasks(){
    List<Long> shardIds = jdbcTemplate.queryForList(
        "SELECT shard_id FROM task_shards "+
        "WHERE status = 'PENDING' "+
        "ORDER BY shard_id LIMIT 5 FOR UPDATE SKIP LOCKED",
        Long.class
    );
    
    shardIds.forEach(shardId ->{
        int updated = jdbcTemplate.update(
            "UPDATE task_shards SET status = 'PROCESSING', "+
            "locked_by = ?, locked_at = NOW() "+
            "WHERE shard_id = ? AND status = 'PENDING'",
            nodeId, shardId
        );
        if(updated >0) processShard(shardId);
    });
}

3. Map阶段分布式处理

java 复制代码
public void processShard(Long shardId){
    try{
        DataRange range =getDataRange(shardId);
        List<Record> records =fetchData(range);
        
        Map<String, Double> partialResult = records.parallelStream()
            .collect(Collectors.groupingBy(
                Record::getCategory,
                Collectors.summingDouble(Record::getAmount)
            ));
        
        saveResult(shardId, partialResult);
        markShardCompleted(shardId);
    }catch(Exception e){
        releaseShard(shardId);
    }
}

4. Reduce阶段聚合实现

java 复制代码
public Map<String, Double> reduceAllResults(){
    return jdbcTemplate.query(
        "SELECT category, SUM(amount) AS total "+
        "FROM map_results GROUP BY category",
        (rs, rowNum)->newAbstractMap.SimpleEntry<>(
            rs.getString("category"),
            rs.getDouble("total")
        )).stream().collect(Collectors.toMap(
            Entry::getKey,Entry::getValue
        ));
}

四、关键优化点

1. 分片锁优化策略

java 复制代码
// 使用乐观锁避免长时间占用连接
public boolean tryLockShard(Long shardId) {
    return jdbcTemplate.update(
        "UPDATE task_shards SET version = version + 1 " +
        "WHERE shard_id = ? AND version = ?",
        shardId, currentVersion) > 0;
}

2. 结果缓存优化

java 复制代码
@Cacheable(value ="partialResults", key ="#shardId")
public Map<String, Double> getPartialResult(Long shardId){
    return jdbcTemplate.query(...);
}

// 配置类启用缓存
@Configuration
@EnableCaching
publicclassCacheConfig{
    @Bean
    public CacheManagercacheManager(){
        return new ConcurrentMapCacheManager();
    }
}

3. 分布式事务处理

java 复制代码
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void markShardCompleted(Long shardId) {
    jdbcTemplate.update(
        "UPDATE task_shards SET status = 'COMPLETED' " +
        "WHERE shard_id = ?", shardId);
    
    eventPublisher.publishEvent(
        new ShardCompleteEvent(shardId));
}

五、部署架构对比

六、性能压测数据

测试环境:

100w数据

七、生产级改进建议

分片策略优化

java 复制代码
// 采用跳跃哈希算法避免热点
public List<Long> assignShards(int totalShards) {
    return IntStream.range(0, totalShards)
        .mapToObj(i -> (nodeHash + i*2654435761L) % totalShards)
        .collect(Collectors.toList());
}

动态分片扩容

java 复制代码
@Scheduled(fixedRate =60000)
public void autoReshard(){
    int currentShards = getCurrentShardCount();
    int required = calculateRequiredShards();
    
    if(required > currentShards){
        jdbcTemplate.execute("ALTER TABLE task_shards AUTO_INCREMENT = "+ required);
    }
}

结果校验机制

java 复制代码
public void validateResults() {
    jdbcTemplate.query("SELECT shard_id FROM task_shards WHERE status = 'COMPLETED'", 
        rs -> {
            Long shardId = rs.getLong(1);
            if(!resultCache.contains(shardId)) {
                repairShard(shardId);
            }
        });
}

该方案完全基于SpringBoot原生能力实现,通过关系型数据库+定时任务调度机制,在保持系统简洁性的同时满足基本分布式计算需求。适合中小规模(日处理千万级以下)的离线计算场景,如需更高性能建议仍考虑引入专业分布式计算框架。

相关推荐
初次攀爬者14 小时前
RocketMQ在Spring Boot上的基础使用
java·spring boot·rocketmq
花花无缺14 小时前
搞懂@Autowired 与@Resuorce
java·spring boot·后端
Derek_Smart16 小时前
从一次 OOM 事故说起:打造生产级的 JVM 健康检查组件
java·jvm·spring boot
Nyarlathotep01131 天前
SpringBoot Starter的用法以及原理
java·spring boot
dkbnull2 天前
深入理解Spring两大特性:IoC和AOP
spring boot
洋洋技术笔记2 天前
Spring Boot条件注解详解
java·spring boot
洋洋技术笔记3 天前
Spring Boot配置管理最佳实践
spring boot
用户8307196840824 天前
Spring Boot 项目中日期处理的最佳实践
java·spring boot
初次攀爬者4 天前
ZooKeeper 实现分布式锁的两种方式
分布式·后端·zookeeper
大道至简Edward4 天前
Spring Boot 2.7 + JDK 8 升级到 Spring Boot 3.x + JDK 17 完整指南
spring boot·后端