INDEXとかを作ったり消したりする時の進捗確認

非力なマシンで膨大なレコードを持ってるテーブルのINDEXを作ってたんですがいつになったら終わるのか今どれくらい出来てんのかわかんなくてヤキモキしてました。

という訳でヤキモキしててもしょうが無いので何か方法無いかしらと調べてみました。

ほんでMySQLのデータディレクトリ(/var/db/mysqlとか)にいかにもテンポラリっぽいファイルを発見。
↓こんなファイルが開始と同時に出来てましてみるみる太ってます。

#sql-[ID].MYD
#sql-[ID].MYI
#sql-[ID].frm

ちなみに各ファイルの役割

[テーブル名].frm => テーブル構造関連ファイル
[テーブル名].MYD => データファイル
[テーブル名].MYI => インデックスデータファイル

悲しいかなMySQLの中は分からにゃいので、勝手に想像でこれはインデックス作成時のテンポラリファイルだと推測。
実行が完了すれば対象テーブルのMYDとかと入れ替わるんでしょうとこれまた勝手に推測。

という訳で進捗を計るには以下で良いんじゃないかしらという結論にたどり着きました。

#sql-[ID].MYDのファイルサイズ / 対象テーブルのMYDのファイルサイズ = 進捗率!

そんな訳で待ってるのも暇なのでスクリプト仕込んでみました。
scpチックに進捗を出してくれます。終わりたい時はctl+cで
↓出力イメージはこんな感じです。

 069% |*********************************************************************                               |

但し、データサイズを変更しないDDLを流す時にだけ使えるという何とも言えない代物です。
具体的にはalter table系はだめでcreate/drop indexとか制約つけた時にいけると思います。
INDEX関連はMYIファイルが太るけどデータサイズは大して変わらんでしょうから。
ALTER系はINDEX追加しないならMYIを指標に出来る鴨。

まぁ何はともあれ以下ソース。

use strict;
use warnings;

use DBI;
use Getopt::Long;
use File::Find;
use File::stat;

my %opt;
Getopt::Long::GetOptions(\%opt, "table=s");

# 以下環境に合わせて・・・
our @DATA_SOURCE = ('dbi:mysql:スキーマ名;host=ホスト','ユーザーID','パスワード');


my $_DATA_DIR = undef;
sub get_datadir($) {
    my $dbh = shift;
    if ($_DATA_DIR) {
        return $_DATA_DIR;
    }
    my $sth = $dbh->prepare('show variables');
    $sth->execute();
    my @ret = ();
    while (my $row = $sth->fetch) {
        my ($key, $val) = @$row;
        if ($key eq 'datadir') {
            $_DATA_DIR = $val;
            return $val;
        }
    }
    die "mysql datadir not found.";
}

sub exists_table($$) {
    my $dbh = shift;
    my $tname = shift;
    my $sth = $dbh->prepare('show tables');
    $sth->execute();
    while (my $row = $sth->fetch) {
        my ($t) = @$row;
        if ($tname eq $t) {
            return 1;
        }
    }
    return 0;
}

sub get_schema_name() {
    my ($dbs) = @DATA_SOURCE;
    my ($db, $attr) = split /;/, $dbs;
    my @info= split /:/, $db;
    return $info[2];
}

sub guess_work_file($) {
    my $dbh = shift;
    my $schema = get_schema_name;
    my $data_dir = get_datadir $dbh;
    my $wf;
    File::Find::find(sub {
        my $fname = $File::Find::name;
        if ($fname =~ /$data_dir$schema\/#sql.+\.MYD$/) {
            $wf = $fname;
        }
    }, "$data_dir$schema");
    if ($wf) {
        return $wf;
    }
    die "work file not found.";
}

sub get_myd_file_name($$) {
    my $dbh = shift;
    my $tname = shift;
    my $schema = get_schema_name;
    my $data_dir = get_datadir($dbh);
    my $myd_file = "$data_dir$schema/$tname.MYD";
    unless (-f $myd_file) {
        die "myd file not found.($myd_file)";
    }
    return $myd_file;
}

sub process {
    my $dbh = DBI->connect(@DATA_SOURCE, {RaiseError => 1});
    unless ($opt{table}) {
        die "require option --table=[stirng].";
    }
    unless (exists_table($dbh, $opt{table})) {
        die "not exists `$opt{table}' in database.";
    }
    my $work_file = guess_work_file($dbh);
    my $myd_size = stat(get_myd_file_name($dbh, $opt{table}))->size;
    $dbh->disconnect();

    my $parcent = 0;
    my $templete = " %03d%% |%s%s|\r";
    while (1) {
        my $parcent = sprintf("%.0f", ((stat($work_file)->size / $myd_size) * 100));
        print STDERR sprintf $templete, $parcent, join('', ('*') x $parcent), join('', (' ') x (100 - $parcent));
        sleep(1);
    }
}

process();

テーブルが引き数になってます。後root(かmysql)じゃないと動かないと思うので起動は↓こんな感じ

sudo perl スクリプト名 -t テーブル名

完了したら寝よう・・・。