SparkETL 用Spark SQL實現ETL

SparkETL

SparkETL主要用SQL方式實現數據倉庫ETL,並保持spark的原生多功能、靈活性。採用java對spark功能進行簡單封裝,對於數據源、目標都是關係型數據庫的,從數據抽取、轉換、加載完全採用SQL方式,對於SQL不滿足的場景,再用spark相關功能實現。
SparkETL是ETL的一個參考實現,實際使用時,需要根據業務需要及模型設計在此基礎上增加、修改。

實現背景

Spark基於rdd實現了很多功能,針對批處理,就提供了Spark Core、DataFrame(Dataset)、SQL,開發語言更是支持Scala、Java、Python、R,而且各功能間基本都支持互相轉換。但是,絕大多數公司80%以上的數據都是結構化數據,SQL以其簡單、友好、高效,尤其適合做ETL處理。Spark支持關係型數據庫、文本文件(text、csv、json、xml等)、二進制文件(Parquet、ORC、SequenceFile等)、大量的非關係型數據庫也提供了Spark接口(MongoDB、Elasticsearch、Redis等),而且一旦加載到Spark中,其後的處理方式就是完全一樣的。也就是說Spark支持多源獲取數據、多源連接、可以將結果同步到不同的目標。Spark對不同的源都有不同的連接方式,需要將連接方式封裝了,然後才能用SQL處理。

實現方式

本程序用Java調用Spark,對源和目標簡單封裝,然後在SQL文件中寫SQL語句,可以實現絕大部分ETL。對於源和目標不好封裝,或者數據是非結構化的,可以用DataFrame、Core處理後,再用SQL實現其餘的ETL。
Java程序讀取SQL文件中的SQL,然後替換其中的參數,最後調用spark接口執行。
不打算做成一個複雜的程序,因此捨棄了些靈活性,採用了一定的約定,約定方式在示例中有說明。程序的調度是較粗粒度的,最細是一個SQL文件調度一次,因此本程序適用於調度相對簡單的數倉。簡單的調度可以用Linux的crontab,複雜些的可以用Airflow這樣的專業調度工具。

功能

  • RDBMS
    通過JDBC或者數據庫提供的導入導出功能進行雙向同步。
    目前測試通過的有MySQL、Microsoft SQL Server、PostgreSQL
    待測試:DB2、Oracle

TODO

  • MongoDB
  • Elasticsearch
  • Redis
  • 文本文件

示例

從rdbms獲取數據並插入hive表

  • sql文件,如RDB_d.sql:
insert overwrite table stg.s_user partition(time_type=${time_type}, time_id='${time_id}')   -- hive中執行,hive語法
select id,username,password,mobile_phone,email,status,create_time,create_user,last_modify_time,last_modify_user,is_deleted -- rdb中執行,rdb語法,與上一行必須在不同行
from individual_user;
  • Java中調用
public ExtractFromRDB(SparkSession spark, Integer timeType, String timeID, Integer backDate, String dbID, Integer frequency) throws SQLException, ClassNotFoundException {
    super(spark, timeType, timeID, backDate, dbID, frequency);
}
public void dpFull() throws Exception {
    exeSQLFile("RDB_d.sql", "insert");  // exeType: insert jdbc方式導入;load 通過rdb命令或者jdbc方式生成文件,然後load到hive表中
}

spark上的執行

  • sql文件,如s2i_d.sql
insert overwrite table inte.i_item partition (time_type=${time_type}, time_id='${time_id}')
select i.id, u.id, s.id, w.id, i.item_type,
    i.category_one_id, i.category_two_id, i.category_three_id, w.warehouse_type, u.mobile_phone, u.email
from stg.s_user u, stg.s_shop s, stg.s_item i, stg.s_warehouse w
where u.id = s.user_id and s.id = i.shop_id and s.id = w.shop_id
and u.time_type = ${time_type} and u.time_id ='${time_id}'
and s.time_type = ${time_type} and s.time_id ='${time_id}'
and i.time_type = ${time_type} and i.time_id ='${time_id}'
and w.time_type = ${time_type} and w.time_id ='${time_id}';
  • Java中調用
public Tran(SparkSession spark, Integer timeType, String timeID, Integer backDate, Integer frequency) {
    super(spark, timeType, timeID, backDate, frequency);
}
public void s2iD() throws Exception {
    exeSQLFile("s2i_d.sql", "insert"); // exeType: insert 插入hive表中; view 生成v_${表名}的臨時試圖,供後續使用
}

計算結果導出到rdbms

  • sql文件,如RDB_d.sql:
delete from m_item_type where time_type = ${time_type} and time_id = '${time_id}';  -- 刪除rdb表數據,rdb語法
@m_item_type -- rdb表名
select cast(time_type as smallint) time_type, time_id, cast(item_type as smallint) item_type, user_no, item_no, shop_no, warehouse_no  -- hive數據查詢,字段名、數據類型與rdb保持一致
from dm.m_item_type
where time_type = ${time_type} and time_id = '${time_id}';
  • Java中調用
public ExportToRDB(SparkSession spark, Integer timeType, String timeID, Integer backDate, String dbID, Integer frequency) throws SQLException, ClassNotFoundException {
    super(spark, timeType, timeID, backDate, dbID, frequency);
}
public void dpD() throws Exception {
    exeSQLFile("RDB_d.sql", "load"); // exeType: insert 通過jdbc方式插入rdb中;load 將數據導到本地,然後用數據庫load方式入庫;db: 在rdb中執行SQL
}

git地址

https://github.com/dazheng/SparkETL

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章