web-dev-qa-db-ja.com

InnoDBテーブルの複合セカンダリインデックスの最後の列として主キーを持つことは何をしますか?

私は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_によって物理的に並べ替えられますか?

ありがとう

8
user3391564

いいえ。テーブルに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
7
ypercubeᵀᴹ

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の場合は、自動番号順になります。

2
RolandoMySQLDBA

ヒント1:

PRIMARY KEY(x, id),
INDEX(id) -- where `id` is `AUTO_INCREMENT`

完全に有効です。多くのクエリが複数の行WHERE x = 123を見つける必要がある場合、より効率的になるというパフォーマンス上の利点があります。つまり、「自明」よりもわずかに効率的です。

PRIMARY KEY(id),
INDEX(x, id)

AUTO_INCREMENT(InnoDBの場合)に関する唯一の規則は、idsomeインデックスの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には「インデックスを使用しています」と表示されます。

2
Rick James