雑にindexの効果を調べた
メンターをしている後輩くんのソースをレビューしててindexが足りてなかった。 それがなんでダメなのか雑に調べた。
- player_hoge
- id
- player_id
- index_id
- created_at
- updated_at
みたいなテーブルがある。 使われ方はplayer_idとindex_idの2つでselectする機会が多い。
そのテーブルにplayer_id のみindexが貼ってあった。 こういう場合は player_id と index_id の2つにindexを貼ったほうが早くなる。 はず。
なので調べるように
- index 貼ってない
- player_id いindexを貼った場合
- player_id, index_id に index を貼った場合
sqlはこんな感じ
DROP TABLE IF EXISTS `no_index`; CREATE TABLE `no_index` ( `id` INTEGER unsigned NOT NULL auto_increment, `player_id` INTEGER unsigned NOT NULL DEFAULT 0, `index_id` INTEGER unsigned NOT NULL DEFAULT 0, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4; DROP TABLE IF EXISTS `one_index`; CREATE TABLE `one_index` ( `id` INTEGER unsigned NOT NULL auto_increment, `player_id` INTEGER unsigned NOT NULL DEFAULT 0, `index_id` INTEGER unsigned NOT NULL DEFAULT 0, INDEX `player_id_idx` (`player_id`), PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4; DROP TABLE IF EXISTS `both_index`; CREATE TABLE `both_index` ( `id` INTEGER unsigned NOT NULL auto_increment, `player_id` INTEGER unsigned NOT NULL DEFAULT 0, `index_id` INTEGER unsigned NOT NULL DEFAULT 0, INDEX `player_id_index_id_idx` (`player_id`, `index_id`), PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4;
使ったデータはこんな感じで作りました。 で調査ようのscriptはこんな感じです
use DBI; use DBD::mysql; use Benchmark qw/timethese cmpthese/; my $d = 'DBI:mysql:index_research'; my $u = 'root'; my $p = ''; my $dbh = DBI->connect($d, $u, $p); my $result = timethese(10, { 'player_id index_id use no index' => sub { for my $id (1..100) { my $player_id = $id * 20; my $sql = "SELECT * FROM no_index WHERE player_id = $player_id AND index_id = $id"; my $sth = $dbh->prepare($sql); $sth->execute; $sth->finish; } }, 'player_id index_id use player_id index' => sub { for my $id (1..100) { my $player_id = $id * 20; my $sql = "SELECT * FROM one_index WHERE player_id = $player_id AND index_id = $id"; my $sth = $dbh->prepare($sql); $sth->execute; $sth->finish; } }, 'player_id index_id use player_id index_id index' => sub { for my $id (1..100) { my $player_id = $id * 20; my $sql = "SELECT * FROM both_index WHERE player_id = $player_id AND index_id = $id"; my $sth = $dbh->prepare($sql); $sth->execute; $sth->finish; } }, }); cmpthese $result;
で結果が以下
player_id index_id use no index 100.0/s -- -30% -70% player_id index_id use player_id index 143/s 43% -- -57% player_id index_id use player_id index_id index 333/s 233% 133% --
やっぱりplayer_id と index_id 両方にindex貼ったほうが早かった。
じゃあ、 player_id だけ select して取る場合はどちらが早いのかと思ったのでそれも調べた
my $result_only_player_id = timethese(10, { 'player_id use no index' => sub { for my $id (1..100) { my $player_id = $id * 20; my $sql = "SELECT * FROM no_index WHERE player_id = $player_id"; my $sth = $dbh->prepare($sql); $sth->execute; $sth->finish; } }, 'player_id use player_id index' => sub { for my $id (1..100) { my $player_id = $id * 20; my $sql = "SELECT * FROM one_index WHERE player_id = $player_id"; my $sth = $dbh->prepare($sql); $sth->execute; $sth->finish; } }, 'player_id use player_id index_id index' => sub { for my $id (1..100) { my $player_id = $id * 20; my $sql = "SELECT * FROM both_index WHERE player_id = $player_id"; my $sth = $dbh->prepare($sql); $sth->execute; $sth->finish; } }, }); cmpthese $result_only_player_id;
結果がこちら
player_id use no index 90.9/s -- -55% -64% player_id use player_id index 200/s 120% -- -20% player_id use player_id index_id index 250/s 175% 25% --
player_id だけの方が早いかと思ったけどそうでもなかった。 データ量がおこまで多くないと違うのかもしれない。
まとめ
index貼るときはどう使うかも考えて貼ったほうがいいですね。