FORCE
インデックスに関する記事を読みましたが、MySQLに_IGNORE ALL
_インデックスを強制するにはどうすればよいですか?
SELECT * FROM tbl IGNORE INDEX(*)
を試しましたが、うまくいきませんでした。
なぜ私(および他の人)がこれを行う必要があるのかについては、たとえば、次のようにtldでリファラー統計を要約する必要がありました。
_SELECT
count(*) as c,
SUBSTRING
(
domain_name,
LENGTH(domain_name) - LOCATE('.', REVERSE(domain_name)) + 2
) as tld
FROM `domains_import`
IGNORE INDEX(domain_name)
GROUP BY tld
ORDER BY c desc
LIMIT 100
_
...しかし、どのインデックスが定義されているかを常に確認するか、Explainを使用してどのインデックスを使用するかを決定する必要があります。単純に_IGNORE INDEX ALL
_を記述し、単に気にしないのは非常に便利です。
誰かが構文やハックを知っていますか? (MySQL定義テーブルを介した数十行は、実際にはショートカットではありません)。
Bechmark:
インデックスなし= 148.5秒
インデックス= 180秒で、まだデータの送信で実行中SSDアレイは非常に強力であるため、データキャッシュはほとんど気にしません...
ベンチマークの定義:
_CREATE TABLE IF NOT EXISTS `domains_import` (
`domain_id` bigint(20) unsigned NOT NULL,
`domain_name` varchar(253) CHARACTER SET ascii COLLATE ascii_bin NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE `domains_import`
ADD PRIMARY KEY (`domain_id`),
ADD UNIQUE KEY `domain_name` (`domain_name`);
ALTER TABLE `domains_import`
MODIFY `domain_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT;
_
InnoDB、インデックス(USE INDEX()または類似のものなし)を使用したテストが250秒間実行されていますが、それを強制終了しました。
なぜこれが必要なのかは明確ではありませんが、ヒントUSE INDEX ()
を使用して、オプティマイザにインデックスを使用しないように指示できます。 MySQLのドキュメントから: index hints
これは、構文的には[省略
index_list
forUSE INDEX
に対して有効です。これは、[インデックスを使用しない]を意味します。]FORCE INDEX
またはIGNORE INDEX
のindex_listを省略すると、構文エラーになります。
クエリは次のようになります。
SELECT count(*) AS c,
substring_index(domain_name, '.', -1) AS tld
FROM domains_import
USE INDEX () -- use no indexes
GROUP BY tld
ORDER BY c DESC
LIMIT 100 ;
補足:複雑な表現:
SUBSTRING(domain_name, LENGTH(domain_name) - LOCATE('.', REVERSE(domain_name)) + 2)
4つの関数呼び出しから1に簡略化できます。
SUBSTRING_INDEX(domain_name, '.', -1)
WHERE 1=1
を埋め込むこともできます
SELECT
count(*) as c,
SUBSTRING
(
domain_name,
LENGTH(domain_name) - LOCATE('.', REVERSE(domain_name)) + 2
) as tld
FROM `domains_import`
WHERE 1=1
GROUP BY tld
ORDER BY c desc
LIMIT 100
ypercubeがちょうど私に尋ねた
ローランド、MySQLのオプティマイザーは非常に馬鹿げているので、単純な常に真の条件ではインデックスの使用が禁止されますか?
はい、しかしあなたはMySQLに本当にばかげたクエリを与えました。 1=1
はクラスター化インデックスに戻ります。それにもかかわらず、別の方法がありますが、オプティマイザに対して少し悪意がある必要があります。
SELECT
count(*) as c,
SUBSTRING
(
domain_name,
LENGTH(domain_name) - LOCATE('.', REVERSE(domain_name)) + 2
) as tld
FROM `domains_import`
WHERE domain_name = domain_name
GROUP BY tld
ORDER BY c desc
LIMIT 100
domain_name
の各行の値がチェックされるため、これによりバスの下のすべてのインデックスが確実にスローされます。 domain_name
がインデックス付けされている場合は、まったくインデックス付けされていないWHERE column_name=column_name
の列を選択する必要があります。
ステージングサーバーの大きなテーブルでこれを試しました
mysql > explain SELECT COUNT(1) FROM VIDEO WHERE EMBEDDED_FLG=EMBEDDED_FLG;
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
| 1 | SIMPLE | VIDEO | ALL | NULL | NULL | NULL | NULL | 354327 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
1 row in set (0.00 sec)
インデックスが選択されていません
次の2つのインデックスがあると仮定します。
ADD PRIMARY KEY (`domain_id`),
ADD UNIQUE KEY `domain_name` (`domain_name`);
次に、オプティマイザが何をするかは重要ではありません。本質的に同量のものをスキャンする必要があります。
ケース1:テーブルスキャンを実行します(またはdomain_idを使用します):(id、name)のペアをスキャンし、すべての名前を検索し、SUBSTRING..LOCATE、GROUP BY、最後にORDER BYを実行します。 GROUP BYとORDER BYは、おそらくそれぞれtmpテーブルとfilesortを必要とします。 EXPLAIN SELECT ...
をチェックして、機能するかどうかを確認してください。
ケース2:(domain_nameの)インデックススキャンを実行します:そのインデックスには実際には(name、id)ペアが含まれます-InnoDBが暗黙的にPKをセカンダリキーの末尾に配置するためです。残りの処理はケース1に相当します。
1つcouldが異なる-2つのBTreeのサイズ。 SHOW TABLE STATUS LIKE domains_import
を実行して、Data_length(ケース1の場合)およびIndex_length(ケース2の場合)を確認します。大きなBTreeは遅くなります。
別のことは異なる可能性があります-キャッシング。 innodb_buffer_pool_size
の値は何ですか?どのくらいRAMありますか?データ(またはインデックス)をバッファプール内に含めることができますか(または、これがテーブル/インデックススキャンであるため、データの37%になりますか?) )当てはまる場合は、クエリを2回実行しますsecond時間は、ディスクにヒットしないため(キャッシュ)、約10倍高速になります。
これが1回限りのタスクである場合、SSDが役立ちます。そうでない場合は、テーブル全体をキャッシュできますが、buffer_poolがロードされた後は役に立ちません。