読者です 読者をやめる 読者になる 読者になる

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

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

LOAD DATAを小刻みに行う

mysql

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テーブルと本番用テーブルを入れ替えた方がいい。

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

#!/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;"