# 如何使用 OceanBase 的 LOAD 命令加载 csv 数据文件 OceanBase

OceanBase MySQL 的 `load data` 命令跟 MySQL 的 `load data` 命令是一样的。这里依然导入上面那个 `t1.csv` 。不同之处，需要把 `csv` 文件放到 OBSERVER 节点机器上。暂时 OceanBase 还不支持加载本地文件，该功能还在研发中。

## LOAD 语法

```bash
LOAD DATA
  [/*+ parallel(N) load_batch_size(M)*/]
    INFILE 'file_name'
    [REPLACE | IGNORE]
    INTO TABLE tbl_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_var
        [, col_name_var] ...)]
    [SET col_name={expr | DEFAULT},
        [, col_name={expr | DEFAULT}] ...]
```

## LOAD 原理

Load Data 目前可以对 CSV 格式的文本文件进行导入，整个导入的过程大概分为以下的过程：

+ 解析文件：OB会根据用户输入的文件名，读取文件中的数据，并且根据用户的输入的并行度来决定 并行或者串行解析输入文件中的数据。
+ 分发数据：由于OB是分布式数据库系统，各个分区的数据可能分布在各个不同的observer，Load Data 会对解析出来的数据进行计算，决定数据需要被发送到哪个observer。
+ 插入数据：当目标observer 收到了发送过来的数据之后，在本地执行insert 操作把数据插入到对应的分区当中。

为了提高Load Data 语句的性能，用户可以执行加载数据的并行度，Load Data 在解析文件、计算分区、数据分发阶段都可以多个线程并行工作。 为了避免分布式事务对性能的影响，Load Data 会将按分区将数据分组，并分发到observer上进行多次写入，每次写入开启一个独立的事务，写入的数据来源于一个分组。如果在Load Data 语句执行过程中出现了错误，用户可能需要手工删除已经被加载的数据。另外，如果导入数据文件很大的话，每个节点插入数据的时间可能会很长，请根据需要调整ob_query_timeout参数。

Load Data 提供了很多选项支持用户不同的需求，目前支持的选项有：
**并行度：**
/*+ parallel(N)*/ 选项指定加载数据的并行度，默认值是N=4，建议使用的值范围是[0--租户的最大CPU数]。

```sql
load data /*+ parallel(4) */infile '/home/admin/a.csv' into table t

```

批量：
/*+ load_batch_size(M)*/ 选项指定每次插入的批量大小，默认是M=1000，根据导入数据行的总长度调整M的大小，建议使用的值是[100-1000]。

**输入文件**：
INFILE 'file_name' 关键字指定输入文件的路径和文件名，目前 Load Data 只支持加载 OBSERVER 本地的输入文件。所以，用户需要在导入之前把文件拷贝到某一个observer所在机器上，并连接文件所在的observer运行Load Data 语句。

**目标表索引**：
为了提高导入效率，建议先建基础表，等导入完成后，再创建表的索引。对全局索引，务必要导入之后再创建，否则可能会报not support错误。

执行权限：
用户需要授予权限才能访问机器上文件，有两步：

+ 1. 首先修改安全文件所在路径，设置为空（即无需检查） set global secure_file_priv = "";
+ 2. 对用户授予权限
  + a. mysql 模式，授予file权限：执行 grant file on *.* to USER_NAME;

**重复数据处理**：
这部分指定如何处理重复的数据。Replace 表示将表中原有的数据替换成为输入文件中的数据； Ignore 表示忽略掉重复的数据。Load Data 通过表的主键来判断数据是否重复，如果表不存在主键，那么 Load Data 语句就无法判断数据是否重复，replace 和 ignore 选项没有区别。如果用户不指定这个选项，那么遇到重复数据的时候，Load Data 语句会将出现错误的数据记录到日志文件中。
**目标表选项：**
INTO TABLE tbl_name 关键字用于指定目标表名称。Load Data 支持分区表和非分区表。

**字段格式：**
这部分指定输入文件的各个字段的分隔符选项，通过 Fields|Columns 子句来指定，其中：Terminated By 关键字用来指定字段的分隔符， Enclosed By 关键字指定每个字段的开始和结束是否包含了特定的字符，Escaped By 关键字用来指定字段中的通配符。

**行格式**：
这部分指定输入文件中每一行的开始和结束字符，通过 Lines 子句设置。 其中 Starting By 用于指定每一行开始的字符；Terminated By 用户指定每一行的结束字符。IGNORE number {LINES | ROWS} 子句指定忽略掉输入文件的前number行数据。

```sql
load data /*+ parallel(4) */infile '/home/admin/a.csv' into table t fields terminated by ',' lines terminated by '\n';
```

## 日志文件

如果导入的过程中出现了错误，基于 Load Data 的设计，出现错误的 insert 语句会被回滚，并且 Load Data 语句会在 OBSERVER 安装路径的 log 子目录下产生名称为 obloaddata.log.<XXXXXX> 的日志文件，以下是一个日志文件的示例：

```bash
Tenant name: mysql
File name: /home/admin/a.csv
Into table: `test`.`t`
Parallel: 1
Batch size: 1000
SQL trace: YD7A20BA65670-0005AADAAA3CAB52
Start time: 2020-07-29 21:08:13.073741
Load query:
load data infile '/home/admin/test.csv' into table t fields terminated by ',' lines terminated by '\n'

Row ErrCode ErrMsg
1 1062 Duplicated primary key
2 1062 Duplicated primary key
```

日志中会包含Load Data 产生的任务的基本信息，包含了：租户名，输入文件名，目标表名，并行度，使用的Load Data 命令。 并且以行为单位给出具体错误的信息。

## 示例

以上一节 MySQL 里导出的 CSV 为例，导入到 OceanBase 。

```bash
$ mysql -h127.1 -uroot@obmysql#obdemo -P2883 -p123456 -c -A test -Ns
MySQL [test]> select * from t1;
MySQL [test]> load data infile '/tmp/t1.csv' into table t1 fields terminated by ',' enclosed by '"' lines terminated by '\n' ;
MySQL [test]> select * from t1;
1        中 国   中 国
2        中,国   "中 国"
3        中\n国 中 \n国
4        中\\国  "中\\国"
MySQL [test]>
```

如果有使用问题，欢迎到 OceanBase 社区版官网问答区反馈 。反馈地址：`https://open.oceanbase.com/answer/`(<https://open.oceanbase.com/answer>) 。