###前提・実現したいこと
以下の状況において、LIKE の前方一致によるINDEXが効かず、性能が出なくて困っております。。。
INDEXが効かない原因と対応策を教えて頂けないでしょうか。
<状況>
1:CREATE TABLEテーブルは以下です。
SQL
1CREATE TABLE `tb_test_data` ( 2 `id` bigint(20) NOT NULL AUTO_INCREMENT, 3 `col1` bigint(20) NOT NULL DEFAULT '0', 4 `col2` int(11) NOT NULL DEFAULT '0', 5 `col3` float NOT NULL DEFAULT '0', 6 `col4` float NOT NULL DEFAULT '0', 7 `col5` int(3) NOT NULL DEFAULT '0', 8 `col6` int(11) NOT NULL DEFAULT '0', 9 `col7` bigint(20) NOT NULL, 10 `url` text, 11 `date` date NOT NULL, 12 PRIMARY KEY (`id`), 13 UNIQUE KEY UNI_col1_col5( col1, col5 ), 14 KEY `IX_col2` (`col7`, col2), 15 KEY `IX_col3` (`col7`, col3), 16 KEY `IX_col4` (`col7`, col4), 17 KEY `IX_col5_col1` (`col7`, col5, col1), 18 KEY `IX_col6` (`col7`, col6), 19 KEY `IX_col1_col5` (`col7`, col1, col5), 20 KEY `IX_date` (`col7`, date), 21 KEY `IX_url` ( url(255) ) 22) ENGINE=MyISAM DEFAULT CHARSET=utf8;
2:上記テーブルは更新型テーブル。更新頻度は毎秒100件くらいのDELETE / INSERTを24時間実施。
(MyISAMのため、参照するとテーブルロックになり、更新はされません)
登録順はcol1をキーにcol5の分だけ削除し、削除したcol1の値でcol5の分だけ登録を行います。
3:上記テーブルは現在、1.5億レコード存在します。AUTO_INCREMENTの値は4.5億です。
4:URL に INDEXを効かせたいSQLは以下のSQLです。
SQL
1SELECT T1.* 2FROM tb_test_data T1 3WHERE T1.col7 = 123456 4 AND T1.url like 'http://abc.jp/xxx/xxxx/xxx%' 5ORDER BY col1 6Limit 0, 100
5:実行計画
SQL
1id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra 21 | SIMPLE | T1 | range | IX_col1,IX_col2,IX_col3,IX_col5_col1,IX_col6,IX_col1_col5,IX_date,IX_url | IX_col1_col5 | 8 | NULL | 1157468 | Using index condition; Using where
※上記の実行計画は「http://abc.jp/xxx/xxxx/xxx%」の部分を68文字のURLを指定し、このURLだけで1件の表示になる値で実行計画を出しています。そのため、理想の実行計画としては、
refに「IX_url」が表示され、rows項目も1となる事を想定していました。
※仮に「http://abc.jp/xxx/xxxx/xxx%」とした場合、データが存在しないため、
ref項目に「IX_url」が表示され、rows項目も1となります。
6:その他注意事項
・URL項目に対して、Likeではなく、=による絞り込みはNGとなります。
・URLを階層別にハッシュ化して、=による絞り込みもNGとなります。
・URLを1文字ずつずらしてハッシュ化して、=による絞り込みはOKですが、
INDEXが最大数(64個)を超えてしまうため、出来ないと考えてます。
・MATCH AGAINSTはOKですが、この更新型テーブルにFULLTEXT INDEXを貼って効果があるかは、現在調査中です。
###発生している問題・エラーメッセージ
USE INDEX ( IX_url )やUSE KEY( IX_url )を利用しても、実行計画のref項目に「IX_url」は表示されず、
実際に実行してもものすごく時間がかかる(10分経っても終わらない)状況です。
これを数秒以内に結果が返ってくる形にしたいと考えています。
###該当のソースコード
上記SQL参照
###試したこと
上記説明参照
###補足情報(言語/FW/ツール等のバージョンなど)
MySQLのバージョンは5.6.22
phpMyAdminのバージョンは4.5.1(ちょっと古い)
実行計画や速度が遅く感じると判断した際の動作環境は
phpMyAdmin上での実行結果になります。
###追記
check table tb_test_data;
を実行した場合、2時間経っても終わらないため、実施していません。
check table tb_test_data fast quick;
にて、テーブルの状態を確認していますが、
statusは「Table is already up to date」の状態で、問題ない認識です。
show table status like 'tb_test_data%';
にて、断片化は確認をしていて、0.6%程度の断片化率です。
(該当テーブルのURL以外の指定における性能劣化は見られていません。)
###追記2
検索用URL(100文字等)を作ってINDEXを貼って検索するのは、個人的には良いのですが、
仕様決定者的にはNGでした。。
###追記3
index(col7,col1,url(200))の追加案については、
col7での絞込件数≒実際の絞込件数(urlによる絞り込みがほとんどない)
であれば、Limitが効くため、性能は良いですが、
検索結果が「1件」の場合、Limitの効果がないため、性能遅延が発生してしまいます。
index(col1,col7,url(200))の追加案については、
全体の件数≒col7での絞込件数≒実際の絞込件数(col7,urlによる絞り込みがほとんどない)
であれば、Limitが効くため、性能は良いですが、
検索結果が「1件」の場合、Limitの効果がないため、性能遅延が発生してしまいます。
なお、上記2つのINDEXでは、URLのINDEXには届かないため、1件抽出する場合、即レスポンスにならないです。
INDEXさえ効けば、検索結果が1件の場合、即終わるハズなのですが、
実行計画の時点で効かない原因を知りたいです。。
###追記4
InnoDBについて
InnoDB化した場合、300万の登録に60分経っても終わらなかったため、INDEXが効くかどうかは分かりません。
別の仕様(毎秒100件の削除・登録)が不可であるため、InnoDB化はできません。
(MyISAMであれば、300万の登録に7分で終わります)
InnoDBは統計情報がきれいなはずなので、InnoDBであれば、確かに効く気はしています。
###追記5
INDEXが効いたSQLと効かないSQLがありましたので、情報共有いたします。
”存在しないURL”を指定すると、基本的に、INDEXが効きいて、1秒以内のレスポンスになりました。
ただし、"存在しないURL"が50文字以上となると、INDEXが効かず、300秒くらいのレスポンスになってしまいました。。
MySQL5.6.22のバグなんじゃないかと思い始めてます。。
###追記6
AkiraPenguin 様の回答により、解決いたしました!
ありがとうございます!!!!!
なお、追記5の問題も、ちょうど50文字目が「」となっておりました。
「」を含むURLは300万件存在するため、「_」の後に存在しないURLを置いていても、
それはINDEXが効きません。。。当然ですね。。。
たくさんの閲覧・回答ありがとうございました!
回答8件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2016/09/14 03:55
2016/09/15 02:56
2016/09/15 10:57