在AWS Glue中实现缓慢变化维度(SCD)的三种类型
根据缓慢变化维度(SCD)的三种核心类型(类型1、类型2、类型3),以下是基于AWS Glue的实现设计、步骤及测试用例:
一、AWS Glue实现SCD的设计与步骤
1. SCD类型1(覆盖旧值)
设计目标:直接更新目标表中的记录,不保留历史数据。
技术选型:
- 使用AWS Glue ETL作业(PySpark)
- 目标存储:S3(Parquet格式)或Amazon Redshift
- 数据比对方式:基于业务键(如
customer_id
)匹配新旧记录
实现步骤:
-
数据源准备:
- 源表(Source):实时更新的客户表(如CSV或数据库表)。
- 目标表(Target):维度表(如
dim_customer
)。
-
Glue作业逻辑:
from pyspark.context import SparkContext from awsglue.context import GlueContextsc = SparkContext() glueContext = GlueContext(sc)# 加载源数据和目标数据 source_df = glueContext.create_dynamic_frame.from_catalog(database="source_db", table_name="customer").toDF() target_df = glueContext.create_dynamic_frame.from_catalog(database="target_db", table_name="dim_customer").toDF()# 合并逻辑:覆盖旧值 merged_df = target_df.alias("target").join(source_df.alias("source"),target_df.customer_id == source_df.customer_id,"outer" ).selectExpr("coalesce(source.customer_id, target.customer_id) as customer_id","source.name as name", # 直接覆盖"source.address as address" # 直接覆盖 ).distinct()# 写入目标表(覆盖模式) glueContext.write_dynamic_frame.from_catalog(frame=DynamicFrame.fromDF(merged_df, glueContext, "merged_df"),database="target_db",table_name="dim_customer",transformation_ctx="write_target" )
-
目标表结构:
CREATE TABLE dim_customer (customer_id INT PRIMARY KEY,name STRING,address STRING );
2. SCD类型2(创建新记录)
设计目标:插入新记录并标记历史版本。
技术选型:
- 使用Glue的窗口函数(
row_number
)跟踪最新记录 - 新增字段:
is_current
(布尔值)、start_date
、end_date
- 存储格式:S3 + Parquet(支持ACID事务)
实现步骤:
-
目标表结构:
CREATE TABLE dim_customer_scd2 (customer_sk INT AUTOINCREMENT PRIMARY KEY, -- 代理键customer_id INT,name STRING,address STRING,is_current BOOLEAN,start_date DATE,end_date DATE );
-
Glue作业逻辑:
from pyspark.sql.window import Window from pyspark.sql.functions import row_number, lit, current_date, when# 加载源数据和目标数据 source_df = glueContext.create_dynamic_frame.from_catalog(database="source_db", table_name="customer").toDF() target_df = glueContext.create_dynamic_frame.from_catalog(database="target_db", table_name="dim_customer_scd2").toDF()# 标记目标表中的旧记录为失效 target_updated = target_df.withColumn("end_date",when((target_df.customer_id.isin(source_df.select("customer_id").collect())) &(target_df.is_current == True),current_date()).otherwise(target_df.end_date) ).withColumn("is_current",when((target_df.customer_id.isin(source_df.select("customer_id").collect())) &(target_df.is_current == True),False).otherwise(target_df.is_current) )# 插入新记录 new_records = source_df.join(target_updated,["customer_id"],"left_anti" # 仅选择源中存在但目标中不存在的记录 ).select("customer_id","name","address",lit(True).alias("is_current"),current_date().alias("start_date"),lit(None).cast("date").alias("end_date") )# 合并并写入目标表 final_df = target_updated.unionByName(new_records) glueContext.write_dynamic_frame.from_catalog(frame=DynamicFrame.fromDF(final_df, glueContext, "final_df"),database="target_db",table_name="dim_customer_scd2" )
3. SCD类型3(添加有效日期)
设计目标:维护当前记录和历史记录的有效时间范围。
技术选型:
- 新增字段:
valid_from
、valid_to
- 使用Glue的
coalesce
处理时间重叠
实现步骤:
-
目标表结构:
CREATE TABLE dim_customer_scd3 (customer_sk INT PRIMARY KEY,customer_id INT,name STRING,address STRING,valid_from DATE,valid_to DATE );
-
Glue作业逻辑:
# 关闭旧记录的valid_to target_updated = target_df.withColumn("valid_to",when((target_df.customer_id.isin(source_df.select("customer_id").collect())) &(target_df.valid_to.isNull()),current_date()).otherwise(target_df.valid_to) )# 插入新记录 new_records = source_df.select("customer_id","name","address",current_date().alias("valid_from"),lit(None).cast("date").alias("valid_to") )# 合并并写入 final_df = target_updated.unionByName(new_records) glueContext.write_dynamic_frame.from_catalog(...)
二、测试用例
通用测试场景
测试场景 | 预期结果 |
---|---|
无变化的记录 | 目标表记录保持不变。 |
新增记录 | 目标表插入新记录(类型2/3新增代理键,类型1直接插入)。 |
属性值变化 | 类型1覆盖旧值;类型2插入新记录并标记旧记录失效;类型3关闭旧记录有效期。 |
多次更新同一记录 | 类型2生成多条历史记录;类型3仅保留当前和上一次状态。 |
类型2专项测试
-
历史版本查询:
SELECT * FROM dim_customer_scd2 WHERE customer_id = 100 ORDER BY start_date DESC;
预期:返回该客户的所有历史地址记录。
-
当前标记验证:
SELECT COUNT(*) FROM dim_customer_scd2 WHERE customer_id = 100 AND is_current = True;
预期:仅返回1条记录。
类型3专项测试
- 时间范围覆盖:
预期:返回该时间点有效的记录。SELECT * FROM dim_customer_scd3 WHERE customer_id = 200 AND valid_from <= '2023-10-01' AND valid_to >= '2023-10-01';
三、性能优化建议
- 分区策略:按日期或业务键分区目标表(如
valid_from
)。 - 索引优化:在Redshift中为
customer_id
和is_current
列创建排序键。 - 增量处理:启用Glue Job Bookmark仅处理新增数据。