6

これらの小さなテーブルがitemあり、次のcategoryとおりです。

CREATE TABLE `item` (
  `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(150) NOT NULL,
  `category_id` mediumint(8) unsigned NOT NULL,
  PRIMARY KEY (`id`),
  KEY `name` (`name`),
  KEY `category_id` (`category_id`)
) CHARSET=utf8

CREATE TABLE `category` (
  `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(150) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `name` (`name`)
) CHARSET=utf8

100 のカテゴリと 1000 のアイテムを挿入しました。

これを実行すると:

EXPLAIN SELECT item.id,category.name AS category_name FROM item JOIN category ON item.category_id=category.id;

次に、テーブルのエンジンが InnoDB の場合、次のようになります。

+----+-------------+----------+-------+---------------+-------------+---------+--------------------+------+-------------+
| id | select_type | table    | type  | possible_keys | key         | key_len | ref                | rows | Extra       |
+----+-------------+----------+-------+---------------+-------------+---------+--------------------+------+-------------+
|  1 | SIMPLE      | category | index | PRIMARY       | name        | 452     | NULL               |  103 | Using index |
|  1 | SIMPLE      | item     | ref   | category_id   | category_id | 3       | dbname.category.id |    5 | Using index |
+----+-------------+----------+-------+---------------+-------------+---------+--------------------+------+-------------+

一方、MyISAM に ( を使用してalter table engine=myisam) 切り替えると、次のようになります。

+----+-------------+----------+--------+---------------+---------+---------+-------------------------+------+-------+
| id | select_type | table    | type   | possible_keys | key     | key_len | ref                     | rows | Extra |
+----+-------------+----------+--------+---------------+---------+---------+-------------------------+------+-------+
|  1 | SIMPLE      | item     | ALL    | category_id   | NULL    | NULL    | NULL                    | 1003 |       |
|  1 | SIMPLE      | category | eq_ref | PRIMARY       | PRIMARY | 3       | dbname.item.category_id |    1 |       |
+----+-------------+----------+--------+---------------+---------+---------+-------------------------+------+-------+

私の質問は、インデックスの処理方法のこの違いはなぜですか?

4

1 に答える 1

4

InnoDB では、すべてのセカンダリ インデックスに、テーブルの主キー列が内部的に含まれています。したがって、列 (name) のインデックスは暗黙的に列 (name, id) にあります。

これは、EXPLAIN がカテゴリ テーブルへのアクセスを「インデックス スキャン」として表示することを意味します (これは、タイプ列に「インデックス」として表示されます)。インデックスをスキャンすることで、2 番目のテーブル item の行を検索するために使用する id 列にもアクセスできます。

次に、実際には (category_id, id) である (category_id) のアイテム インデックスも利用し、インデックスを読み取るだけで選択リストの item.id を取得できます。テーブルを読み取る必要はまったくありません (これは、Extra列に「インデックスの使用」として示されています)。

MyISAM は、このようにプライマリ キーをセカンダリ キーと共に保存しないため、同じ最適化を行うことはできません。カテゴリ テーブルへのアクセスは、テーブル スキャンを意味するタイプ「ALL」です。

(category_id) のインデックスを使用して行を検索するため、MyISAM テーブル項目へのアクセスは "ref" になると思います。ANALYZE TABLE itemただし、テーブル内の行が非常に少ない場合、またはインデックスを作成してから行っていない場合、オプティマイザーは歪んだ結果を取得する可能性があります。


あなたの更新について:

オプティマイザはテーブル スキャンよりもインデックス スキャンを優先しているように見えるため、InnoDB でインデックス スキャンを実行する機会を利用して、カテゴリ テーブルを最初に配置します。オプティマイザは、クエリで指定した順序でテーブルを使用するのではなく、テーブルの順序を変更することを決定します。

MyISAM テーブルでは、最初にアクセスするために選択したテーブルに 1 つのテーブル スキャンがありますが、カテゴリ テーブルを 2 番目に配置することで、アイテムのセカンダリ インデックスではなく、カテゴリの PRIMARY キー インデックスに結合します。オプティマイザーは、一意のキーまたは主キー (タイプ "eq_ref") へのルックアップを優先します。

于 2013-04-15T20:42:59.290 に答える