本文為您介紹在Hologres中如何使用COPY命令進行數據導入與導出。
使用限制
使用COPY命令的限制說明如下:
如果導入的是分區表數據,則Hologres只支持導入數據至分區表子表,不支持導入數據至分區表父表。
在v1.1.43+版本中,當使用
COPY FROM STDIN
命令時,支持表中有DEFAULT關鍵字以及serial類型字段,早期版本不支持。
命令介紹
COPY FROM
命令用于從客戶端的標準輸入導入數據至Hologres;COPY TO
命令用于導出Hologres數據。
當前COPY命令支持的數據類型與Hologres引擎支持的數據類型一致,詳情請參見數據類型匯總。
Hologres僅支持使用
COPY FROM STDIN
命令導入數據和COPY ( query ) TO STDOUT
命令導出數據。COPY命令保障數據導入與導出的原子性。FIXED COPY模式由于將表級鎖優化為行級鎖,因此不保障原子性,當業務出現臟數據時,僅會針對該條數據報錯,其余數據可能部分寫入或未寫入。
Hologres V3.0版本前,COPY在元倉(hologres.hg_query_log)中只有COPY本身一條記錄。V3.0版本起,COPY在元倉中會產生兩條記錄,包括COPY本身及COPY過程中產生的INSERT記錄。二者可以通過Transaction ID實現關聯,關聯示例如下:
SELECT query_id, query, extended_info FROM hologres.hg_query_log WHERE extended_info ->> 'source_trx' = '<transaction_id>' -- 通過COPY本身記錄的trans_id字段可以取到transaction id ORDER BY query_start ;
命令格式
Hologres支持的COPY語句格式如下:
COPY table_name [ ( column_name [, ...] ) ]
FROM STDIN
[ [ WITH ] ( option [, ...] ) ]
COPY { ( query ) }
TO STDOUT
[ [ WITH ] ( option [, ...] ) ]
where option can be one of:
FORMAT format_name
DELIMITER 'delimiter_character'
NULL 'null_string'
HEADER [ boolean ]
QUOTE 'quote_character'
ESCAPE 'escape_character'
FORCE_QUOTE { ( column_name [, ...] ) | * }
FORCE_NOT_NULL ( column_name [, ...] )
ENCODING 'encoding_name'
STREAM_MODE [ boolean]
ON_CONFLICT 'none/ignore/update'
參數說明
參數 | 描述 |
table_name | Hologres接收數據的表名稱。 |
query | 查詢語句。 |
STDIN | 指定從客戶端使用標準輸入。 |
STDOUT | 導出至指定客戶端。 |
FORMAT | 支持TEXT、CSV和BINARY格式。 默認為TEXT格式。僅導出數據、FIXED COPY模式導入數據支持BINARY格式。 |
DELIMITER | 指定的字段分隔符。 文本格式默認為制表符,CSV格式默認為半角逗號(,)。例如 |
NULL | 指定表示一個空值的字符串。
|
HEADER | 指定文件包含標題行,其中包含每一列的名稱。 說明 僅CSV格式支持該選項。 |
QUOTE | 指定一個數據值被引用時使用的引用字符,必須是一個單一的單字節字符。 說明 僅CSV格式支持該選項。默認為雙引號。 |
ESCAPE | 指定應該出現在一個匹配 說明 僅CSV格式支持該選項。默認和 |
FORCE_QUOTE | 強制對指定列中的所有值使用引號(NULL除外)。 說明 僅 |
FORCE_NOT_NULL | 不將指定列的值與空字符串匹配。空值會被讀取為零長度字符串,而非NULL。 說明 僅 |
ENCODING | 指定文件按照encoding_name編碼。默認使用當前的客戶端編碼。 |
STREAM_MODE | 指定是否使用FIXED COPY模式,默認為FALSE。詳情請參見使用COPY導入數據至Hologres中的FIXED COPY,取值如下:
|
ON_CONFLICT | 主鍵沖突時的策略,僅STREAM_MODE為TRUE時生效,默認為NONE。
|
使用COPY導入數據至Hologres
本地文件導入
Hologres支持使用COPY命令進行本地文件的導入與導出。更多關于COPY命令的原理與用法請參見PostgreSQL官網COPY。
使用示例:
使用STDIN導入數據至Hologres,命令如下。
--創建Hologres表。 CREATE TABLE copy_test ( id int, age int, name text ) ; --導入數據至Hologres表。 COPY copy_test FROM STDIN WITH DELIMITER AS ',' NULL AS ''; 53444,24,wangming 55444,38,ligang 55444,38,luyong \. --查詢表中的數據。 SELECT * FROM copy_test;
說明PSQL客戶端僅支持使用STDIN(標準輸入)方式導入數據,HoloWeb暫不支持使用命令行方式導入數據。
使用STDIN方式導入CSV格式的文件至Hologres,命令如下。
--創建Hologres表。 CREATE TABLE partsupp ( ps_partkey integer NOT NULL, ps_suppkey integer NOT NULL, ps_availqty integer NOT NULL, ps_supplycost float NOT NULL, ps_comment text NOT NULL ); --導入CSV格式的文件至Hologres表。 COPY partsupp FROM STDIN WITH DELIMITER '|' CSV; 1|2|3325|771.64|final theodolites 1|25002|8076|993.49|ven ideas \. --查詢表中的數據。 SELECT * FROM partsupp;
說明PSQL客戶端支持使用STDIN導入數據,HoloWeb暫不支持使用命令行方式通過STDIN導入CSV格式的文件。
導入本地文件至Hologres,命令如下。
psql -U <username> -p <port> -h <endpoint> -d <databasename> -c "COPY <table> FROM STDIN WITH DELIMITER '|' CSV;" <<filename>;
說明由于PSQL客戶端支持使用STDIN(標準輸入)方式導入數據,因此需要將文件數據轉換為標準輸入格式。HoloWeb暫不支持使用命令行方式通過STDIN導入本地文件。
參數說明:
參數
描述
username
阿里云賬號:當前阿里云賬號的AccessKey ID。您可以單擊AccessKey 管理,獲取AccessKey ID。
建議使用環境變量的方式調用用戶名和密碼,降低密碼泄露風險。
自定義賬號:自定義賬號的用戶名,例如BASIC$abc。
port
Hologres實例的公共網絡端口。
示例取值
80
。endpoint
Hologres實例的公共網絡地址。
示例取值
xxx-cn-hangzhou.hologres.aliyuncs.com
。databasename
Hologres的數據庫名稱。
詳情請參見創建數據庫。
示例取值
mydb
。table
Hologres數據庫中待導入數據的表名。
filename
需要導入的本地文件路徑。
示例取值
D:\tmp\copy_test.csv
。如下示例將指導您在PSQL客戶端執行命令導入本地文件至Hologres。
輸入命令導入本地文件copy_test至Hologres。其中,插入的標準文件內容如下:
01,01,name1 02,01,name2 03,01,name3 04,01,name4
執行完成后,回到psql客戶端可以查詢新插入的數據,如下圖所示。
CopyManager導入JDBC客戶端文件
針對JDBC客戶端文件,可以使用CopyManager工具進行數據復制。
CopyManager是JDBC針對PostgreSQL的COPY場景封裝的API。Hologres兼容PostgreSQL協議,可以直接使用CopyManager進行JDBC客戶端文件的導入與導出。更多關于CopyManager的信息請參見JDBC官網文檔CopyManager。
使用示例:使用CopyManager導入JDBC客戶端的文件至Hologres,代碼樣例如下。
package com.aliyun.hologram.test.jdbc;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.sql.*;
import java.util.Properties;
import org.postgresql.copy.CopyManager;
import org.postgresql.core.BaseConnection;
public class jdbcCopyFile {
public static void main(String args[]) throws Exception {
System.out.println(copyFromFile(getConnection(), "/Users/feng/Workspace/region.tbl", "region"));
}
public static Connection getConnection() throws Exception {
Class.forName("org.postgresql.Driver");
String url = "jdbc:postgresql://endpoint:port/dbname";
Properties props = new Properties();
//set db user
props.setProperty("user", "AAA");//當前賬號的AccessKey ID,建議通過環境變量調用,降低密碼泄露風險。
//set db password
props.setProperty("password", "BBB");//當前賬號的AccessKey SECRET,建議通過環境變量調用,降低密碼泄露風險。
return DriverManager.getConnection(url, props);
}
/**
* 導入文件至數據庫。
*
* @param connection
* @param filePath
* @param tableName
* @return
* @throws SQLException
* @throws IOException
*/
public static long copyFromFile(Connection connection, String filePath, String tableName)
throws SQLException, IOException {
long count = 0;
FileInputStream fileInputStream = null;
try {
CopyManager copyManager = new CopyManager((BaseConnection) connection);
fileInputStream = new FileInputStream(filePath);
count = copyManager.copyIn("COPY " + tableName + " FROM STDIN delimiter '|' csv", fileInputStream);
} finally {
if (fileInputStream != null) {
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return count;
}
}
FIXED COPY
FIXED COPY是使用Fixed Plan優化COPY命令執行的新模式,是Hologres特有的執行引擎優化方式,僅支持導入數據至Hologres。更多關于Fixed Plan的原理請參見Fixed Plan加速SQL執行。
從Hologres V1.3.17版本起,支持FIXED COPY模式導入數據至Hologres。與未經Fixed Plan優化的COPY命令相比,有如下不同:
類別 | FIXED COPY | COPY |
鎖力度 | 行級鎖 | 表級鎖 |
數據可見行 | 寫入即可見 | COPY結束后可見 |
性能 | 高 | 非常高 |
格式支持 | TEXT、BINARY | TEXT |
主鍵沖突策略 | NONE(沖突則報錯)、UPDATE、IGNORE | NONE(沖突則報錯) |
針對非全列COPY的場景,FIXED COPY的表現如下:
如果COPY寫入的列不是全列,則為局部更新,表現如下:
CREATE TABLE t0 (id int NOT NULL, name text, age int, primary key(id)); COPY t0(id, name) FROM STDIN WITH ( STREAM_MODE TRUE, ON_CONFLICT UPDATE); -- 上述COPY等價與如下INSERT INTO INSERT INTO t0(id, name) VALUES(?,?) ON CONFLICT(id) DO UPDATE SET id = excluded.id, name = excluded.name;
如果COPY寫入的列不是全列,且未參與寫入的列包含default value,表現如下:
CREATE TABLE t0 (id int not null, name text, age int DEFAULT 0, primary key(id)); COPY t0(id, name) FROM STDIN WITH ( STREAM_MODE TRUE, ON_CONFLICT UPDATE); -- 上述COPY等價與如下INSERT INTO -- 若id數據不存在,age列賦值default value; -- 若id數據已存在,age列不更新 INSERT INTO t0(id, name, age) VALUES(?, ?, default) ON CONFLICT(id) DO UPDATE SET id = excluded.id, name = excluded.name;
使用COPY從Hologres中導出數據
導出至本地文件
使用示例:
使用
\copy
導出Hologres的數據至本地文件。說明僅支持PSQL客戶端使用該方式導出數據。
-- 建表 CREATE TABLE copy_to_local ( id int, age int, name text ) ; -- 寫入數據 INSERT INTO copy_to_local VALUES (1,1,'a'), (1,2,'b'), (1,3,'c'), (1,4,'d'); -- 查數據 SELECT * FROM copy_to_local; -- 導出數據至本地文件 \COPY (SELECT * FROM copy_to_local) TO '/root/localfile.txt';
使用STDOUT導出Hologres數據至本地文件。
說明僅支持PSQL客戶端使用該方式導出數據。
psql -U <username> -p <port> -h <endpoint> -d <databasename> -c "COPY (SELECT * FROM <tablename>) TO STDOUT WITH DELIMITER '|' CSV;" ><filename>;
導出至OSS
阿里云對象存儲(Object Storage Service,簡稱OSS)是阿里云提供的安全、低成本及高可靠的云存儲服務。Hologres支持通過以COPY命令語句的方式將查詢的數據導出到指定的OSS。
使用限制
僅當前Hologres實例的Superuser或擁有pg_execute_server_program權限的用戶,才可以使用
hg_dump_to_oss
導出Hologres的數據至OSS。Superuser可以授予其他用戶pg_execute_server_program權限,命令如下。--DB開啟簡單權限模型,執行以下語句 CALL spm_grant('pg_execute_server_program','云賬號ID/云郵箱/RAM賬號'); --DB使用的是專家權限模型,執行以下語句 GRANT pg_execute_server_program TO 云賬號ID/云郵箱/RAM賬號;
單次導入至OSS的數據量不能超過5GB。
命令介紹
COPY TO
命令:COPY ( query ) TO { PROGRAM 'command' | STDOUT } [ [ WITH ] ( option [, ...] ) ]
其中,PROGRAM指一個需要執行的命令,輸出會寫入到該命令的標準輸入。其余參數說明請參見上文中的參數說明。
hg_dump_to_oss
命令(需要與COPY TO
命令組合使用):COPY (query) TO PROGRAM 'hg_dump_to_oss --AccessKeyId <accessid> --AccessKeySecret <accesskey> --Endpoint <ossendpoint> --BucketName <bucketname> --DirName <dirname> --FileName <filename> --BatchSize <xxx> ' (DELIMITER ',', HEADER true, FORMAT CSV);
重要<dirname>前請不要添加斜杠(/)、反斜杠(\)等字符。
參數說明:
參數
描述
示例
query
輸入的查詢語句
select * from dual;
AccessKeyId
當前賬號的AccessKey ID。
您可以進入AccessKey管理頁面獲取AccessKey ID。
建議您使用環境變量的方式調用用戶名和密碼,降低密碼泄露風險。
無
AccessKeySecret
AccessKey ID對應的AccessKey Secret。
建議您使用環境變量的方式調用用戶名和密碼,降低密碼泄露風險。
無
Endpoint
OSS的經典網絡訪問域名。獲取方式如下:
您可以單擊Bucket列表頁面的目標Bucket名稱,進入Bucket詳情頁查看。
您可以根據實例所屬地域從OSS地域和訪問域名中獲取。
oss-cn-beijing-internal.aliyuncs.com
BucketName
OSS對應的bucket名字。
dummy_bucket
DirName
OSS存放輸出結果的目錄。
testdemo/
FileName
(可選)OSS對應的文件名稱。
說明不支持文件名中包含
;#`|?~<()"$\{}[]&*\n\r
。file_name
BatchSize
每次執行
hg_dump_to_oss
的行數,默認為1000。5000
DELIMITER
結果列之間的分隔符,默認為制表符(Tab-separated Values,簡稱TSV)。
,
使用示例
在Hologres中
hg_dump_to_oss
命令與COPY TO
命令的使用示例如下。-- 將Hologres內部表數據dump到指定OSS COPY (SELECT * FROM holo_test LIMIT 2) TO PROGRAM 'hg_dump_to_oss --AccessKeyId <access id> --AccessKeySecret <access key> --Endpoint oss-cn-hangzhou-internal.aliyuncs.com --BucketName hologres-demo --DirName holotest/ --FileName file_name --BatchSize 3000' DELIMITER ','; -- 將Hologres外部表數據dump到指定OSS COPY (SELECT * FROM foreign_holo_test LIMIT 20) TO PROGRAM 'hg_dump_to_oss --AccessKeyId <access id> --AccessKeySecret <access key> --Endpoint oss-cn-hangzhou-internal.aliyuncs.com --BucketName hologres-demo --DirName holotest/ --FileName file_name --BatchSize 3000' (DELIMITER ',', HEADER true); -- 跨region dump到指定OSS COPY (SELECT * FROM holo_test_1 LIMIT 20) TO PROGRAM 'hg_dump_to_oss --AccessKeyId <access id> --AccessKeySecret <access key> --Endpoint oss-cn-beijing-internal.aliyuncs.com --BucketName hologres-demo --DirName holotest/ --FileName file_name --BatchSize 3000' (DELIMITER ',', HEADER true, FORMAT CSV);
說明Hologres支持跨地域導出數據至指定的OSS。例如,可以導出杭州地域的實例數據至北京地域的OSS。
常見問題
常見的報錯內容及解決方法如下:
報錯信息
解決方法
ERROR: syntax error at or near ")"LINE 1: COPY (select 1,2,3 from ) TO PROGRAM 'hg_dump_to_oss2 --Acce...
輸入的query有誤,請檢查對應的查詢語句。
DETAIL: child process exited with exit code 255
選擇的OSS網絡類型有誤。如果您使用的是公共云,請選擇經典網絡。
DETAIL: command not found
您需要配置DUMP TO OSS的program為hg_dump_to_oss,否則會出現該報錯。
ERROR: program "hg_dump_to_oss ..." failed DETAIL: child process exited with exit code 101
輸入的AccessKeyId不合法,請使用當前賬號的AccessKey ID。
ERROR: program "hg_dump_to_oss ..." failed DETAIL: child process exited with exit code 102
輸入的AccessKeySecret不合法,請使用當前賬號的AccessKey Secret。
ERROR: program "hg_dump_to_oss ..." failed DETAIL: child process exited with exit code 103
輸入的Endpoint不合法,請確認對應OSS經典網絡的Endpoint。
ERROR: program "hg_dump_to_oss ..." failed DETAIL: child process exited with exit code 104
輸入的BucketName不合法,請確認對應的Bucket名稱。
ERROR: program "hg_dump_to_oss ..." failed DETAIL: child process exited with exit code 105
缺少參數,請對照參數說明,檢查必選參數是否均已配置。
ERROR: program "hg_dump_to_oss ..." failed DETAIL: child process exited with exit code 255
一般情況下是由于holo server與指定的OSS網絡不通導致該報錯,可以更換OSS域名(例如:OSS網絡類型選擇經典網絡)。更多關于OSS的域名信息,請參見OSS地域和訪問域名。
CopyManager導出至JDBC客戶端文件
使用示例:使用CopyManager導出Hologres的數據至JDBC客戶端的文件,代碼樣例如下。
import org.postgresql.copy.CopyManager;
import org.postgresql.core.BaseConnection;
import java.io.FileOutputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
public class copy_to_local_file {
public static void main(String args[]) throws Exception {
System.out.println(copyToFile(getConnection(), "/Users/feng/Workspace/region.tbl", "select * from region"));
}
public static Connection getConnection() throws Exception {
Class.forName("org.postgresql.Driver");
String url = "jdbc:postgresql://endpoint:port/dbname";
Properties props = new Properties();
//set db user
props.setProperty("user", "AAA");//當前賬號的AccessKey ID,建議通過環境變量調用,降低密碼泄露風險。
//set db password
props.setProperty("password", "BBB");//當前賬號的AccessKey SECRET,建議通過環境變量調用,降低密碼泄露風險。
return DriverManager.getConnection(url, props);
}
/**
* 導出數據至JDBC客戶端文件。
*
* @param connection
* @param filePath
* @param SQL_Query
* @return
* @throws SQLException
* @throws IOException
*/
public static String copyToFile(Connection connection, String filePath, String SQL_Query)
throws SQLException, IOException {
FileOutputStream fileOutputStream = null;
try {
CopyManager copyManager = new CopyManager((BaseConnection)connection);
fileOutputStream = new FileOutputStream(filePath);
copyManager.copyOut("COPY " + "(" + SQL_Query + ")" + " TO STDOUT DELIMITER '|' csv ", fileOutputStream);
} finally {
if (fileOutputStream != null) {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return filePath;
}
}