中国电网Java面试被问:流批一体架构的实现和状态管理

一、流批一体架构概览

1. 核心设计原则

text

复制

下载

复制代码
同一套API:一套代码既能处理流也能处理批
统一状态管理:流批共享状态存储和访问方式
统一计算引擎:底层引擎同时支持流批处理
统一元数据:一致的schema管理和数据目录

2. 架构演进对比

图表

代码

复制

下载

全屏

复制代码
graph TD
    A[传统Lambda架构] --> B[流批一体架构]
    
    subgraph A [Lambda架构:两套系统]
        A1[批处理层] --> A2[批处理视图]
        A3[速度层] --> A4[实时视图]
        A2 --> A5[服务层]
        A4 --> A5
    end
    
    subgraph B [流批一体:统一系统]
        B1[统一计算层] --> B2[统一状态存储]
        B2 --> B3[统一服务层]
    end

二、核心实现技术栈

1. 主流实现框架

python

复制

下载

复制代码
# Apache Flink(最成熟的流批一体)
from pyflink.datastream import StreamExecutionEnvironment
from pyflink.table import StreamTableEnvironment

env = StreamExecutionEnvironment.get_execution_environment()
t_env = StreamTableEnvironment.create(env)

# 同一套Table API处理流和批
# 流处理
t_env.execute_sql("""
    CREATE TABLE orders_stream (
        order_id STRING,
        user_id BIGINT,
        amount DOUBLE,
        order_time TIMESTAMP(3)
    ) WITH (
        'connector' = 'kafka',
        'scan.startup.mode' = 'earliest-offset'
    )
""")

# 批处理
t_env.execute_sql("""
    CREATE TABLE orders_batch (
        order_id STRING,
        user_id BIGINT,
        amount DOUBLE,
        order_time TIMESTAMP(3)
    ) WITH (
        'connector' = 'filesystem',
        'format' = 'parquet'
    )
""")

2. 统一API层设计

java

复制

下载

复制代码
// 以Apache Beam为例的跨引擎API
PipelineOptions options = PipelineOptionsFactory.create();

// 批处理模式
options.setRunner(DirectRunner.class);

// 流处理模式  
options.setRunner(FlinkRunner.class);
options.as(FlinkPipelineOptions.class).setStreaming(true);

// 同一套Pipeline定义
Pipeline p = Pipeline.create(options);
p.apply(TextIO.read().from("input.txt"))
 .apply(ParDo.of(new DoFn<String, String>() {
     @ProcessElement
     public void processElement(ProcessContext c) {
         c.output(c.element().toUpperCase());
     }
 }))
 .apply(TextIO.write().to("output.txt"));

三、统一状态管理实现

1. 状态存储架构

text

复制

下载

复制代码
┌─────────────────────────────────────────┐
│          应用层(流/批处理)              │
├─────────────────────────────────────────┤
│    统一状态API(KeyedState/OperatorState)│
├─────────────────────────────────────────┤
│  状态后端抽象(StateBackend Interface)    │
├─────────────────────────────────────────┤
│  RocksDB      │  Heap      │  Filesystem │
│  (流式优化)    │ (批处理优化)│ (检查点存储)  │
└─────────────────────────────────────────┘

篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc

需要全套面试笔记及答案
【点击此处即可/免费获取】

2. 状态后端实现

java

复制

下载

复制代码
// Flink状态后端配置示例
StreamExecutionEnvironment env = StreamExecutionEnvironment.get_execution_environment();

// RocksDBStateBackend - 适合大规模状态
env.setStateBackend(new RocksDBStateBackend(
    "hdfs://namenode:40010/flink/checkpoints",
    true  // 异步快照
));

// FsStateBackend - 适合中小规模状态
env.setStateBackend(new FsStateBackend(
    "hdfs://namenode:40010/flink/checkpoints",
    false  // 是否异步
));

// HeapStateBackend - 适合测试和小规模
env.setStateBackend(new MemoryStateBackend());

// 统一状态访问
DataStream<Tuple2<String, Integer>> stream = ...;
stream.keyBy(0)
    .flatMap(new RichFlatMapFunction<Tuple2<String, Integer>, Tuple2<String, Integer>>() {
        
        private transient ValueState<Integer> sumState;
        
        @Override
        public void open(Configuration parameters) {
            // 定义状态描述符
            ValueStateDescriptor<Integer> descriptor = 
                new ValueStateDescriptor<>("sum", Integer.class);
            sumState = getRuntimeContext().getState(descriptor);
        }
        
        @Override
        public void flatMap(Tuple2<String, Integer> value, Collector<Tuple2<String, Integer>> out) {
            Integer currentSum = sumState.value();
            if (currentSum == null) {
                currentSum = 0;
            }
            currentSum += value.f1;
            sumState.update(currentSum);
            out.collect(Tuple2.of(value.f0, currentSum));
        }
    });

3. 状态持久化和恢复

java

复制

下载

复制代码
// 检查点配置
env.enableCheckpointing(60000); // 每60秒做一次checkpoint
env.getCheckpointConfig().setCheckpointingMode(CheckpointingMode.EXACTLY_ONCE);
env.getCheckpointConfig().setCheckpointTimeout(30000);
env.getCheckpointConfig().setMinPauseBetweenCheckpoints(500);
env.getCheckpointConfig().setMaxConcurrentCheckpoints(1);
env.getCheckpointConfig().enableExternalizedCheckpoints(
    ExternalizedCheckpointCleanup.RETAIN_ON_CANCELLATION);

// 保存点(Savepoint)管理
String savepointPath = "hdfs:///savepoints/savepoint-123";
// 触发保存点
String triggerSavepoint = env.triggerSavepoint(savepointPath);

// 从保存点恢复
StreamExecutionEnvironment restoredEnv = 
    StreamExecutionEnvironment.get_execution_environment();
restoredEnv.setStateBackend(...);
restoredEnv.restoreState(savepointPath);

四、流批统一的计算模型

1. 统一窗口机制

sql

复制

下载

复制代码
-- Apache Flink SQL的窗口语法
-- 流处理窗口
SELECT 
    user_id,
    TUMBLE_START(order_time, INTERVAL '1' HOUR) as window_start,
    COUNT(*) as order_count,
    SUM(amount) as total_amount
FROM orders_stream
GROUP BY 
    user_id,
    TUMBLE(order_time, INTERVAL '1' HOUR)

-- 批处理窗口(历史数据重计算)
SELECT 
    user_id,
    TUMBLE_START(order_time, INTERVAL '1' HOUR) as window_start,
    COUNT(*) as order_count,
    SUM(amount) as total_amount
FROM orders_batch
GROUP BY 
    user_id,
    TUMBLE(order_time, INTERVAL '1' HOUR)

-- 事件时间处理统一
CREATE TABLE orders_with_watermark (
    order_id STRING,
    user_id BIGINT,
    amount DOUBLE,
    order_time TIMESTAMP(3),
    WATERMARK FOR order_time AS order_time - INTERVAL '5' SECOND
) WITH (...);

2. 时间语义统一

python

复制

下载

复制代码
# 使用同一套时间处理API
from pyflink.common import WatermarkStrategy, TimeCharacteristic
from pyflink.common.watermark_strategy import TimestampAssigner

# 事件时间处理(流批统一)
class OrderTimestampAssigner(TimestampAssigner):
    def extract_timestamp(self, value, record_timestamp):
        return value.order_time

# 流处理
stream_env.set_stream_time_characteristic(TimeCharacteristic.EventTime)
watermark_strategy = WatermarkStrategy.for_bounded_out_of_orderness(
    Duration.of_seconds(5)
).with_timestamp_assigner(OrderTimestampAssigner())

# 批处理也使用相同的时间提取逻辑
batch_env.set_parallelism(1)

五、增量计算与状态维护

1. 增量处理模式

java

复制

下载

复制代码
// 使用MapState实现增量聚合
public class IncrementalAggregator extends 
    KeyedProcessFunction<String, Order, AggregationResult> {
    
    private transient MapState<Long, Double> hourlyState; // key: hour, value: sum
    
    @Override
    public void open(Configuration parameters) {
        MapStateDescriptor<Long, Double> descriptor = 
            new MapStateDescriptor<>("hourly-amount", Long.class, Double.class);
        hourlyState = getRuntimeContext().getMapState(descriptor);
    }
    
    @Override
    public void processElement(Order order, Context ctx, 
                              Collector<AggregationResult> out) throws Exception {
        
        long hour = order.getTimestamp() / (60 * 60 * 1000);
        
        // 增量更新
        Double current = hourlyState.get(hour);
        if (current == null) {
            current = 0.0;
        }
        current += order.getAmount();
        hourlyState.put(hour, current);
        
        // 输出当前聚合结果
        out.collect(new AggregationResult(order.getUserId(), hour, current));
    }
}

2. 状态TTL管理

java

复制

下载

复制代码
// 状态生存时间配置
StateTtlConfig ttlConfig = StateTtlConfig
    .newBuilder(Time.hours(24))  // 24小时TTL
    .setUpdateType(StateTtlConfig.UpdateType.OnCreateAndWrite)  // 更新时刷新TTL
    .setStateVisibility(StateTtlConfig.StateVisibility.NeverReturnExpired) // 不返回过期数据
    .cleanupInBackground()  // 后台清理
    .build();

ValueStateDescriptor<Integer> descriptor = 
    new ValueStateDescriptor<>("user-session", Integer.class);
descriptor.enableTimeToLive(ttlConfig);

六、数据一致性保障

1. 端到端Exactly-Once实现

java

复制

下载

复制代码
// 两阶段提交实现端到端一致性
public class TwoPhaseCommitSink extends RichSinkFunction<Order> 
    implements CheckpointedFunction, CheckpointListener {
    
    private transient Connection connection;
    private transient PreparedStatement statement;
    private transient List<Order> pendingRecords;
    
    @Override
    public void invoke(Order value, Context context) throws Exception {
        // 添加到待提交列表
        pendingRecords.add(value);
    }
    
    @Override
    public void snapshotState(FunctionSnapshotContext context) throws Exception {
        // 第一阶段:预提交
        connection.setAutoCommit(false);
        for (Order order : pendingRecords) {
            statement.setString(1, order.getId());
            statement.setDouble(2, order.getAmount());
            statement.addBatch();
        }
        statement.executeBatch();
        // 不提交,等待checkpoint完成
    }
    
    @Override
    public void notifyCheckpointComplete(long checkpointId) throws Exception {
        // 第二阶段:正式提交
        connection.commit();
        pendingRecords.clear();
    }
    
    @Override
    public void initializeState(FunctionInitializationContext context) throws Exception {
        // 状态恢复
        if (context.isRestored()) {
            // 从检查点恢复状态
            ListState<Order> restoredState = context.getOperatorStateStore()
                .getListState(new ListStateDescriptor<>("pending-records", Order.class));
            for (Order order : restoredState.get()) {
                pendingRecords.add(order);
            }
        }
    }
}

2. 幂等性写入

python

复制

下载

复制代码
# 基于主键的幂等写入
class IdempotentDatabaseSink:
    def __init__(self, connection_params):
        self.conn = create_connection(connection_params)
        self.processed_ids = set()  # 已处理ID集合
        
    def write_with_idempotency(self, records):
        with self.conn.cursor() as cursor:
            for record in records:
                # 检查是否已处理
                if record.id in self.processed_ids:
                    continue
                    
                # 使用INSERT ON CONFLICT或MERGE实现幂等
                sql = """
                    INSERT INTO orders (id, user_id, amount, timestamp)
                    VALUES (%s, %s, %s, %s)
                    ON CONFLICT (id) DO NOTHING
                """
                cursor.execute(sql, (record.id, record.user_id, 
                                    record.amount, record.timestamp))
                
                self.processed_ids.add(record.id)
            
            self.conn.commit()

七、架构实践案例

1. 电商实时数仓流批一体实现

yaml

复制

下载

复制代码
# 架构配置
architecture:
  processing_engine: flink
  state_backend: rocksdb
  metastore: hive
  
layers:
  ods:
    source: kafka
    format: json
    watermark: event_time
    
  dwd:
    processing: 
      - deduplication
      - dimension_join
      - normalization
    state: 
      ttl: 7d
      backend: rocksdb
      
  dws:
    windows:
      - tumbling_1h
      - sliding_5m
      - session_10m
    aggregations:
      - count
      - sum
      - uv
      
  ads:
    serving: 
      - mysql
      - redis
      - clickhouse
    refresh: 
      streaming: incremental
      batch: full

sql

复制

下载

复制代码
-- 创建Iceberg表
CREATE CATALOG hive_catalog WITH (
  'type'='iceberg',
  'catalog-type'='hive',
  'uri'='thrift://localhost:9083',
  'warehouse'='hdfs://localhost:9000/warehouse'
);

USE CATALOG hive_catalog;

-- 流式写入
INSERT INTO user_behavior_iceberg
SELECT user_id, item_id, action, event_time
FROM kafka_user_behavior;

-- 批量读取(同一张表)
SELECT user_id, COUNT(*) as action_count
FROM user_behavior_iceberg
WHERE event_date = '2024-01-01'
GROUP BY user_id;

-- 时间旅行查询
SELECT * FROM user_behavior_iceberg 
FOR SYSTEM_TIME AS OF timestamp '2024-01-01 10:00:00';

-- 增量读取
SELECT * FROM user_behavior_iceberg
WHERE event_date >= '2024-01-01' 
  AND __change_type != 'DELETE';

八、性能优化策略

1. 状态优化

java

复制

下载

复制代码
// 1. 状态序列化优化
env.getConfig().registerTypeWithKryoSerializer(
    UserBehavior.class, 
    CustomKryoSerializer.class
);

// 2. 状态分区优化
DataStream<Tuple2<String, Integer>> partitionedStream = 
    stream.keyBy(new KeySelector<Tuple2<String, Integer>, String>() {
        @Override
        public String getKey(Tuple2<String, Integer> value) {
            // 自定义分区逻辑
            return value.f0.substring(0, 3); // 前3字符作为key
        }
    });

// 3. RocksDB优化配置
RocksDBStateBackend backend = new RocksDBStateBackend("hdfs://checkpoints");
backend.setDbStoragePath("/tmp/rocksdb");  // 本地存储路径
backend.setPredefinedOptions(PredefinedOptions.SPINNING_DISK_OPTIMIZED);

2. 检查点优化

java

复制

下载

复制代码
// 对齐优化
env.getCheckpointConfig().enableUnalignedCheckpoints();
env.getCheckpointConfig().setAlignedCheckpointTimeout(Duration.ofMillis(100));

// 增量检查点(RocksDB)
RocksDBStateBackend backend = (RocksDBStateBackend) env.getStateBackend();
backend.enableIncrementalCheckpointing(true);

// 检查点压缩
env.getCheckpointConfig().enableExternalizedCheckpoints(
    ExternalizedCheckpointCleanup.RETAIN_ON_CANCELLATION);
env.getCheckpointConfig().setMinPauseBetweenCheckpoints(5000);

篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc

需要全套面试笔记及答案
【点击此处即可/免费获取】

九、监控与运维

1. 状态监控指标

prometheus

复制

下载

复制代码
# Prometheus监控指标
flink_taskmanager_job_task_stateSize_bytes
flask_taskmanager_job_task_numRegisteredStates
flink_taskmanager_job_task_lastCheckpointSize
flink_jobmanager_job_lastCheckpointDuration
flink_taskmanager_job_task_checkpointAlignmentTime

# 自定义状态监控
public class StateMonitor extends RichMapFunction<String, String> {
    
    @Override
    public String map(String value) throws Exception {
        RuntimeContext ctx = getRuntimeContext();
        
        // 获取状态统计
        MetricGroup metrics = ctx.getMetricGroup();
        metrics.gauge("state-size", new Gauge<Long>() {
            @Override
            public Long getValue() {
                return estimateStateSize();
            }
        });
        
        return value;
    }
}

2. 自动扩缩容

yaml

复制

下载

复制代码
# Flink on K8s自动扩缩配置
apiVersion: flink.apache.org/v1beta1
kind: FlinkDeployment
metadata:
  name: stream-batch-unified
spec:
  flinkVersion: v1_17
  image: flink:1.17
  flinkConfiguration:
    taskmanager.numberOfTaskSlots: "2"
    kubernetes.operator.job.autoscaler.enabled: "true"
    kubernetes.operator.job.autoscaler.metric: "cpu"
    kubernetes.operator.job.autoscaler.target: "70"
    kubernetes.operator.job.autoscaler.stabilization.interval: "5m"

十、最佳实践总结

1. 状态设计原则

  • 最小化:只存储必要状态

  • 分区化:合理设计key避免数据倾斜

  • 生命周期管理:设置合适的TTL

  • 序列化优化:选择高效序列化方式

2. 流批统一要点

  • 使用事件时间作为统一时间语义

  • 实现增量计算而非全量重算

  • 设计幂等的输出结果

  • 保持流批处理逻辑一致性

3. 实施路径建议

text

复制

下载

复制代码
1. 评估阶段:分析现有Lambda架构痛点
2. 试点阶段:选择非核心业务进行验证
3. 扩展阶段:逐步迁移核心业务
4. 优化阶段:性能调优和监控完善
5. 治理阶段:建立标准和最佳实践

流批一体架构是现代数据处理的必然趋势,正确实现状态管理和统一计算模型是关键。建议从Flink或Spark Structured Streaming开始实践,逐步构建完整的流批一体数据平台。

相关推荐
lkbhua莱克瓦242 小时前
稠密、稀疏与MoE:大模型时代的三重架构革命
人工智能·深度学习·机器学习·ai·架构
1***43802 小时前
C++跨平台开发的核心挑战线程管理等基础功能
开发语言·c++
做萤石二次开发的哈哈2 小时前
萤石开放平台 萤石可编程设备 | 设备脚本自定义开发
开发语言·python·萤石云·萤石·萤石开放平台
倦王2 小时前
力扣日刷26112
算法·leetcode·职场和发展
程序员清风2 小时前
猿辅导二面:线上出现的OOM是如何排查的?
java·后端·面试
无风听海2 小时前
深入讲解 C# 中 string 如何支持 CultureInfo
开发语言·c#
yaoxin5211232 小时前
291. Java Stream API - 从正则表达式创建 Stream
java·开发语言
BHXDML2 小时前
Java 设计模式详解
java·开发语言·设计模式