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


8

たとえば、1対Nの関係があるとし(person_id, pet_id)ます。pet_idが主キーであるテーブルがあります。

InnoDBセカンダリインデックスは基本的にBツリーであり、値は行の対応するプライマリキー値であると理解しています。

さて、一人の人が何千ものペットを持つことができ、私はしばしば人のペットをの順にしたいとしpet_idます。次に、セカンダリインデックスのレコードが並べ替えられている(person_id, pet_id)か、並べ替えられていないためのだけperson_idであるかが重要になります。後で推測。pet_idperson_id

では、person_idが一意でない場合、レコードは、(person_id, pet_id)またはJUST によって物理的にソートされていpet_idますか?

ありがとう


1
私は最後の質問が本当にそうであると思います:「それで、もしperson_idユニークでないなら、レコードは物理的に、(person_id, pet_id)またはちょうどによってソートされperson_idますか?」
ypercubeᵀᴹ

回答:


7

いいえ。テーブルにInnoDBエンジンとPRIMARY KEYisが含ま(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はなくなりました


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

1
InnoDBはMS SQL Serverにこの点で同様に動作と仮定すると、そこにあるの指標との差異(<some_column>)及び(<some_column>, <pk>)理由ON (<some_column>)と同等でON (<some_column>) INCLUDE (<pk>)はないがON (<some_column>, <pk>)。ほとんどの場合、これはほとんど意味がありませんが、PKがランダム(つまり、UUID)のON (<s_c>,<pk>)場合、余分な断片化が発生する可能性があります。または、PKがキー以外の意味がある場合ORDER BY s_c, pk、インデックスのようにソートが高速になる可能性があります。すでに完全に整っています。
David Spillett 16

@DavidSpillettそうです。MySQLにはINCLUDE (columns)機能はありません。それが、(s_c)インデックスがと同等であると結論付けたもう1つの理由(s_c, pk)です。
ypercubeᵀᴹ

私がバックアップするためのドキュメントを見つけることはできません(そのため、覚えていないと思います)が、InnoDBは、要求されない限り、セカンダリインデックスでPKを安定した順序で保持しないことを読んだことは確かです。とにかく違いは小さいですが。次にmySQLを試す時間があるときは、理論をテストする必要があります...
David Spillett

@DavidSpillett - blog.jcole.us/2013/01/10/... セカンダリインデックス -セクション「セカンダリインデックス非リーフ・ページのためのノートの一つのことがあります:クラスタ化されたキーフィールド(PKV)レコードに含まれているされているが値ではなく、レコードのキーの一部と見なされました。」少なくともページのレベルでそれらを注文します。その説明からそれが単一のページ内にどのようにあるか正確にわかりませんが、そうでない場合でも、それは小さなバッファによって単純に解決されます-1つのページからPKを読み取り、並べ替え(最大〜500?無関係。
jkavalik 2016年

2

クラスタ化インデックスとセカンダリインデックスに関するMySQLドキュメントによると

セカンダリインデックスとクラスター化インデックスの関係

クラスタ化インデックス以外のすべてのインデックスは、セカンダリインデックスと呼ばれます。InnoDBでは、セカンダリインデックスの各レコードには、行のプライマリキー列と、セカンダリインデックスに指定された列が含まれます。InnoDBはこの主キー値を使用して、クラスター化インデックス内の行を検索します。

主キーが長い場合、セカンダリインデックスはより多くの領域を使用するので、主キーを短くすることが有利です。

したがって、プライマリキーをセカンダリインデックスに追加することは間違いなく冗長です。インデックスエントリが必要です(person_id, pet_id, pet_id)。これにより、のコピーが2つあるため、セカンダリインデックスが不必要に膨らむことになりPRIMARY KEYます。

を使用したインデックスの(person_id)場合、このようなクエリを実行すると

SELECT * FROM yourtable WHERE person_id = 127 ORDER BY pet_id;

PRIMARY KEY完全にこのクエリに従事し、その結果を順生成されるだろうPRIMARY KEY、とにかく。物理的な観点から、行は挿入順に並べられます。pet_idがAUTO_INCREMENTの場合は、自動番号順になります。


1
Afaik InnoDBは、PK列がすでに存在する場合、2回目にPK列を追加することで、インデックスを「膨らませ」ません。これを使用して、複数列キーのPK列の異なる順序を指定することも(owner_id, pet_id)できます。PK があるが、キー(vet_id, pet_id[, owner_id])を作成して異なる列順序を利用できる場合。
jkavalik

2

ヒント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でなければならない最初の列いくつかの指標。そのルールは、「PRIMARYまたはUNIQUE列のみ」については何も述べていないことに注意してください。

ヒントはx、他のものと一緒にフェッチされることが多い巨大なテーブルに役立ちます。

ヒント2:あるとします

SELECT name FROM tbl WHERE person_id = 12 AND pet_id = 34;

これは「カバーする」インデックスです。

INDEX(person_id, pet_id, name)

つまり、クエリ全体をインデックスのBTree内で実行できます。EXPLAINには「インデックスを使用しています」と表示されます。

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