一、Lambda架构概述
Lambda架构是一种大数据架构模式,通过批处理层和流处理层分别处理历史数据和实时数据,最终通过服务层提供统一的查询接口。Lambda架构能够兼顾批处理的准确性和流处理的实时性。
二、Lambda架构组成
2.1 Lambda架构图
graph TD
A[数据源] --> B[批处理层]
A --> C[流处理层]
B --> D[批处理视图]
C --> E[实时视图]
D --> F[服务层]
E --> F
F --> G[查询接口]
G --> H[用户查询]
B --> B1[Spark/Hadoop]
C --> C1[Flink/Kafka Streams]
2.2 Lambda架构层次
| 层次 | 职责 | 技术选型 | 数据延迟 |
|---|---|---|---|
| 批处理层 | 处理历史数据,生成批处理视图 | Spark、Hadoop | 小时/天 |
| 流处理层 | 处理实时数据,生成实时视图 | Flink、Kafka Streams | 秒/分钟 |
| 服务层 | 合并批处理视图和实时视图 | Redis、Elasticsearch | 毫秒 |
三、批处理层
3.1 批处理层职责
批处理层负责处理全量历史数据,生成批处理视图:
flowchart TD
A[原始数据] --> B[数据存储]
B --> C[批处理作业]
C --> D[批处理视图]
B --> B1[HDFS]
B --> B2[S3]
C --> C1[Spark作业]
C --> C2[MapReduce作业]
D --> D1[数据仓库]
D --> D2[分析数据库]
3.2 批处理作业实现
public class BatchProcessingJob
{
public void RunDailyBatch(DateTime date)
{
var spark = SparkSession.Builder()
.AppName("DailyBatch")
.GetOrCreate();
var rawData = spark.Read()
.Parquet($"hdfs:///data/raw/{date:yyyy-MM-dd}");
var processedData = rawData
.Filter("event_type = 'order'")
.GroupBy("user_id")
.Agg(
Sum("amount").Alias("total_amount"),
Count("order_id").Alias("order_count")
);
processedData.Write()
.Mode(SaveMode.Overwrite)
.Parquet($"hdfs:///data/batch_view/{date:yyyy-MM-dd}");
spark.Stop();
}
public void RunFullRebuild()
{
var spark = SparkSession.Builder()
.AppName("FullRebuild")
.GetOrCreate();
var allData = spark.Read()
.Parquet("hdfs:///data/raw/*");
var batchView = allData
.GroupBy("user_id")
.Agg(
Sum("amount").Alias("total_amount"),
Count("order_id").Alias("order_count")
);
batchView.Write()
.Mode(SaveMode.Overwrite)
.Parquet("hdfs:///data/batch_view/latest");
spark.Stop();
}
}
3.3 批处理调度
public class BatchScheduler
{
public void ScheduleDailyBatch()
{
var scheduler = new Quartz.StdSchedulerFactory().GetScheduler().Result;
var job = JobBuilder.Create<DailyBatchJob>()
.WithIdentity("DailyBatch", "Batch")
.Build();
var trigger = TriggerBuilder.Create()
.WithIdentity("DailyBatchTrigger", "Batch")
.WithCronSchedule("0 0 2 * * ?")
.Build();
scheduler.ScheduleJob(job, trigger).Wait();
scheduler.Start().Wait();
}
public void ScheduleFullRebuild()
{
var scheduler = new Quartz.StdSchedulerFactory().GetScheduler().Result;
var job = JobBuilder.Create<FullRebuildJob>()
.WithIdentity("FullRebuild", "Batch")
.Build();
var trigger = TriggerBuilder.Create()
.WithIdentity("FullRebuildTrigger", "Batch")
.WithCronSchedule("0 0 2 * * 0")
.Build();
scheduler.ScheduleJob(job, trigger).Wait();
}
}
四、流处理层
4.1 流处理层职责
流处理层负责处理实时数据,生成实时视图:
flowchart TD
A[实时数据] --> B[消息队列]
B --> C[流处理作业]
C --> D[实时视图]
B --> B1[Kafka]
B --> B2[Pulsar]
C --> C1[Flink作业]
C --> C2[Kafka Streams]
D --> D1[Redis]
D --> D2[内存数据库]
4.2 Flink流处理实现
public class StreamingProcessingJob
{
public void RunStreamingJob()
{
var env = StreamExecutionEnvironment.GetExecutionEnvironment();
var stream = env
.AddSource(new FlinkKafkaConsumer<String>(
"orders",
new SimpleStringSchema(),
GetKafkaConfig()
));
var orderStream = stream
.Map(json => JsonSerializer.Deserialize<Order>(json))
.Filter(order => order.Status == OrderStatus.Completed);
var userAggregations = orderStream
.KeyBy(order => order.UserId)
.Window(TumblingEventTimeWindows.Of(Time.Minutes(5)))
.Aggregate(
new OrderAggregateFunction(),
new OrderWindowFunction()
);
userAggregations
.Map(agg => JsonSerializer.Serialize(agg))
.AddSink(new RedisSink<String>(GetRedisConfig()));
env.Execute("OrderStreamingJob");
}
private Configuration GetKafkaConfig()
{
var config = new Configuration();
config.SetProperty("bootstrap.servers", "kafka:9092");
config.SetProperty("group.id", "flink-streaming");
return config;
}
}
4.3 Kafka Streams实现
public class KafkaStreamsProcessing
{
public void RunKafkaStreams()
{
var config = new StreamsConfig(GetProperties());
var builder = new KStreamBuilder();
var orderStream = builder.Stream<String, String>("orders");
orderStream
.Filter((key, value) =>
{
var order = JsonSerializer.Deserialize<Order>(value);
return order.Status == OrderStatus.Completed;
})
.GroupByKey()
.WindowedBy(TimeWindows.Of(TimeUnit.MINUTES.toMillis(5)))
.Aggregate(
() => new UserAggregation(),
(key, value, aggregate) =>
{
var order = JsonSerializer.Deserialize<Order>(value);
aggregate.TotalAmount += order.Amount;
aggregate.OrderCount++;
return aggregate;
},
Materialized.<String, UserAggregation, WindowStore>("user-aggregations")
)
.ToStream()
.Map((key, value) =>
KeyValue.pair(key.key(), JsonSerializer.Serialize(value)))
.To("user-aggregations-output");
var streams = new KafkaStreams(builder, config);
streams.Start();
}
private Properties GetProperties()
{
var props = new Properties();
props.SetProperty(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, "kafka:9092");
props.SetProperty(StreamsConfig.APPLICATION_ID_CONFIG, "kafka-streams-app");
return props;
}
}
五、服务层
5.1 服务层职责
服务层负责合并批处理视图和实时视图,提供统一的查询接口:
flowchart TD
A[用户查询] --> B[查询服务]
B --> C[批处理视图]
B --> D[实时视图]
C --> E[合并结果]
D --> E
E --> F[返回结果]
5.2 服务层实现
public class QueryService
{
private readonly IBatchViewRepository _batchViewRepository;
private readonly IRealTimeViewRepository _realTimeViewRepository;
public async Task<UserAggregation> GetUserAggregation(Guid userId)
{
var batchAggregation = await _batchViewRepository.GetByIdAsync(userId);
var realTimeAggregation = await _realTimeViewRepository.GetByIdAsync(userId);
return MergeAggregations(batchAggregation, realTimeAggregation);
}
private UserAggregation MergeAggregations(UserAggregation batch, UserAggregation realTime)
{
if (batch == null)
return realTime;
if (realTime == null)
return batch;
return new UserAggregation
{
UserId = batch.UserId,
TotalAmount = batch.TotalAmount + realTime.TotalAmount,
OrderCount = batch.OrderCount + realTime.OrderCount,
LastUpdateTime = DateTime.UtcNow
};
}
public async Task<List<UserAggregation>> GetTopUsers(int limit)
{
var batchUsers = await _batchViewRepository.GetTopUsers(limit);
var realTimeUsers = await _realTimeViewRepository.GetTopUsers(limit);
var merged = MergeUserLists(batchUsers, realTimeUsers);
return merged.OrderByDescending(u => u.TotalAmount).Take(limit).ToList();
}
private List<UserAggregation> MergeUserLists(List<UserAggregation> batch, List<UserAggregation> realTime)
{
var dictionary = new Dictionary<Guid, UserAggregation>();
foreach (var user in batch)
{
dictionary[user.UserId] = user;
}
foreach (var user in realTime)
{
if (dictionary.TryGetValue(user.UserId, out var existing))
{
existing.TotalAmount += user.TotalAmount;
existing.OrderCount += user.OrderCount;
}
else
{
dictionary[user.UserId] = user;
}
}
return dictionary.Values.ToList();
}
}
5.3 查询缓存
public class CachedQueryService
{
private readonly QueryService _queryService;
private readonly IDistributedCache _cache;
public async Task<UserAggregation> GetUserAggregation(Guid userId)
{
var cacheKey = $"user_aggregation:{userId}";
var cached = await _cache.GetStringAsync(cacheKey);
if (!string.IsNullOrEmpty(cached))
return JsonSerializer.Deserialize<UserAggregation>(cached);
var result = await _queryService.GetUserAggregation(userId);
await _cache.SetStringAsync(cacheKey, JsonSerializer.Serialize(result),
new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5) });
return result;
}
}
六、Lambda架构数据流
6.1 完整数据流
sequenceDiagram
participant Source as 数据源
participant Batch as 批处理层
participant Stream as 流处理层
participant BatchView as 批处理视图
participant RealTimeView as 实时视图
participant Service as 服务层
participant User as 用户
Source->>Batch: 历史数据
Source->>Stream: 实时数据
Batch->>BatchView: 生成批处理视图
Stream->>RealTimeView: 生成实时视图
User->>Service: 查询请求
Service->>BatchView: 获取批处理数据
Service->>RealTimeView: 获取实时数据
Service->>Service: 合并数据
Service-->>User: 返回结果
Batch->>RealTimeView: 批处理结果覆盖实时视图
七、Lambda架构优势与挑战
7.1 优势
| 优势 | 描述 |
|---|---|
| 批流分离 | 批处理和流处理可以独立优化 |
| 数据完整 | 批处理保证数据准确性 |
| 实时性 | 流处理提供实时数据 |
| 可扩展 | 批流可以独立扩展 |
| 容错性 | 批处理可以修复流处理错误 |
7.2 挑战
| 挑战 | 描述 | 解决方案 |
|---|---|---|
| 代码重复 | 批流逻辑需要重复实现 | 流批一体框架 |
| 复杂性 | 需要维护两套系统 | 统一平台 |
| 延迟 | 批处理延迟高 | 缩短批处理周期 |
| 一致性 | 批流结果可能不一致 | 使用相同的处理逻辑 |
八、Kappa架构
8.1 Kappa架构概述
Kappa架构是Lambda架构的简化版本,使用流处理统一处理实时和历史数据:
graph TD
A[数据源] --> B[消息队列]
B --> C[流处理层]
C --> D[服务层]
B --> B1[持久化存储]
C --> C1[实时处理]
C --> C2[历史重放]
D --> D1[查询接口]
8.2 Kappa架构实现
public class KappaArchitectureService
{
public void RunStreamProcessing()
{
var env = StreamExecutionEnvironment.GetExecutionEnvironment();
var stream = env
.AddSource(new FlinkKafkaConsumer<String>(
"orders",
new SimpleStringSchema(),
GetKafkaConfig()
))
.SetParallelism(1)
.AssignTimestampsAndWatermarks(new OrderTimestampExtractor());
var processedStream = stream
.Map(json => JsonSerializer.Deserialize<Order>(json))
.Filter(order => order.Status == OrderStatus.Completed);
var userAggregations = processedStream
.KeyBy(order => order.UserId)
.Window(TumblingEventTimeWindows.Of(Time.Days(1)))
.Aggregate(
new OrderAggregateFunction(),
new OrderWindowFunction()
);
userAggregations
.Map(agg => JsonSerializer.Serialize(agg))
.AddSink(new ElasticsearchSink<String>(GetEsConfig()));
env.Execute("KappaStreamingJob");
}
public void RunHistoricalReplay(DateTime startDate, DateTime endDate)
{
var env = StreamExecutionEnvironment.GetExecutionEnvironment();
var historicalStream = env
.AddSource(new FlinkKafkaConsumer<String>(
"orders",
new SimpleStringSchema(),
GetKafkaConfig(startDate, endDate)
));
historicalStream
.Map(json => JsonSerializer.Deserialize<Order>(json))
.Filter(order => order.Status == OrderStatus.Completed)
.KeyBy(order => order.UserId)
.Window(TumblingEventTimeWindows.Of(Time.Days(1)))
.Aggregate(
new OrderAggregateFunction(),
new OrderWindowFunction()
)
.Map(agg => JsonSerializer.Serialize(agg))
.AddSink(new ElasticsearchSink<String>(GetEsConfig()));
env.Execute("HistoricalReplayJob");
}
}
8.3 Lambda vs Kappa
| 特性 | Lambda | Kappa |
|---|---|---|
| 处理方式 | 批处理+流处理 | 统一流处理 |
| 代码重复 | 是 | 否 |
| 复杂度 | 高 | 低 |
| 批处理性能 | 高 | 中等 |
| 适用场景 | 复杂分析 | 实时分析 |
九、流批一体
9.1 流批一体概述
流批一体是一种新的大数据处理架构,使用统一的引擎处理批处理和流处理:
graph TD
A[统一处理引擎] --> B[批处理模式]
A --> C[流处理模式]
B --> D[批处理作业]
C --> E[流处理作业]
D --> F[统一视图]
E --> F
9.2 Flink流批一体
public class UnifiedProcessingJob
{
public void RunUnifiedJob(ProcessingMode mode)
{
var env = mode switch
{
ProcessingMode.Batch => ExecutionEnvironment.GetExecutionEnvironment(),
ProcessingMode.Streaming => StreamExecutionEnvironment.GetExecutionEnvironment(),
_ => throw new ArgumentOutOfRangeException()
};
var data = mode switch
{
ProcessingMode.Batch => env.ReadTextFile("hdfs:///data/orders"),
ProcessingMode.Streaming => env.AddSource(new FlinkKafkaConsumer<String>(
"orders",
new SimpleStringSchema(),
GetKafkaConfig()
)),
_ => throw new ArgumentOutOfRangeException()
};
var processedData = data
.Map(json => JsonSerializer.Deserialize<Order>(json))
.Filter(order => order.Status == OrderStatus.Completed)
.GroupBy(order => order.UserId)
.Aggregate(
new OrderAggregateFunction(),
new OrderWindowFunction()
);
processedData
.Map(agg => JsonSerializer.Serialize(agg))
.WriteAsText("hdfs:///data/results");
env.Execute("UnifiedProcessingJob");
}
}
public enum ProcessingMode { Batch, Streaming }
十、Lambda架构最佳实践
10.1 使用统一的数据模型
批处理和流处理使用相同的数据模型和处理逻辑。
10.2 缩短批处理周期
尽量缩短批处理周期,减少数据延迟。
10.3 使用流批一体框架
使用Flink等流批一体框架,减少代码重复。
10.4 监控数据流
监控批处理和流处理的状态,及时发现问题。
10.5 选择合适的架构
根据业务需求选择Lambda或Kappa架构。
十一、总结
Lambda架构是一种经典的大数据架构,通过批处理层和流处理层分别处理历史数据和实时数据。Kappa架构是Lambda架构的简化版本,使用统一的流处理引擎。流批一体是未来的发展方向,使用统一的引擎处理批处理和流处理。选择合适的架构需要根据业务需求和技术栈来决定。