5

非常に奇妙な動作をする非常に単純なMySQLテーブルがあります。ちなみに、奇妙な振る舞いはまさに私が望んでいることですが、なぜそれが何をしているのかわからないので、これを本番環境に入れたくありません。

とにかく、私は次のように作成されたテーブルを持っています:

Create table `raceTimes` (
    `userID` mediumint(8) unsigned,
    `time` time,
    primary key (`userID`),
    key `idx_time` (`time`)
) engine=InnoDB default charset=utf8;

ここで、raceTimesクエリからSelect *を実行すると、次のような結果セットが得られます。

mysql> Select * from raceTimes;
+--------+----------+
| userID | time     |
+--------+----------+
|     14 | 12:37:46 |
|      6 | 12:41:11 |
|      5 | 12:48:45 |
|     13 | 12:55:46 |
|     10 | 13:13:37 |
|      9 | 13:40:37 |
|     17 | 15:30:44 |
|     18 | 15:46:58 |
|      3 | 16:16:45 |
|      8 | 16:40:11 |
|      7 | 16:41:11 |
|      4 | 16:48:45 |
|     16 | 20:30:44 |
|     15 | 20:37:44 |
|      1 | 21:00:00 |
|      2 | 21:16:00 |
|     11 | 23:13:37 |
|     20 | 23:14:58 |
|     19 | 23:46:58 |
|     12 | 23:55:46 |
+--------+----------+

結果セットは、時間に基づいて、最低から最高の順になっていることに注意してください。さて、これをゲームのリーダーボードに使用しようとしているので、まさにそれがテーブルに実行させたいことです。クエリでexplainを実行すると、次のようになります。

mysql> explain select * from raceTimes;
+----+-------------+------------+-------+---------------+----------+---------+------+------+-------------+
| id | select_type | table      | type  | possible_keys | key      | key_len | ref  | rows | Extra       |
+----+-------------+------------+-------+---------------+----------+---------+------+------+-------------+
|  1 | SIMPLE      | raceTimes  | index | NULL          | idx_time | 4       | NULL |   20 | Using index |
+----+-------------+------------+-------+---------------+----------+---------+------+------+-------------+

これまでのところすべてが素晴らしいです。idx_timeインデックスが(インデックスと同じように)ソートされているため、ソートされた結果セットが返されます。そのために、インデックスをヒットしています。さて、奇妙な振る舞いについて。

私が読んだところによると、主キーはデフォルトでインデックスが付けられており、テーブルをクエリするときに最速のインデックスになるはずです。しかし、それは使用されていません。私の推測では、idx_timeインデックスは、mediumint(8)タイプではなく時間タイプであるため、主キーインデックスよりも小さいと思います。しかし、それは単なる推測です。

ここで、上記で作成したものと同じテーブルを作成しますが、次のように主キーを省略します。

Create table `raceTimes2` (
    `userID` mediumint(8) unsigned,
    `time` time,
    key `idx_time` (`time`)
) engine=InnoDB default charset=utf8;

その場合、結果セットは時間列で並べ替えられません。この動作は、クエリでidx_timeインデックスを具体的に使用するように指示した場合でも当てはまります。また、クエリについて説明すると、次のようになります。

mysql> explain select * from testTable6 use index(`idx_time`);
+----+-------------+------------+------+---------------+------+---------+------+------+-------+
| id | select_type | table      | type | possible_keys | key  | key_len | ref  | rows | Extra |
+----+-------------+------------+------+---------------+------+---------+------+------+-------+
|  1 | SIMPLE      | raceTimes2 | ALL  | NULL          | NULL | NULL    | NULL |   20 |       |
+----+-------------+------------+------+---------------+------+---------+------+------+-------+

だから私が見つけようとしているのは、舞台裏で何が起こっているのかということです。主キーと別のインデックスがある場合、試行しなくてもインデックスで並べ替えられた結果セットを取得できるように見えるのはなぜですか。また、クエリオプティマイザーが主キーインデックスではなく他のインデックスを使用するのはなぜですか。

4

2 に答える 2

2

クエリに「orderby」ステートメントを含めない限り、結果セットの順序に依存しないでください。SQLは明示的に順序を保証しません。小さな例から大きなテーブルで何が起こるかまで一般化しないでください。

結果セットを注文する場合は、以下を含めます。

order by time desc

これは、注文に頼ることができる唯一の方法です。

たとえば、全表スキャンを実行していて、ページキャッシュにすでにいくつかのページがあるとします。これらは-ランダムな順序で-最初に読み取ることができます。または、複数の「スピンドル」(これはSQL Serverの用語です)がテーブルを読み取り、任意の順序で結果を返すようにすることもできます。

于 2012-07-20T22:40:07.363 に答える
2

ゴードンが指摘したように、結果セットの自然な順序に依存するべきではありません。結果が得られる理由は次のとおりです。

最初のケースでは、MySQLはidx_time実際のテーブルを開かずに、インデックスを使用してのみクエリを実行します。これは、使用しているすべての列がインデックス内にある場合に可能です(InnoDBテーブルの主キーは常にすべてのインデックスの最後に追加されるため、インデックスは実際には(timeuserID)舞台裏にあります)。timeこれはインデックス内の実際の順序であるため、結果は時間順に並べられています。

2番目のケースでは、列userIDはインデックスの一部ではなく、MySQLは結果をフェッチするために定期的なテーブルスキャンを実行する必要があります。この場合の「 useindex( )」は、列idx_timeを使用するWHERE句がないため、何もしません。time

編集:
選択肢がある場合にのみ適用され ますが、 USE INDEXMySQLで指定されたインデックスを使用できない場合は、そのテーブルのインデックスを検索に使用せず(WHERE / ON句)、テーブル全体を読み取ります。したがって、インデックスヒントを使用するときは十分に注意する必要があります。
また、explaintype ='index'の行は、テーブル内のすべての行が読み取られることを意味し、type='ALL'とほぼ同じくらい悪いです。

インデックスヒントに関するMySQLマニュアルを確認し、出力について説明する必要があります。

于 2012-07-20T22:55:37.243 に答える