文系プログラマによるTIPSブログ

文系プログラマ脳の私が開発現場で学んだ事やプログラミングのTIPSをまとめています。

MySQLでLOAD DATAを小刻みに行う

効果の程はどうでしょうね〜


f:id:treeapps:20180418131549p:plain

http://www.mysqlperformanceblog.com/2008/07/03/how-to-load-large-files-safely-into-innodb-with-load-data-infile/

これに対抗してシェルスクリプトだけでLOAD DATAを小刻みに行うコードを書いてみます。

中途半端な引数だったり、MySQL関連の変数がベタ書きだったり、ファイル名は「テーブル名.tsv」固定なのは勘弁して下さい。

#/bin/sh

filePath=$1
splitLineCount=$2
schemaName=$3
dir="/Users/tree"
USAGE="Usage:<filePath> <splitLineCount> <schemaName>"

if [ -z $filePath ] || [ -z $splitLineCount ] || [ -z $schemaName ]; then
    echo "$USAGE"
    exit 1
fi

echo "bulk load data start."

# file split
fileRegex="$filePath\.*"
rm -rf $fileRegex
split -l $splitLineCount $filePath $filePath.
splitFileCount=$(ls $fileRegex | wc -l)

# mysql setting
tableName="$(echo $(basename $filePath) | cut -d'.' -f1)"
MYSQL_CON="mysql -uroot $schemaName"
FIELDS_TERMINATED_BY="\t"
ENCLOSED_BY=""
LINES_TERMINATED_BY="\n"
count=1

# truncate
$MYSQL_CON -e "truncate $tableName;"

# bulk load data
for file in `ls $fileRegex`
do

$MYSQL_CON <<EOF
alter table $tableName disable keys;
load data infile "$dir/$file" into table $tableName fields terminated by "$FIELDS_TERMINATED_BY" enclosed by "$ENCLOSED_BY" lines terminated by "$LINES_TERMINATED_BY";
alter table $tableName enable keys;
EOF

if [ $? -eq 1 ]; then
    echo "[$count/$splitFileCount] load data $file failure."
    exit 1
else
    echo "[$count/$splitFileCount] load data $file success."
fi

rm -rf $file
count=$(expr $count + 1)
done

echo "bulk load data finish."

実行結果は以下の通りです。

$ ll bulkLoadData.sh t_test.tsv 
 -rwxr-xr-x  1 tree  staff     1193  4 14 00:40 bulkLoadData.sh
 -rw-r--r--  1 tree  staff  2109444  3 31 01:45 t_test.tsv
$ ./bulkLoadData.sh t_test.tsv 100000 test
bulk load data start.
[1/4] load data t_test.tsv.aa success.
[2/4] load data t_test.tsv.ab success.
[3/4] load data t_test.tsv.ac success.
[4/4] load data t_test.tsv.ad success.
bulk load data finish.

LOAD DATAする時はDISABLE KEYSしてます。

これで巨大なファイルを読み込む時にバッファを使い切る事もなくなりますかね??

このbulkLoadData.shですが、workテーブルに対して読み込んで、データ登録完了後にworkテーブルと本番用テーブルを入れ替えた方が良さそうです。

こんな感じです↓

#!/bin/sh
mysql -uroot test -e "create table t_test_work like t_test;"
./bulkLoadData.sh t_test_work.tsv 10000 test
if [ $? -eq 1 ]; then
    exit 1
fi
mysql -uroot test -e "rename table t_test to t_test_old, t_test_work to t_test;"

t_test_workまでは失敗してもt_testに全然影響無し。rename テーブルはトランザクションが有効なので、影響無く入れ替えられるはず。