全部產品
Search
文件中心

E-MapReduce:Insert Into

更新時間:Jan 25, 2025

Insert Into語句的使用方式和MySQL等資料庫中Insert Into語句的使用方式類似。在Doris中,所有的資料寫入都是一個獨立的匯入作業。因此,本文將Insert Into作為一種匯入方式,介紹Insert Into的使用方法和最佳實務。

背景資訊

說明

本文部分內容來源於Apache Doris,詳情請參見Introduction to Apache Doris

主要的Insert Into命令包含以下兩種:

  • NSERT INTO tbl SELECT ...

  • INSERT INTO tbl (col1, col2, ...) VALUES (1, 2, ...), (1,3, ...);

    重要

    該命令僅用於Demo,請勿使用在測試或生產環境中。

匯入操作及返回結果

Insert Into命令需要通過MySQL協議提交,建立匯入請求會同步返回匯入結果。

匯入操作

Insert Into的使用樣本如下:

INSERT INTO tbl2 WITH LABEL label1 SELECT * FROM tbl3;
INSERT INTO tbl1 VALUES ("qweasdzxcqweasdzxc"), ("a");
重要

當需要使用CTE(Common Table Expressions) 作為insert操作中的查詢部分時,必須指定WITH LABEL和column list部分或者對CTE進行封裝,樣本如下。

INSERT INTO tbl1 WITH LABEL label1
WITH cte1 AS (SELECT * FROM tbl1), cte2 AS (SELECT * FROM tbl2)
SELECT k1 FROM cte1 JOIN cte2 WHERE cte1.k1 = 1;

INSERT INTO tbl1 (k1)
WITH cte1 AS (SELECT * FROM tbl1), cte2 AS (SELECT * FROM tbl2)
SELECT k1 FROM cte1 JOIN cte2 WHERE cte1.k1 = 1;

INSERT INTO tbl1 (k1)
select * from (
WITH cte1 AS (SELECT * FROM tbl1), cte2 AS (SELECT * FROM tbl2)
SELECT k1 FROM cte1 JOIN cte2 WHERE cte1.k1 = 1) as ret

樣本中的參數說明,詳情請參見INSERT INTO命令或者執行HELP INSERT來查看。

返回結果

Insert Into本身是一個SQL命令,其返回結果會根據執行結果的不同,分為以下幾種:

  • 結果集為空白

    如果Insert對應Select語句的結果集為空白,返回樣本如下。

    mysql> insert into tbl1 select * from empty_tbl;
    Query OK, 0 rows affected (0.02 sec)

    Query OK表示執行成功,0 rows affected表示沒有資料被匯入。

  • 結果集不為空白

    在結果集不為空白的情況下,返回結果分為如下幾種情況:

    • Insert執行成功並可見。

      mysql> insert into tbl1 select * from tbl2;
      Query OK, 4 rows affected (0.38 sec)
      {'label':'insert_8510c568-9eda-4173-9e36-6adc7d35****', 'status':'visible', 'txnId':'4005'}
      
      mysql> insert into tbl1 with label my_label1 select * from tbl2;
      Query OK, 4 rows affected (0.38 sec)
      {'label':'my_label1', 'status':'visible', 'txnId':'4005'}
      
      mysql> insert into tbl1 select * from tbl2;
      Query OK, 2 rows affected, 2 warnings (0.31 sec)
      {'label':'insert_f0747f0e-7a35-46e2-affa-13a235f4****', 'status':'visible', 'txnId':'4005'}
      
      mysql> insert into tbl1 select * from tbl2;
      Query OK, 2 rows affected, 2 warnings (0.31 sec)
      {'label':'insert_f0747f0e-7a35-46e2-affa-13a235f4****', 'status':'committed', 'txnId':'4005'}

      Query OK表示執行成功,4 rows affected表示總共有4行資料被匯入,2 warnings表示被過濾的行數。

      同時會返回一個JSON串,樣本如下:

      {'label':'my_label1', 'status':'visible', 'txnId':'4005'}
      {'label':'insert_f0747f0e-7a35-46e2-affa-13a235f4****', 'status':'committed', 'txnId':'4005'}
      {'label':'my_label1', 'status':'visible', 'txnId':'4005', 'err':'some other error'}
      • label:您指定的Label或自動產生的Label。Label是該Insert Into匯入作業的標識,每個匯入作業都有一個在單database內部唯一的Label。

      • status:表示匯入資料是否可見。如果可見,顯示visible,如果不可見,顯示committed。

      • txnId:該Insert對應的匯入事務的id。

      • err:顯示一些其他非預期錯誤。

      您可以使用SHOW LOAD語句查看被過濾的行,樣本如下。返回結果中的URL可以用於查詢錯誤的資料。

      show load where label="xxx";

      資料不可見是一個臨時狀態,資料最終是一定可見的。可以通過SHOW TRANSACTION語句查看這批資料的可見狀態,樣本如下。返回結果中的TransactionStatus列如果為visible,則表述資料可見。

      show transaction where id=4005;
    • Insert執行失敗。

      執行失敗表示沒有任何資料被成功匯入,返回樣本如下。

      mysql> insert into tbl1 select * from tbl2 where k1 = "a";
      ERROR 1064 (HY000): all partitions have no load data. url: http://10.74.xx.xx:8042/api/_load_error_log?file=__shard_2/error_log_insert_stmt_ba8bb9e158e4879-ae8de8507c0b****_ba8bb9e158e4879_ae8de8507c0b****

      其中ERROR 1064 (HY000): all partitions have no load data顯示失敗原因。後面的url可以用於查詢錯誤的資料。

綜上,對於Insert操作返回結果的正確處理邏輯為:

  • 如果返回結果為ERROR 1064 (HY000),則表示匯入失敗。

  • 如果返回結果為Query OK,則表示執行成功。

    • 如果rows affected為0,表示結果集為空白,沒有資料被匯入。

    • 如果rows affected大於0:

      • 如果status為committed,表示資料不可見,需要通過SHOW TRANSACTION語句查看狀態直到visible。

      • 如果status為visible,表示資料匯入成功。

      • 如果warnings大於0,表示有資料被過濾,可以通過SHOW LOAD語句擷取URL查看被過濾的行。

SHOW LAST INSERT

上面介紹了如何根據Insert操作的返回結果進行後續處理。但一些語言的MySQL類庫中很難擷取返回結果中的JSON字串。因此,Doris還提供了SHOW LAST INSERT命令來顯式的擷取最近一次Insert操作的結果。當執行完一個Insert操作後,可以在同一Session串連中執行SHOW LAST INSERT,該命令會返回最近一次Insert操作的結果。例如:

mysql> show last insert\G
*************************** 1. row ***************************
    TransactionId: 640**
            Label: insert_ba8f33aea9544866-8ed77e2844d0****
         Database: default_cluster:db1
            Table: t1
TransactionStatus: VISIBLE
       LoadedRows: 2
     FilteredRows: 0

該命令會返回Insert以及對應事務的詳細資料。因此,您可以在每次執行完Insert操作後,繼續執行show last insert命令來擷取Insert的結果。

重要

該命令只會返回在同一Session串連中,最近一次Insert操作的結果。如果串連斷開或更換了新的串連,則將返回空集。

相關係統配置

FE配置

timeout:匯入任務的逾時時間(以秒為單位)。匯入任務在設定的timeout時間內未完成則會被系統取消,變成CANCELLED。目前Insert Into暫不支援自訂匯入的timeout時間,所有Insert Into匯入的逾時時間是統一的,預設的timeout 時間為1小時。如果匯入的源檔案無法在規定時間內完成匯入,則需要調整FE的參數insert_load_default_timeout_second。同時Insert Into語句受到Session變數query_timeout的限制,可以通過SET query_timeout = xxx; 來增加逾時時間,單位是秒。

Session變數

  • enable_insert_strict:Insert Into匯入本身不能控制匯入可容忍的錯誤率。您只能通過Session參數enable_insert_strict控制。當該參數設定為false時,表示至少有一條資料被正確匯入,則返回成功;如果有失敗資料,則還會返回一個Label。當該參數設定為true時,表示如果有一條資料錯誤,則匯入失敗。預設為false。可通過SET enable_insert_strict = true;來設定。

  • query_timeout:Insert Into本身也是一個SQL命令,因此也受到Session變數query_timeout的限制。可以通過SET query_timeout = xxx;來增加逾時時間,單位是秒。

最佳實務

應用情境

  • 僅匯入幾條測試資料,驗證Doris系統的功能,適合使用INSERT INTO VALUES文法,其與MySql文法相同。

  • 將已經在Doris表中的資料進行ETL轉換並匯入到一個新的Doris表中,適合使用INSERT INTO SELECT文法。

  • 建立一種外部表格,如MySQL外部表格映射一張MySQL系統中的表,或者建立Broker外部表格來映射HDFS上的資料檔案。然後通過INSERT INTO SELECT文法將外部表格中的資料匯入到Doris表中儲存。

資料量

Insert Into對資料量沒有限制,也支援巨量資料量匯入。但Insert Into有預設的逾時時間,如果您預估的匯入資料量過大,需要修改系統的Insert Into匯入逾時時間。

例如,當匯入資料量為36 GB時,匯入時間約小於等於3600s*10 M/s。其中10 M/s是最大匯入限速,您需要根據當前叢集情況計算出平均的匯入速度來替換公式中的10 M/s。

完整樣本

在資料庫sales中有表store_sales,又建立表bj_store_sales,希望將store_sales中銷售記錄在bj的資料匯入到表bj_store_sales中,匯入的資料量約為10 GB,當前叢集的平均匯入速度約為5 M/s。

store_sales schema:
(id, total, user_id, sale_timestamp, region)

bj_store_sales schema:
(id, total, user_id, sale_timestamp)
  1. 判斷是否要修改Insert Into的預設逾時時間。

    計算匯入的大概時間
    10 GB / 5 M/s = 2000s
    
    修改FE配置
    insert_load_default_timeout_second = 2000
  2. 建立匯入任務。

    由於希望將一張表中的資料做ETL並匯入到目標表中,所以應該使用Insert into query_stmt方式匯入。

    INSERT INTO bj_store_sales WITH LABEL `label` SELECT id, total, user_id, sale_timestamp FROM store_sales where region = "bj";