Columnar table data import

更新时间:
复制 MD 格式

Use Direct Load to efficiently import CSV and Parquet data into PolarDB for MySQL columnar tables from OSS or local files.

Overview

Direct Load is a high-performance bulk loading mechanism for X-Engine columnar tables that imports data from OSS (Object Storage Service) or local files in CSV (default) or Parquet format. X-Engine columnar tables also support MySQL native import and DTS import (with the target engine set to X-Engine).

Session parameters

Direct Load uses the following session-level parameters:

Parameter

Description

Valid values

Default

xengine_dload_parallel

Storage-layer parallelism. 0 disables Direct Load. 1 to 1024 sets the parallelism level.

0 to 1024

1

xengine_dload_check_unique_mod

The uniqueness check mode.

  • OFF: Disables uniqueness checking. Primary key duplicates still cause errors.

  • KEEPONE: Randomly keeps one record when duplicates are found.

  • ERROR: Returns an error immediately when a duplicate is found.

OFF / KEEPONE / ERROR

KEEPONE

xengine_dload_async_build_nci

Whether to build non-clustered indexes (NCI) asynchronously. FORMAT PARQUET always uses the asynchronous path internally.

ON / OFF

OFF

xengine_dload_skip_redo_log

Whether to skip redo log replication on CFile commit. The CFile is persisted to primary storage but not replicated to the Standby cluster.

Note

This parameter applies only to Standard Edition clusters without a Standby cluster.

ON / OFF

OFF

Syntax

Direct Load extends the LOAD DATA syntax for OSS and local file imports.

LOAD DATA [OSS|LOCAL]
    INFILE 'file_name'
    [REPLACE | IGNORE]
    INTO TABLE tbl_name
    [PARTITION (partition_name [, ...])]
    [CHARACTER SET charset_name]
    [{FIELDS | COLUMNS}
        [TERMINATED BY 'string']
        [[OPTIONALLY] ENCLOSED BY 'char']
        [ESCAPED BY 'char']
    ]
    [LINES
        [STARTING BY 'string']
        [TERMINATED BY 'string']
    ]
    [IGNORE number {LINES | ROWS}]
    [(col_name_or_user_var [, ...])]
    [SET col_name={expr | DEFAULT} [, ...]]

Restrictions

Direct Load has the following restrictions:

  • Table requirements: Direct Load supports only tables with ENGINE=XENGINE and TABLE_FORMAT=COLUMN. The target table must be empty.

  • Index support:

    • Tables with ORDER KEY are supported.

    • Secondary indexes, foreign keys, and hidden keys are not supported.

      Note

      The hidden key restriction applies when the parameter xengine_enable_ctable_hidden_key=ON is set.

  • Binary logging: Direct Load automatically disables binary logging for the import session by temporarily clearing the OPTION_BIN_LOG flag. This does not affect the global configuration.

  • Triggers: Tables with triggers are not supported.

  • Concurrency: Only one Direct Load statement can run per table at a time. The statement holds an exclusive MDL lock, which blocks all DML and DDL operations on the table.

  • Wildcards:

    • OSS mode supports wildcards (*). For example, /home/t4/data/*.csv.

    • LOCAL mode and non-Direct Load mode do not support wildcards.

Examples

Import data from OSS

Step 1: Create an OSS Server

Create an OSS Server with the connection details.

Note

Creating an OSS Server requires the SERVERS_ADMIN privilege. Run SHOW GRANTS FOR username; to verify the current user has the SERVERS_ADMIN privilege.

A privileged account can grant this privilege. Without it, the following error is returned: Access denied; you need (at least one of) the SERVERS_ADMIN OR SUPER privilege(s) for this operation.

DROP SERVER IF EXISTS my_server;
CREATE SERVER my_server FOREIGN DATA WRAPPER oss OPTIONS (
  EXTRA_SERVER_INFO '{"oss_endpoint":"oss-cn-xxxx-internal.aliyuncs.com","oss_bucket":"xxxx","oss_access_key_id":"xxxx","oss_access_key_secret":"xxxx"}'
);
SELECT * FROM mysql.servers;

Step 2: Create the target table

Create an X-Engine columnar table. This example uses the TPC-H lineitem table.

CREATE TABLE lineitem (
    l_orderkey BIGINT NOT NULL,
    l_partkey BIGINT NOT NULL,
    l_suppkey BIGINT NOT NULL,
    l_linenumber INT NOT NULL,
    l_quantity DECIMAL(15,2) NOT NULL,
    l_extendedprice DECIMAL(15,2) NOT NULL,
    l_discount DECIMAL(15,2) NOT NULL,
    l_tax DECIMAL(15,2) NOT NULL,
    l_returnflag CHAR(1) NOT NULL,
    l_linestatus CHAR(1) NOT NULL,
    l_shipdate DATE NOT NULL,
    l_commitdate DATE NOT NULL,
    l_receiptdate DATE NOT NULL,
    l_shipinstruct CHAR(25) NOT NULL,
    l_shipmode CHAR(10) NOT NULL,
    l_comment VARCHAR(44) NOT NULL,
    PRIMARY KEY (l_orderkey, l_linenumber, l_shipdate),
    ORDER KEY(l_orderkey)
) ENGINE=XENGINE TABLE_FORMAT=COLUMN;

Step 3: Import data

Set the parallelism level and run LOAD DATA OSS to import data. Wildcards can match multiple files.

-- Set the parallelism level (optional)
SET xengine_dload_parallel = 2;

-- Use wildcards to import multiple files (OSS mode only)
LOAD DATA OSS INFILE 'my_server/lineitem/lineitem_test*.csv'
INTO TABLE lineitem
FIELDS TERMINATED BY '|';

-- Verify the import results
SELECT * FROM lineitem ORDER BY l_orderkey, l_partkey DESC LIMIT 10;
SELECT COUNT(1) FROM lineitem;

Import data from a local file

Note

Direct Load supports only one local file per statement. To import multiple files, use Import data from OSS.

Use LOAD DATA LOCAL to import data from a local file.

CREATE TABLE lineitem (
    l_orderkey       BIGINT NOT NULL,
    l_partkey        BIGINT NOT NULL,
    l_suppkey        BIGINT NOT NULL,
    l_linenumber     BIGINT NOT NULL,
    l_quantity       DECIMAL(15,2) NOT NULL,
    l_extendedprice  DECIMAL(15,2) NOT NULL,
    l_discount       DECIMAL(15,2) NOT NULL,
    l_tax            DECIMAL(15,2) NOT NULL,
    l_returnflag     CHAR(1) NOT NULL,
    l_linestatus     CHAR(1) NOT NULL,
    l_shipdate       DATE NOT NULL,
    l_commitdate     DATE NOT NULL,
    l_receiptdate    DATE NOT NULL,
    l_shipinstruct   CHAR(25) NOT NULL,
    l_shipmode       CHAR(10) NOT NULL,
    l_comment        VARCHAR(44) NOT NULL,
    PRIMARY KEY (l_orderkey, l_linenumber, l_shipdate),
    ORDER KEY(l_orderkey)
) ENGINE=XENGINE TABLE_FORMAT=COLUMN;

SET xengine_dload_parallel = 1;

-- Import from a local file (wildcards are not supported)
LOAD DATA LOCAL INFILE '/home/data/lineitem/lineitem_test.csv'
INTO TABLE lineitem
FIELDS TERMINATED BY '|';

-- Verify the import results
SELECT * FROM lineitem ORDER BY l_orderkey, l_partkey DESC LIMIT 10;
SELECT COUNT(1) FROM lineitem;

FORMAT PARQUET import

Direct Load imports Parquet files directly into X-Engine columnar tables, skipping CSV text parsing through an Arrow-to-ORC columnar fast path for better performance.

Parquet syntax

LOAD DATA [LOCAL | OSS] INFILE 'file_name_or_glob'
    [REPLACE | IGNORE]
    FORMAT PARQUET
    INTO TABLE tbl_name
    [PARALLEL n]
    [(col_name [, col_name] ...)]
    [OPTIONS (
        batch_size     = <unsigned int>,
        validate_schema = TRUE | FALSE,
        parallel       = <unsigned int>
    )];

Syntax details

  • Auto-detection: If the file name ends with .parquet (case-insensitive), the system automatically enables Parquet import mode, even if you omit the FORMAT PARQUET keyword.

  • Parallel loading (PARALLEL n): Specifies the thread count for parallel RowGroup reads. The actual parallelism is the smaller of n and the total number of RowGroups across all files: actual parallelism = min(n, RowGroups x files). If omitted, parallelism defaults to the session variable xengine_dload_parallel.

    • The parallel key in OPTIONS is equivalent to the outer PARALLEL n. When both are set, OPTIONS(parallel=N) takes precedence.

  • Selective column import ((col_name, ...)): Lists specific columns to import from the Parquet file. Only listed columns are loaded; unlisted columns receive their default values.

Parquet restrictions

  • Data type compatibility: Data types in the Parquet file must be compatible with the target table column types. For example, Parquet STRING maps to VARCHAR or TEXT, but BINARY maps only to BLOB or VARBINARY. See Type mapping for details.

  • Schema matching:

    • Positional mode (no column list): The number of columns in the Parquet file must exactly match the number of columns in the target table, and types must be compatible by position.

    • Named mode (column list provided): Each column name in the list must exist in both the Parquet file and the target table, and the types must be compatible.

  • Asynchronous index build: Parquet import always uses asynchronous NCI (non-clustered index) build internally. LOAD DATA returns immediately after data is written, and the index builds in the background. During this process, the table supports only columnar queries. You do not need to set xengine_dload_async_build_nci manually.

  • Unsupported type: FIXED_SIZE_BINARY Arrow columns are not supported in the current version.

Type mapping

Parquet/Arrow types map to MySQL column types as follows.

Parquet / Arrow type

Compatible MySQL types

Notes

BOOLEAN

TINYINT(1), SMALLINT, INT, BIGINT

Auto-widened

INT8, INT16, INT32, INT64

Matching or wider integer types

Smaller integers auto-widen to BIGINT

UINT8, UINT16, UINT32, UINT64

SMALLINT, INT, BIGINT

Safe widening on signed types

FLOAT

FLOAT, DOUBLE

FLOAT auto-widens to DOUBLE

DOUBLE

DOUBLE

-

STRING (UTF-8)

VARCHAR(n), CHAR(n), TEXT

Non-binary character sets only

LARGE_STRING

TEXT

For extra-long strings

BINARY

BLOB, VARBINARY

Cannot map to VARCHAR or other non-binary character sets

LARGE_BINARY

BLOB

-

DATE32

DATE, VARCHAR(>=10)

When mapped to VARCHAR, the format is YYYY-MM-DD

TIMESTAMP[us, UTC]

DATETIME(6), TIMESTAMP(6)

Nullable types are supported

DECIMAL(p,s) (p <= 18)

DECIMAL(p,s)

Uses the Decimal64 path internally

DECIMAL(p,s) (19 <= p <= 38)

DECIMAL(p,s)

Uses the Decimal128 path internally

FIXED_SIZE_BINARY

--

Not supported

Parquet import examples

Import from a local file (hidden primary key)

SET xengine_enable_ctable_hidden_key = ON;
SET xengine_dload_parallel = 4;

CREATE TABLE t_basic (
    id     INT,
    bigval BIGINT,
    amount DOUBLE,
    name   VARCHAR(100)
) ENGINE=XENGINE TABLE_FORMAT=COLUMN;

-- Explicit FORMAT PARQUET
LOAD DATA LOCAL INFILE '/data/test_basic.parquet'
  FORMAT PARQUET INTO TABLE t_basic;

-- Auto-detect by .parquet extension
LOAD DATA LOCAL INFILE '/data/test_basic.parquet' INTO TABLE t_basic;

-- Wait for the asynchronous NCI build to complete before using row plans or DML
SELECT schema_name, table_name, task_status
  FROM information_schema.XENGINE_CTABLE_NCI_BUILD;

Import with named columns

Specify columns to import. Each name must exist in both the Parquet file and the target table.

-- Import only the id and name columns. Other columns are filled with defaults or NULL.
LOAD DATA LOCAL INFILE '/data/test_basic.parquet'
  FORMAT PARQUET INTO TABLE t_prune (id, name);

Parallel loading (PARALLEL clause)

-- PARALLEL clause: 4 threads split by RowGroup
LOAD DATA LOCAL INFILE '/data/big.parquet'
  FORMAT PARQUET INTO TABLE t_par PARALLEL 4;

-- Wildcards + PARALLEL: multi-file parallel import
LOAD DATA LOCAL INFILE '/data/multi_*.parquet'
  FORMAT PARQUET INTO TABLE t_multi PARALLEL 8;

OPTIONS clause

The OPTIONS clause controls batch size and schema validation for Parquet imports.

LOAD DATA LOCAL INFILE '/data/test_basic.parquet'
  FORMAT PARQUET INTO TABLE t_opts
  OPTIONS(batch_size=65536, validate_schema=TRUE);

Import with a user-defined primary key

When the target table has a user-defined primary key, the Parquet file must include the primary key columns.

-- Single-column primary key
CREATE TABLE t_upk1 (
    id     INT PRIMARY KEY,
    bigval BIGINT,
    amount DOUBLE,
    name   VARCHAR(100)
) ENGINE=XENGINE TABLE_FORMAT=COLUMN;

LOAD DATA LOCAL INFILE '/data/test_basic.parquet'
  FORMAT PARQUET INTO TABLE t_upk1;

-- Composite primary key
CREATE TABLE t_upk_comp (
    id     INT,
    bigval BIGINT,
    amount DOUBLE,
    name   VARCHAR(100),
    PRIMARY KEY (id, bigval)
) ENGINE=XENGINE TABLE_FORMAT=COLUMN;

LOAD DATA LOCAL INFILE '/data/test_basic.parquet'
  FORMAT PARQUET INTO TABLE t_upk_comp;

Import Parquet from OSS

Import Parquet files from OSS. Wildcards can match multiple files.

-- Create the OSS Server (same as the CSV example)
DROP SERVER IF EXISTS my_server;
CREATE SERVER my_server FOREIGN DATA WRAPPER oss OPTIONS (
  EXTRA_SERVER_INFO '{"oss_endpoint": "oss-cn-xxxx.aliyuncs.com",
                      "oss_bucket": "xxxx",
                      "oss_access_key_id": "xxxx",
                      "oss_access_key_secret": "xxxx"}'
);

SET xengine_dload_parallel = 16;

LOAD DATA OSS INFILE 'my_server/path/to/data_*.parquet'
  FORMAT PARQUET INTO TABLE t_basic PARALLEL 16;

Asynchronous NCI build mode

When xengine_dload_async_build_nci=ON, the import returns immediately after CTable files are written. The NCI builds asynchronously in the background.

Usage

SET xengine_dload_async_build_nci = ON;
SET xengine_dload_parallel = 64;

LOAD DATA OSS INFILE 'my_server/tpch1000g/lineitem.*'
INTO TABLE tpch1000.lineitem
FIELDS TERMINATED BY '|';

Restrictions during NCI build

  • DML and DDL operations on the table are blocked during the NCI build.

  • Only columnar execution plans are supported. Row-store plans fail with ER_XENGINE_CTABLE_NCI_NOT_READY_FOR_ROWPLAN.

Query examples during asynchronous NCI build

SET use_imci_engine = ON;
SELECT * FROM lineitem ORDER BY l_orderkey, l_partkey DESC LIMIT 10;  -- OK
SELECT COUNT(1) FROM lineitem;  -- OK

SET use_imci_engine = OFF;
SELECT * FROM lineitem ORDER BY l_orderkey, l_partkey DESC LIMIT 10;  -- Error
UPDATE lineitem SET l_partkey=1 WHERE l_orderkey=149999904;  -- Error

Monitor NCI build progress

Query information_schema.XENGINE_CTABLE_NCI_BUILD to monitor progress.

SELECT schema_name, table_name, task_status FROM information_schema.XENGINE_CTABLE_NCI_BUILD ORDER BY subtable_id;

Cancel and rebuild NCI

Cancel an ongoing NCI build with the dbms_ctable package, then rebuild if needed.

-- Cancel the NCI build
CALL dbms_ctable.cancel_nci_build(<subtable_id>);
-- Rebuild the NCI after cancellation
CALL dbms_ctable.rebuild_nci(<subtable_id>);

TPC-H 100 GB import example (CSV)

This example imports TPC-H 100 GB data from OSS using Direct Load.

Create the target tables

This example uses the lineitem table.

SET xengine_enable_ctable_hidden_key = OFF;

DROP DATABASE IF EXISTS tpch100;
CREATE DATABASE tpch100;
USE tpch100;

-- Example: Create the lineitem table
CREATE TABLE lineitem (
    L_ORDERKEY    INTEGER NOT NULL,
    L_PARTKEY     INTEGER NOT NULL,
    L_SUPPKEY     INTEGER NOT NULL,
    L_LINENUMBER  INTEGER NOT NULL,
    L_QUANTITY    DECIMAL(15,2) NOT NULL,
    L_EXTENDEDPRICE DECIMAL(15,2) NOT NULL,
    L_DISCOUNT    DECIMAL(15,2) NOT NULL,
    L_TAX         DECIMAL(15,2) NOT NULL,
    L_RETURNFLAG  CHAR(1) NOT NULL,
    L_LINESTATUS  CHAR(1) NOT NULL,
    L_SHIPDATE    DATE NOT NULL,
    L_COMMITDATE  DATE NOT NULL,
    L_RECEIPTDATE DATE NOT NULL,
    L_SHIPINSTRUCT CHAR(25) NOT NULL,
    L_SHIPMODE     CHAR(10) NOT NULL,
    L_COMMENT      VARCHAR(44) NOT NULL,
    PRIMARY KEY (L_ORDERKEY, L_LINENUMBER, L_SHIPDATE)
) ENGINE=XENGINE TABLE_FORMAT=COLUMN;

Create the OSS Server

DROP SERVER IF EXISTS my_server;

CREATE SERVER my_server
FOREIGN DATA WRAPPER oss OPTIONS
(
  EXTRA_SERVER_INFO '{"oss_endpoint": "oss-cn-xxxx.aliyuncs.com","oss_bucket": "xxxx","oss_access_key_id": "xxxx","oss_access_key_secret": "xxxx"}'
);

Batch import script

The following bash script imports all TPC-H tables in parallel.

#!/usr/bin/env bash
set -u

SOCK=/tmp/mysql29.sock
OUT=output.txt
PREFIX="my_server/imci/tpch100g"
DB=tpch100
PARA=64

: > "$OUT"

tables=(lineitem nation region part supplier partsupp customer orders)

run() {
  local t="$1"
  (
    echo "===== [$t] START $(date '+%F %T') ====="
    sql="SET xengine_dload_parallel=${PARA};
         LOAD DATA OSS INFILE '${PREFIX}/${t}.*'
         INTO TABLE ${DB}.${t}
         FIELDS TERMINATED BY '|';"
    { time mysql -uroot -S "$SOCK" -e "$sql"; } 2>&1
    rc=${PIPESTATUS[0]:-0}
    echo "===== [$t] END   $(date '+%F %T') rc=$rc ====="
    echo
    exit "$rc"
  ) >>"$OUT" 2>&1 &
  echo "[$t] pid=$!"
}

for t in "${tables[@]}"; do
  run "$t"
done

wait
echo "ALL DONE $(date '+%F %T')" >>"$OUT"

TPC-H Parquet import example

This example follows the same structure as TPC-H 100 GB import example (CSV). Table DDL and OSS Server setup are identical — replace tpch100 with tpch_parquet for a separate database. The differences: FORMAT PARQUET replaces CSV, FIELDS TERMINATED BY is not needed, and asynchronous NCI runs automatically without setting xengine_dload_async_build_nci.

#!/usr/bin/env bash
set -u

SOCK=/tmp/mysql29.sock
OUT=output.txt
# TODO: Replace with the actual OSS path, for example: my_server/imci/tpch1000g_parquet
PREFIX="my_server/<TBD>/tpch_parquet"
DB=tpch_parquet
PARA=64

: > "$OUT"

tables=(lineitem nation region part supplier partsupp customer orders)

run() {
  local t="$1"
  (
    echo "===== [$t] START $(date '+%F %T') ====="
    sql="SET xengine_dload_parallel=${PARA};
         LOAD DATA OSS INFILE '${PREFIX}/${t}/*.parquet'
         FORMAT PARQUET INTO TABLE ${DB}.${t} PARALLEL ${PARA};"
    { time mysql -uroot -S "$SOCK" -e "$sql"; } 2>&1
    rc=${PIPESTATUS[0]:-0}
    echo "===== [$t] END   $(date '+%F %T') rc=$rc ====="
    echo
    exit "$rc"
  ) >>"$OUT" 2>&1 &
  echo "[$t] pid=$!"
}

for t in "${tables[@]}"; do
  run "$t"
done

wait
echo "ALL DONE $(date '+%F %T')" >>"$OUT"

# Wait for the background asynchronous NCI build to complete before using row plans or DML
mysql -uroot -S "$SOCK" -N -e "
  SELECT TABLE_NAME, TASK_STATUS, RUN_SEC
    FROM information_schema.XENGINE_CTABLE_NCI_BUILD
   ORDER BY CREATE_TIME;"

Error handling

Common Direct Load errors and solutions.

Error code

Description

Solution

ER_FEATURE_UNSUPPORTED

Unsupported feature

Verify that the table uses ENGINE=XENGINE and TABLE_FORMAT=COLUMN, and has no secondary indexes, foreign keys, or triggers.

ER_DUP_KEY

Primary key conflict

Check for duplicate primary keys in the data. Set xengine_dload_check_unique_mod=KEEPONE to auto-deduplicate.

ER_XENGINE_CTABLE_NCI_NOT_READY_FOR_ROWPLAN

NCI not ready for row-store plan

Wait for the NCI build to complete, or use a columnar execution plan. Check the build progress in information_schema.XENGINE_CTABLE_NCI_BUILD.

ER_XENGINE_CTABLE_NCI_NOT_READY_FOR_DML

DML attempted during NCI build

DML and DDL operations are not allowed during NCI build. Wait for the NCI build to complete.

Best practices

  • Binary logging is automatically disabled: Direct Load disables binary logging for the import session. The global configuration is not affected.

  • Set appropriate parallelism: For OSS imports, set xengine_dload_parallel to 32–64. For local file imports, use a lower value.

  • Use asynchronous NCI build for large imports: Enable xengine_dload_async_build_nci=ON to reduce import time.

  • Ensure the target table is empty: Direct Load requires an empty target table.

  • Remove secondary indexes before import: Drop secondary indexes before import and recreate them afterward.

  • Use wildcards in OSS mode: Match multiple files with wildcards (*) in a single statement.

  • Monitor NCI build progress: Check information_schema.XENGINE_CTABLE_NCI_BUILD to verify completion before running row-store plans.

  • Prefer Parquet over CSV: Parquet skips text parsing and uses a columnar fast path for better performance.