使用spark-sql處理Doris大表關聯

相由心生,命由己造發表於2024-06-12

背景

最近專案上有一個需求,需要將兩張表(A表和B表)的資料進行關聯並回寫入其中一張表(A表),兩張表都是分割槽表,但是關聯條件不包括分割槽欄位。

分析過程

方案一

最樸素的想法,直接關聯執行,全表關聯,一條SQL搞定全部邏輯。想法越簡單,執行越困難。由於資料量大,伺服器規模較小,儘管各臺伺服器記憶體和CPU配置都很高,關聯會將資料讀取到記憶體,記憶體根本放不下,而且叢集配置了workload group,可使用記憶體更小了,方案一不可行。

方案二

可以在關聯時增加分割槽欄位對任務進行拆解,這樣可以實現,但是會形成笛卡爾積,歷史資料量巨大,分割槽較多(A表和B表都是1年),缺點也很明顯。

  • 執行耗時長
  • 執行語句太多,操作不便
  • 如果按照單分割槽關聯,A表的每一個分割槽將會掃描B表全表

經過評估上述方案二不可行。

方案三

透過外部計算和儲存來實現,可選的有Hive、Spark、Flink。三種方案都是可行的,但是從操作複雜度來看使用spark-sql直接讀取Doris進行關聯並寫回Doris,除DDL外,只需要一條SQL即可搞定。

詳細過程

  1. 下載預編譯的spark和spark-doris-connector
  • spark-3.4.3-bin-hadoop3.tgz

  • spark-doris-connector-3.4_2.12-1.3.2.jar

  1. 部署
    將上述安裝檔案上傳至Hadoop叢集的其中一臺機器,放置到任意目錄,比如/opt,請保證使用的使用者可以向Yarn提交任務。
    解壓縮spark-3.4.3-bin-hadoop3.tgz得到spark-3.4.3-bin-hadoop3目錄
    將spark-doris-connector-3.4_2.12-1.3.2.jar放到spark-3.4.3-bin-hadoop3/jars/

  2. 啟動spark-sql

bin/spark-sql --master yarn --num-executors 40 --executor-memory 7G --name Spark-SQL:Doris
  1. 建立對映表
-- 用於讀取A表資料
CREATE
TEMPORARY VIEW spark_doris_a
USING doris
OPTIONS(
  "table.identifier"="mydb.table_a",
  "fenodes"="10.*.*.1:9030,10.*.*.2:9030,10.*.*.3:9030",
  "user"="root",
  "password"="$YOUR_DORIS_PASSWORD"
);
-- 用於讀取B表資料
CREATE
TEMPORARY VIEW spark_doris_b
USING doris
OPTIONS(
  "table.identifier"="mydb.table_b",
  "fenodes"="10.*.*.1:9030,10.*.*.2:9030,10.*.*.3:9030",
  "user"="root",
  "password"="$YOUR_DORIS_PASSWORD"
);
-- 用於向A表部分列寫入資料
CREATE
TEMPORARY VIEW spark_doris_a_sink
USING doris
OPTIONS(
  "table.identifier"="mydb.table_a",
  "fenodes"="10.*.*.1:9030,10.*.*.2:9030,10.*.*.3:9030",
  "user"="root",
  "password"="$YOUR_DORIS_PASSWORD",
  "sink.properties.partial_columns"="true",
  "sink.properties.column"="'column_a','column_b','column_c'"
);
  1. DML語句
set enable_unique_key_partial_update=true;
set enable_insert_strict=false;

insert into spark_doris_a_sink
(column_a,column_b,column_c)
select a.column_a,b.column_b,b.column_c from spark_doris_a a left join spark_doris_b b on a.colum_d = b.column_e;

遇到的問題

處理過程很簡單,但是實際也是遇到了很多問題

  1. DML語句中不能使用分割槽欄位進行過濾,因為Doris 2.0版本在提供的獲取執行計劃的API中對於引號的處理存在問題,如果傳遞的是"2024-06-12",則會得到數值2006,該數值無法轉換為日期,如果傳遞"20240612"也無法得到Date("20240612")。理論上,如果調整此處的寫法增加巢狀的引號配合跳脫字元也能實現功能,Java程式設計師都懂的,有興趣可以自行驗證。
  2. 資料動態變化,由於這兩張表的資料都在不斷變化,如果讀取檔案耗時較久,將可能出現tablet對應版本不存在的異常。我的做法是另建表,將資料寫入新表,然後再執行關聯。

總結

  1. spark executor 記憶體和並行度設定,這個需要不斷調整,我也是嘗試了多次,才得到這個可以執行的結果。並行度設定太高了,將會對Doris形成較大的網路和IO衝擊,一定要慎重。並行度低了,記憶體就要高一點,不然資料都已經從Doris讀取出來了,關聯的時候會記憶體溢位。
  2. 讀取Doris的速率還是很快的,而且spark在讀取doris前獲取了執行計劃,對資料進行了行和列的裁剪,不會將整表資料都讀出來。
  3. 耗時,耗時主要在資料讀取和資料寫入,資料讀取是直接訪問BE讀取tablet,資料寫入不是檔案級別的,所以寫入效率並不高。

相關文章