私は1対Nの関係があるとしましょう_(person_id, pet_id)
_。 _pet_id
_が主キーであるテーブルがあります。
InnoDBセカンダリインデックスは基本的にBツリーであり、値は行の対応するプライマリキー値であると理解しています。
さて、1人が何千ものペットを飼うことができ、私はしばしば_pet_id
_の順序でその人のペットを必要とするとします。次に、セカンダリインデックスのレコードが_(person_id, pet_id)
_で並べ替えられているか、_person_id
_の_pet_id
_が並べ替えられていない_person_id
_だけで並べ替えられているかが重要になります。後で推測。
したがって、_person_id
_が一意でない場合、レコードは_(person_id, pet_id)
_またはJUST _pet_id
_によって物理的に並べ替えられますか?
ありがとう
いいえ。テーブルにInnoDBエンジンがあり、PRIMARY KEY
が(pet_id)
の場合、セカンダリインデックスを(person_id)
または(person_id, pet_id)
として定義しても違いはありません。
インデックスにはpet_id
列も含まれているため、どちらの場合も値は(person_id, pet_id)
としてソートされます。
あなたが持っているようなクエリ:
SELECT pet_id FROM yourtable
WHERE person_id = 127
ORDER BY pet_id ;
pet_id
の値は既にインデックスで並べ替えられているため、値を取得するにはインデックスのみにアクセスする必要があり、さらに並べ替えを行う必要はありません。これは、実行プラン(EXPLAIN
)を確認することで確認できます。
最初に、MyISAMテーブルを試してみます。
CREATE TABLE table pets
( pet_id int not null auto_increment PRIMARY KEY,
person_id int not null,
INDEX person_ix (person_id)
) ENGINE = myisam ;
INSERT INTO pets (person_id)
VALUES (1),(2),(3),(1),(2),(3),(4),(1),(8),(1),(2),(3) ;
mysql> EXPLAIN SELECT pet_id FROM pets
WHERE person_id = 2
ORDER BY pet_id asc \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: pets
type: ref
possible_keys: person_ix
key: person_ix
key_len: 4
ref: const
rows: 3
Extra: Using where; Using filesort
1 row in set (0.00 sec)
filesortに注意してください!
さて、複合インデックスを持つMyISAM:
DROP TABLE IF EXISTS pets ;
CREATE TABLE table pets
( pet_id int not null auto_increment PRIMARY KEY,
person_id int not null,
INDEX person_ix (person_id, pet_id) -- composite index
) ENGINE = myisam ;
INSERT INTO pets (person_id)
VALUES (1),(2),(3),(1),(2),(3),(4),(1),(8),(1),(2),(3) ;
mysql> EXPLAIN SELECT pet_id FROM pets
WHERE person_id = 2
ORDER BY pet_id asc \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: pets
type: ref
possible_keys: person_ix
key: person_ix
key_len: 4
ref: const
rows: 3
Extra: Using where; Using index
1 row in set (0.00 sec)
Filesort is gone、予想通り。
InnoDBエンジンで同じことを試してみましょう:
DROP TABLE IF EXISTS pets ;
CREATE TABLE table pets
( pet_id int not null auto_increment PRIMARY KEY,
person_id int not null,
INDEX person_ix (person_id) -- simple index
) ENGINE = innodb ; -- InnoDB engine
INSERT INTO pets (person_id)
VALUES (1),(2),(3),(1),(2),(3),(4),(1),(8),(1),(2),(3) ;
mysql> EXPLAIN SELECT pet_id FROM pets
WHERE person_id = 2
ORDER BY pet_id asc \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: pets
type: ref
possible_keys: person_ix
key: person_ix
key_len: 4
ref: const
rows: 3
Extra: Using where; Using index
1 row in set (0.00 sec)
filesortもありません!インデックスにpet_id
列がない場合でも、値はそこにあり、ソートされます。 (person_id, pet_id)
でインデックスを定義した場合、EXPLAIN
が同一であることを確認できます。
InnoDBと複合インデックスを使用して、実際にそれを実行しましょう。
DROP TABLE IF EXISTS pets ;
CREATE TABLE table pets
( pet_id int not null auto_increment PRIMARY KEY,
person_id int not null,
INDEX person_ix (person_id, pet_id) -- composite index
) ENGINE = innodb ; -- InnoDB engine
INSERT INTO pets (person_id)
VALUES (1),(2),(3),(1),(2),(3),(4),(1),(8),(1),(2),(3) ;
mysql> EXPLAIN SELECT pet_id FROM pets
WHERE person_id = 2
ORDER BY pet_id asc \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: pets
type: ref
possible_keys: person_ix
key: person_ix
key_len: 4
ref: const
rows: 3
Extra: Using where; Using index
1 row in set (0.00 sec)
同じ計画前のケースと同じです。
100%確実にするために、最後の2つのケース(単一インデックスと複合インデックスを使用するInnoDBエンジン)も実行して、file_per_table
設定を有効にし、テーブルに数千行を追加します。
DROP TABLE IF EXISTS ... ;
CREATE TABLE ... ;
mysql> INSERT INTO pets (person_id)
VALUES (1),(2),(3),(1),(2),(3),(4),(1),(8),(1),(2),(3) ;
Query OK, 12 rows affected (0.00 sec)
Records: 12 Duplicates: 0 Warnings: 0
mysql> INSERT INTO pets (person_id)
VALUES (1),(2),(3),(1),(2),(3),(4),(1),(8),(1),(2),(3),(127) ;
Query OK, 13 rows affected (0.00 sec)
Records: 13 Duplicates: 0 Warnings: 0
mysql> INSERT INTO pets (person_id)
VALUES (1),(2),(3),(1),(2),(3),(4),(1),(8),(1),(2),(3),(127) ;
Query OK, 13 rows affected (0.00 sec)
Records: 13 Duplicates: 0 Warnings: 0
mysql> INSERT INTO pets (person_id)
SELECT a.person_id+b.person_id-1
FROM pets a CROSS JOIN pets b CROSS JOIN pets c ;
Query OK, 54872 rows affected (0.47 sec)
Records: 54872 Duplicates: 0 Warnings: 0
どちらの場合も、実際のファイルサイズをチェックすると、同一の結果が得られます。
ypercube@apollo:~$ Sudo ls -la /var/lib/mysql/x/ | grep pets
-rw-rw---- 1 mysql mysql 8604 Apr 21 07:25 pets.frm
-rw-rw---- 1 mysql mysql 11534336 Apr 21 07:25 pets.ibd
MySQLのドキュメントによると クラスター化インデックスとセカンダリインデックス
セカンダリインデックスとクラスター化インデックスの関係
クラスタ化インデックス以外のすべてのインデックスは、セカンダリインデックスと呼ばれます。 InnoDBでは、セカンダリインデックスの各レコードには、行のプライマリキー列とセカンダリインデックスに指定された列が含まれます。 InnoDBはこの主キー値を使用して、クラスター化インデックス内の行を検索します。
主キーが長い場合、セカンダリインデックスはより多くのスペースを使用するので、主キーを短くすることは有利です。
したがって、プライマリキーをセカンダリインデックスに追加することは間違いなく冗長です。インデックスエントリは_(person_id, pet_id, pet_id)
_のようになります。これはまた、_PRIMARY KEY
_の2つのコピーを持つことで、セカンダリインデックスを不必要に膨らませることになります。
_(person_id)
_を含むインデックスの場合、このようなクエリを実行すると
_SELECT * FROM yourtable WHERE person_id = 127 ORDER BY pet_id;
_
_PRIMARY KEY
_はこのクエリに完全に関与しており、いずれにしても_PRIMARY KEY
_で順序付けられた結果を生成します。物理的な観点から、行は挿入順に並べられます。 pet_idがAUTO_INCREMENTの場合は、自動番号順になります。
ヒント1:
PRIMARY KEY(x, id),
INDEX(id) -- where `id` is `AUTO_INCREMENT`
完全に有効です。多くのクエリが複数の行WHERE x = 123
を見つける必要がある場合、より効率的になるというパフォーマンス上の利点があります。つまり、「自明」よりもわずかに効率的です。
PRIMARY KEY(id),
INDEX(x, id)
AUTO_INCREMENT
(InnoDBの場合)に関する唯一の規則は、id
がsomeインデックスのfirst列でなければならないことです。このルールはPRIMARY
またはUNIQUE
または「列のみ」については何も述べていないことに注意してください。
ヒントは、x
が他のものと一緒にフェッチすることが多い巨大なテーブルで役立ちます。
ヒント2:あるとします
SELECT name FROM tbl WHERE person_id = 12 AND pet_id = 34;
これは「カバーする」インデックスです。
INDEX(person_id, pet_id, name)
つまり、クエリ全体をインデックスのBTree内で実行できます。 EXPLAINには「インデックスを使用しています」と表示されます。