MySQLにすべてのインデックスを強制的に強制するにはどうすればよいですか?


12

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秒間実行されていますが、それを強制終了しました。

回答:


24

これがなぜ必要なのかは明確ではありませんが、ヒントUSE INDEX ()を使用して、オプティマイザにインデックスを使用しないように指示できます。MySQLドキュメントから:インデックスのヒント

for省略index_listUSE INDEXても構文的には有効であり「インデックスを使用しない」こと意味します。FORCE INDEXまたはのindex_listを省略するIGNORE INDEXと、構文エラーになります。

クエリは次のようになります。

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)

1
MySQL 5.7.10オプティマイザがクエリプランを変更して、一部のクエリプランを削除したときに、それが最悪のクエリプランに変更されたとき、それは私にとって役に立ちましLEFT JOINた。`USE INDEX()`により、MySQL JOINは2つのインデックス間で500行をクロスする代わりに、20K行のテーブルと1対1のテーブルスキャンを実行しました。20倍速くなりました。
Xenos

2

埋め込むこともできます 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)

インデックスが選択されていません


ローランド、MySQLのオプティマイザーは非常に馬鹿げているので、単純な常に真の条件ではインデックスの使用が禁止されますか?
ypercubeᵀᴹ

@ypercubeはい、しかしそれが起こるのに十分なほどクエリをダミーダウンする必要があります
RolandoMySQLDBA 2015

1
ねえ、私は自分自身の答えを賛成した。私の答えは別の方法であり、オプティマイザーの抜け穴を説明します。
RolandoMySQLDBA 2015

1
Rolando、真ではありません:インデックスが使用されます:SQLfiddle。より複雑なものを作成してもWHERE id+0 = id*1、インデックスは引き続き使用され、追加Using whereが表示されます。
ypercubeᵀᴹ

4
@PaulWhiteなるだろう。(それはばかげていますが、ばかげていません;)そして、それがおそらくRoalndoのクエリがインデックスを使用しない理由であり、列はNULLとして定義されている必要があります。
ypercubeᵀᴹ

0

次の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に相当します。

一つのことは、可能性が異なること- 2つのbtreeのサイズ。SHOW TABLE STATUS LIKE domains_importData_length(ケース1の場合)とIndex_length(ケース2の場合)を確認してください。大きなBTreeは遅くなります。

別のことは異なる可能性があります-キャッシング。の価値はinnodb_buffer_pool_size何ですか?RAMはどれくらいありますか?データ(またはインデックス)をバッファープール内に含めることができますか?(または、これはテーブル/インデックススキャンであるため、37%になりますか?)収まる場合は、クエリを2回実行します。時間が速いため、ディスク(キャッシング)を打っていないに10倍程度になります。

これが1回限りのタスクである場合、SSDが役立ちます。そうでない場合は、テーブル全体をキャッシュできますが、buffer_poolがロードされた後は役に立ちません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.