使用 MySQL-rust 导入数据无效

mysql / rust

最近打算写一个工具来向数据库导入较大规模的数据,导入时遇到了些问题,记录一下

使用到的工具和库

docker

我的 mysql 是使用 docker 安装的,可以说是有好有坏,好处是系统相对整洁,便于清理, 但也有不方便的地方,比如修改配置,mysql 版本是 5.7

rust / mysql crate

语言选择了 rust,这个属于个人喜好,mysql 接口库选择了 mysql crate,是因为这个库 执行 raw sql 比较方便直观

问题

经常跟大规模数据打交道的话,可能都对大量数据的导入颇有感慨,大部分的图形工具还处 于把数据文件分解成 insert 语句来进行导入,小规模的数据没有问题,数据量一大,那 很多时候就不是出去喝喝茶的事情了,之前有同事下班前启动导入,第二天上班,还没有完 成,我们知道这种情况下,我们就要请出 mysqlimport,或是直接用 load data 命令来 导入,上 G 的数据也可以在有限时间内完成,而不是要苦等一天,这次我们也是会用到 load data,只是我打算用 rust 来完成。

感觉这个思路不会很难对不对,只是包装一下 sql 语句而已,但是执行到 load data 时, mysql 报错:

ERROR 1290 (HY000): The MySQL server is running with the –secure-file-priv option so it cannot execute this statement

我的 sql 语句是这样的:

LOAD DATA INFILE 'filename.txt' INTO TABLE Table_Name
FIELDS TERMINATED BY '\x1'
LINES TERMINATED BY '\n'

一番研究之后,我估计是因为我没有加 LOCAL 关键字的原因,因为这次我不是像之前一样 在 mysql db 上运行 sql,而是在另一台开发电脑上,所以需要将文件先传到 mysql 所在 的服务器,然后再执行导入指令,修改后的命令变成这样:

LOAD DATA LOCAL INFILE 'filename.txt' INTO TABLE Table_Name
FIELDS TERMINATED BY '\x1'
LINES TERMINATED BY '\n'

这次没有任何报错,程序顺利执行完成,但是查看数据库发现并未导入任何数据。

这个就诡异了,没有报错,也没有导入,真的是 mysql 配置的问题吗,这个问题我花了不 少时间,中间也改过 docker mysql 的配置,后来证明这个问题和配置无关。

解决

这个问题在于,调用 LOAD DATA LOCAL INFILE 前,需要指定一个 file handler,这样 才能正确地将文件传送到 mysql 服务器执行导入,详细了解可能要看一下 这里, 对于 rust mysql,这个 handler 可以是这样:

conn.set_local_infile_handler(Some(LocalInfileHandler::new(|file_name, writer| {
    let file_name_str = String::from_utf8_lossy(file_name).to_string();
    let file_content = fs::read_to_string(file_name_str.as_str())?;
    writer.write_all(file_content.as_bytes())
})));

设置后再执行 load data,就不再有问题了。