下面结合Python及相关库(pandas、redshift_connector、awswrangler、openpyxl、boto3、pathlib和datetime)实现从本地读取Parquet文件、从Amazon Redshift导出数据到S3并下载、合并两个DataFrame并按指定列排序后导出至Excel文件(一个文件多个Sheet)的完整数据工程流程,并展开详细论述。所有涉及的库与实现思路均可在生产环境或数据分析项目中直接复用。
1. 环境准备
首先安装所需依赖包,并导入相关库:
pip install pandas pyarrow redshift_connector awswrangler openpyxl boto3importosimportpandasaspdimportredshift_connectorimportawswrangleraswrimportboto3fromdatetimeimportdatetimefrompathlibimportPath2. 从本地Parquet读取DataFrame(df_1)
利用pandas.read_parquet从本地读取parquet文件并将其加载为DataFrame:
parquet_path="./data/source_data.parquet"df_1=pd.read_parquet(parquet_path,engine="pyarrow")read_parquet默认使用pyarrow引擎,该引擎对列式存储的parquet文件有较好的性能表现。在处理大规模数据时,可通过设置use_threads=True启用多线程读取,或通过memory_map=True使用内存映射文件以提升读取速度。
3. 从Amazon Redshift通过UNLOAD导出数据到S3并下载为DataFrame(df_2)
此部分整合了redshift_connector、awswrangler、boto3、pathlib和datetime等库的功能。
3.1 建立Redshift连接
使用redshift_connector创建与Amazon Redshift集群的连接:
conn=redshift_connector.connect(host=os.getenv("REDSHIFT_HOST"),database=os.getenv("REDSHIFT_DATABASE"),port=int(os.getenv("REDSHIFT_PORT",5439)),user=os.getenv("REDSHIFT_USER"),password=os.getenv("REDSHIFT_PASSWORD"))此方式基于AWS凭证进行认证,符合Amazon Redshift Python连接器的标准用法。
3.2 利用awswrangler执行UNLOAD操作
awswrangler.redshift.unload_to_files封装了Redshift的UNLOAD命令,可将查询结果直接导出至S3(支持CSV或Parquet格式),适用于大规模数据导出场景。
使用带时间戳的S3路径以规避文件名冲突:
timestamp=datetime.now().strftime("%Y%m%d_%H%M%S")s3_unload_path=f"s3://your-bucket/redshift_unload/{timestamp}/"sql_query=""" SELECT id, column_a, column_b, created_at FROM your_schema.your_table WHERE created_at>= '2026-01-01' """wr.redshift.unload_to_files(sql=sql_query,path=s3_unload_path,con=conn,iam_role=os.getenv("REDSHIFT_IAM_ROLE"),unload_format="PARQUET",parallel=True,region=os.getenv("AWS_REGION","us-east-1"))UNLOAD路径设计说明:通过在S3路径中嵌入datetime.now().strftime("%Y%m%d_%H%M%S")生成的时间戳,可确保每次运行生成唯一路径,防止多次执行之间的文件覆盖。也可在UNLOAD命令中使用ALLOWOVERWRITE参数覆盖已有文件,但通过时间戳隔离版本是更为稳健的做法。
参数说明:
sql:执行的查询语句,支持任意复杂的SELECT语句。path:S3目标路径,UNLOAD会在此路径下生成一个或多个数据文件。con:通过redshift_connector建立的连接对象。iam_role:具有写入目标S3桶权限的IAM角色ARN,该角色至少需要s3:PutObject和s3:ListBucket权限。unload_format:导出格式,支持"CSV"或"PARQUET",此处选用"PARQUET"以获得更高的压缩率和更快的后续读取速度。parallel:是否并行写入多个文件,默认True可充分利用Redshift集群的切片数提升导出速度。region:S3桶所在的AWS区域。
3.3 从S3下载并读取为DataFrame
UNLOAD执行完毕后,使用boto3列出S3路径下生成的文件,并通过awswrangler.s3.read_parquet直接读取S3上的parquet文件为DataFrame:
session=boto3.Session(region_name=os.getenv("AWS_REGION","us-east-1"))df_2=wr.s3.read_parquet(path=s3_unload_path,boto3_session=session)awswrangler.s3.read_parquet可以自动识别S3路径下的所有parquet文件并将它们合并为单个DataFrame,极大简化了S3数据读取流程。
3.4 关闭Redshift连接
conn.close()4. 统一按id列排序
两个DataFrame均按照id列升序排列,以确保写入Excel后的数据具备一致性:
df_1=df_1.sort_values(by="id",ascending=True,ignore_index=True)df_2=df_2.sort_values(by="id",ascending=True,ignore_index=True)ignore_index=True可重置索引,避免将旧索引带入Excel输出。
5. 将两个DataFrame写入同一Excel文件的不同Sheet
使用pd.ExcelWriter配合openpyxl引擎(支持.xlsx格式),将两个DataFrame分别写入不同Sheet:
output_path=Path("./output/mult_sheets_1.xlsx")output_path.parent.mkdir(parents=True,exist_ok=True)withpd.ExcelWriter(output_path,engine="openpyxl")aswriter:df_1.to_excel(writer,sheet_name="df_1",index=False)df_2.to_excel(writer,sheet_name="df_2",index=False)with语句可确保Excel文件在写入完成后自动保存并关闭。sheet_name分别设置为"df_1"和"df_2",index=False避免将行索引写入Excel。如需在同一文件内写入更多DataFrame,只需继续调用to_excel方法即可。
6. 完整代码示例
将上述各模块整合为完整可运行的代码:
importosimportpandasaspdimportredshift_connectorimportawswrangleraswrimportboto3fromdatetimeimportdatetimefrompathlibimportPath# ---------- 配置 ----------parquet_path="./data/source_data.parquet"output_path=Path("./output/mult_sheets_1.xlsx")output_path.parent.mkdir(parents=True,exist_ok=True)sql_query=""" SELECT id, column_a, column_b, created_at FROM your_schema.your_table WHERE created_at>= '2026-01-01' """# ---------- 1. 读取本地Parquet(df_1) ----------df_1=pd.read_parquet(parquet_path,engine="pyarrow")# ---------- 2. 连接Redshift ----------conn=redshift_connector.connect(host=os.getenv("REDSHIFT_HOST"),database=os.getenv("REDSHIFT_DATABASE"),port=int(os.getenv("REDSHIFT_PORT",5439)),user=os.getenv("REDSHIFT_USER"),password=os.getenv("REDSHIFT_PASSWORD"))# ---------- 3. UNLOAD到S3 ----------timestamp=datetime.now().strftime("%Y%m%d_%H%M%S")s3_unload_path=f"s3://your-bucket/redshift_unload/{timestamp}/"wr.redshift.unload_to_files(sql=sql_query,path=s3_unload_path,con=conn,iam_role=os.getenv("REDSHIFT_IAM_ROLE"),unload_format="PARQUET",parallel=True,region=os.getenv("AWS_REGION","us-east-1"))# ---------- 4. 从S3读取为DataFrame(df_2) ----------session=boto3.Session(region_name=os.getenv("AWS_REGION","us-east-1"))df_2=wr.s3.read_parquet(path=s3_unload_path,boto3_session=session)# ---------- 5. 关闭Redshift连接 ----------conn.close()# ---------- 6. 按id排序 ----------df_1=df_1.sort_values(by="id",ascending=True,ignore_index=True)df_2=df_2.sort_values(by="id",ascending=True,ignore_index=True)# ---------- 7. 写入Excel ----------withpd.ExcelWriter(output_path,engine="openpyxl")aswriter:df_1.to_excel(writer,sheet_name="df_1",index=False)df_2.to_excel(writer,sheet_name="df_2",index=False)print(f"Excel文件已生成:{output_path}")7. 常见问题与调试建议
7.1 UNLOAD权限错误
执行UNLOAD时若遇到AccessDenied错误,请检查iam_role对应的IAM角色是否具备以下权限:
s3:PutObject(允许写入S3对象)s3:ListBucket(允许列出桶内对象)
7.2 区域不匹配
若S3桶与Redshift集群位于不同AWS区域,需通过region参数明确指定S3桶所在区域,否则UNLOAD将失败。
7.3 时间戳路径覆盖问题
如需在同一路径下多次运行,建议保留时间戳前缀,避免使用ALLOWOVERWRITE参数以防止意外覆盖历史数据。同时可在脚本末尾添加S3清理逻辑,定期删除过期的UNLOAD临时文件以控制存储成本。
7.4 性能优化建议
- UNLOAD并行度:
parallel=True可显著提升大数据量的导出速度,但如果后续处理对文件顺序有严格要求,可设置parallel=False并按ORDER BY子句生成单一有序文件。 - Parquet读取:若
df_2数据量极大,可使用wr.s3.read_parquet的chunked=True参数实现分块读取,降低内存压力。 - 内存映射:对于本地parquet文件的读取,可通过
pd.read_parquet(..., memory_map=True)减少内存开销。
8. 扩展建议
以上流程可作为日常数据分析或ETL任务的基础模板。如需进一步工程化,可考虑:
- 参数化配置:将S3桶名、Redshift连接信息、SQL查询等提取为配置文件或环境变量,提升代码的可移植性。
- 日志记录:引入Python内置的
logging模块,记录UNLOAD耗时、DataFrame行数等关键指标。 - 错误重试机制:对UNLOAD和S3读取操作添加重试逻辑,增强脚本在临时网络波动下的鲁棒性。
- S3临时文件清理:UNLOAD完成后可选择性地删除S3上的临时文件以控制存储成本,可通过
wr.s3.delete_objects实现。 - 多Sheet样式定制:若需要对Excel输出进行样式设置(如列宽、字体、颜色),可使用
openpyxl或xlsxwriter的高级接口对每个Sheet单独定制。
此方案覆盖了从本地数据读取、云端数据仓库导出、云存储中转、本地合并到最终Excel输出的全链路,适用于数据分析报告生成、数据质量比对、多源数据融合等业务场景。