Insert Into語句的使用方式和MySQL等數據庫中Insert Into語句的使用方式類似。在Doris中,所有的數據寫入都是一個獨立的導入作業。因此,文本將Insert Into作為一種導入方式,介紹Insert Into的使用方法和最佳實踐。
背景信息
本文部分內容來源于Apache Doris,詳情請參見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)
判斷是否要修改Insert Into的默認超時時間。
計算導入的大概時間 10 GB / 5 M/s = 2000s 修改FE配置 insert_load_default_timeout_second = 2000
創建導入任務。
由于希望將一張表中的數據做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";