Mysqlに大量データをInsertしたい時、マルチプルインサートをしてたけども、LOAD DATA INFILEならさらに高速にInsertを行えるらしい。気になったのでやってみたときのメモ。
環境
mysql:5.7
docker-compose.ymlはversion 3
以上のおそらく初歩的エラー4本立てで書き残していく。
はじめに
target_table(データをimportしたいテーブル)
id int(11) 連番
user_id varchar(255)
target_date datetime
count int(11) [0]
import_data.csv(Importするデータ)
abcd,2021-04-16 01:00:00,1
bcda,2021-04-16 05:00:00,2
cdab,2021-04-16 21:00:00,1
ディレクトリの大体の構成
product/
├ mysql/
│ ├ Dockerfile
│ └ my.conf...etc
│
├ app/
│ ├ php.ini
│ └ Dockerfile...etc
......(関係なさそうなのは端折る)
├.docker-compose.yml
│
└ www/
├ index.php..etc
└ data/
└import_data.csv
docker-compose.ymlの一部
version: "3"
services:
php:
container_name: php
build: ./app
env_file:
- ./web/web.env
volumes:
- ./www:/var/www
- ./app/php.ini:/usr/local/etc/php/php.ini
links:
- mysql
mysql:
container_name: mysql-db
build: ./mysql
volumes:
- ./www/mysql_app_share:/tmp/mysql_app_share
...etc
入力コマンド
LOAD DATA INFILE 'var/www/data/import_data.csv' INTO TABLE target_table FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n' (user_id,target_date,count);
エラー1.php.iniの設定
まずはこのエラーに当たる
LOAD DATA LOCAL INFILE forbidden
解消
phpinfo()で確認してみるとmysqli.allow_local_infile=”Off”…
docker-composeでphp.iniにマウントしているファイルをmysqli.allow_local_infile = “On”
に書き換えてdockerを再起動。
エラー2.Mysqlセキュリティの設定
次はこのエラー
The MySQL server is running with the --secure-file-priv option so it cannot execute this statement
secure-file-priv optionが動作して怒られてるみたい。
書き込みが安全に実行できるディレクトリに –secure-file-priv を設定したりできます。
-MySQL 5.6 リファレンスマニュアル
読み書きしたいものはここで指定したディレクトリに置かないといけないみたい。
設定を確認すると
@@global.secure_file_priv
-------------------------
/var/lib/mysql-files/
なんだか全然違うとこが参照になってた。
解消
他のphpファイルとかのあるwww/data/配下におきたいので新たにmy.cnfに記述を追加
[mysqld]
secure-file-priv = "www/data/"
マウントを追加して再起動。
volumes
./mysql/my.cnf:/etc/mysql/conf.d/my.cnf
エラー3.DockerとMysqlでファイルを参照できない
また怒られた
Can't get stat of '/var/lib/mysql/var/www/data/import_data.csv' (Errcode: 2 - No such file or directory)
当然だったんだけど、mysqlコンテナ内だけを参照する..
そこで
Dockerボリューム機能を使用して、外部のストレージとかを別コンテナにマウントできる->これを使っていこう
解決
docker上のmysqlコンテナとphp用コンテナでファイルを共有できるディレクトリが必要。
お得意のvolumeマウントでmysql部分に追記
volumes:
- ./mysql/my.cnf:/etc/mysql/conf.d/my.cnf
- ./www/data:/tmp/data<-ここ
でdocker-compose up -dとrestartをかけるとcsvファイルは見つからないよと怒られなくなる。
ちなみにこれでimport/Export用ディレクトが/tmp/dataに決まったので、エラー2でsecure-file-priv = “”にしちゃってたのをsecure-file-priv = “/tmp/data/”に書き換えてちょっと安全になった気でいる(どこのディレクトリでもImport/Exportオッケーにしておくのはよろしくないなということで)
おまけ.datetime型が入らない
Incorrect datetime value: 2021-04-16 01:00:00 for column 'target_date' at row 1
解決
SET sql_mode = 'ONLY_FULL_GROUP_BY,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';
or
SET sql_mode='';
…紆余曲折を経て
LOAD DATA INFILE '/tmp/data/import_data.csv' INTO TABLE target_table FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n' (user_id,target_date,count);
クエリーを実行しました。3 行を変更しました (0.009 秒)
Yeah!
結局マルチプルインサートになるかもしれないけれど、ダミーデータ挿入とかでも使えそうなのでちゃんと覚えておこう。
本日もおつかれさま👏👏