Apa yang dilakukan dengan kunci utama sebagai kolom terakhir dalam indeks sekunder gabungan dalam tabel InnoDB lakukan?

8

Katakanlah saya memiliki hubungan 1-ke-N (person_id, pet_id). Saya punya tabel di mana pet_idkunci utama.

Saya mengerti bahwa indeks sekunder InnoDB pada dasarnya adalah B-tree di mana nilainya adalah nilai kunci primer yang sesuai untuk baris tersebut.

Sekarang, misalkan satu orang dapat memiliki ribuan hewan peliharaan dan saya sering menginginkan hewan peliharaan seseorang pet_id. Maka akan menjadi masalah jika catatan dalam indeks sekunder diurutkan berdasarkan (person_id, pet_id)atau hanya person_iddengan pet_id's untuk itu person_idtidak disortir. Menebak nanti.

Jadi, jika person_idtidak unik, apakah rekaman secara fisik diurutkan berdasarkan (person_id, pet_id)atau HANYA pet_id?

Terima kasih

pengguna3391564
sumber
1
Saya kira pertanyaan terakhir sebenarnya adalah: "Jadi, jika person_idtidak unik, apakah catatan secara fisik disortir oleh (person_id, pet_id)atau HANYA person_id?"
ypercubeᵀᴹ

Jawaban:

7

Tidak. Jika tabel Anda memiliki mesin InnoDB dan PRIMARY KEYitu (pet_id), maka mendefinisikan indeks sekunder sebagai (person_id)atau (person_id, pet_id)tidak ada bedanya.

Indeks juga menyertakan pet_idkolom sehingga nilai diurutkan seperti (person_id, pet_id)dalam kedua kasus.

Kueri seperti yang Anda miliki:

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

Anda hanya perlu mengakses indeks untuk mendapatkan nilai dan bahkan lebih, itu tidak perlu melakukan apa pun, karena pet_idnilai sudah diurutkan dalam indeks. Anda dapat memverifikasi ini dengan melihat rencana eksekusi ( EXPLAIN):


Pertama, kami mencoba dengan tabel 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)

Perhatikan filesort!

Sekarang, MyISAM dengan indeks komposit:

 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 hilang , seperti yang diharapkan.


Sekarang mari kita coba hal yang sama dengan mesin 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)

Juga tidak ada filesort! Meskipun indeks tidak secara eksplisit memiliki pet_idkolom, nilainya ada di sana dan diurutkan. Anda dapat memeriksa bahwa jika Anda mendefinisikan indeks dengan (person_id, pet_id), EXPLAINidentik.

Mari kita benar-benar melakukannya, dengan InnoDB dan indeks komposit:

 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)

Rencana identik dengan kasus sebelumnya.


Agar 100% yakin, saya juga menjalankan 2 case terakhir (mesin InnoDB, dengan indeks tunggal dan komposit) memungkinkan file_per_tablepengaturan dan menambahkan beberapa ribu baris dalam tabel:

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

Dalam kedua kasus, memeriksa ukuran file aktual, menghasilkan hasil yang identik :

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
ypercubeᵀᴹ
sumber
1
Dengan asumsi InnoDB bekerja sama dalam hal ini untuk MS SQL Server, ada adalah perbedaan antara indeks pada (<some_column>)dan (<some_column>, <pk>)karena ON (<some_column>)setara dengan ON (<some_column>) INCLUDE (<pk>)dan tidak ON (<some_column>, <pk>). Dalam kebanyakan situasi ini memiliki signifikansi nol, tetapi jika PK Anda acak (yaitu UUID) maka ON (<s_c>,<pk>)dapat menyebabkan fragmentasi tambahan atau jika PK Anda bermakna selain menjadi kunci dan Anda mungkin ORDER BY s_c, pkkemudian jenis seperti itu akan lebih cepat sebagai indeks sudah dalam urutan penuh.
David Spillett
@ DavidSpillett Benar. MySQL tidak memiliki INCLUDE (columns)fungsi. Itu alasan lain saya menyimpulkan bahwa (s_c)indeks ini setara dengan (s_c, pk).
ypercubeᵀᴹ
Saya tidak dapat menemukan dokumentasi untuk mendukung saya (jadi saya mungkin salah ingat) tapi saya cukup yakin saya pernah membaca bahwa InnoDB tidak menyimpan PK dalam urutan stabil dalam indeks sekunder kecuali diminta. Meskipun perbedaannya kecil. Ketika selanjutnya saya punya waktu untuk bermain dengan mySQL saya harus menguji teorinya ...
David Spillett
@DavidSpillett - blog.jcole.us/2013/01/10/… bagian indeks sekunder - "Ada satu hal yang perlu diperhatikan untuk halaman indeks non-daun indeks sekunder: bidang kunci berkerumun (PKV) dimasukkan dalam catatan dan dianggap sebagai bagian dari kunci rekaman, bukan nilainya. " jadi itu memerintahkan mereka paling tidak pada tingkat halaman. Tidak yakin persis bagaimana itu di dalam satu halaman dari deskripsi itu, tetapi bahkan jika tidak, itu hanya diselesaikan dengan buffer kecil - baca PK dari satu halaman, urutkan (maks ~ 500 item) dan ambil pesanan jadi mungkin tidak relevan.
jkavalik
2

Menurut Dokumentasi MySQL tentang Indeks Clustered dan Secondary

Bagaimana Indeks Sekunder Berhubungan dengan Indeks Berkelompok

Semua indeks selain indeks berkerumun dikenal sebagai indeks sekunder. Di InnoDB, setiap catatan dalam indeks sekunder berisi kolom kunci utama untuk baris, serta kolom yang ditentukan untuk indeks sekunder . InnoDB menggunakan nilai kunci utama ini untuk mencari baris dalam indeks berkerumun.

Jika kunci primer panjang, indeks sekunder menggunakan lebih banyak ruang, sehingga menguntungkan untuk memiliki kunci primer pendek.

Oleh karena itu, menambahkan KUNCI UTAMA ke indeks sekunder jelas berlebihan. Entri indeks Anda ingin (person_id, pet_id, pet_id). Ini juga akan mengasapi indeks sekunder dengan memiliki 2 salinan PRIMARY KEY.

Untuk indeks dengan (person_id), jika Anda menjalankan kueri seperti ini

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

The PRIMARY KEYakan terlibat penuh dalam query ini dan menghasilkan hasil diperintahkan oleh PRIMARY KEYpula. Dari sudut pandang fisik, baris dipesan dengan urutan penyisipan. Jika pet_id adalah AUTO_INCREMENT, maka itu dipesan berdasarkan nomor otomatis.

RolandoMySQLDBA
sumber
1
Afaik InnoDB tidak akan "mengasapi" indeks dengan menambahkan kolom PK untuk kedua kalinya ketika sudah ada. Anda bahkan dapat menggunakannya untuk menentukan urutan berbeda dari kolom PK untuk kunci multikolom: ketika Anda memiliki PK (owner_id, pet_id)tetapi Anda dapat membuat kunci (vet_id, pet_id[, owner_id])untuk memanfaatkan urutan kolom yang berbeda.
jkavalik
2

Kiat 1:

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

sangat valid. Ini memiliki keunggulan kinerja menjadi lebih efisien ketika banyak permintaan perlu menemukan beberapa baris WHERE x = 123. Artinya, itu sedikit lebih efisien daripada yang 'jelas'

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

Satu-satunya aturan tentang AUTO_INCREMENT(untuk InnoDB) adalah yang idharus menjadi kolom pertama dalam beberapa indeks. Perhatikan bahwa aturan yang mengatakan apa-apa tentang PRIMARYatau UNIQUEatau 'hanya kolom'.

Tip berguna untuk meja besar yang sering diambil xbersama dengan barang-barang lainnya.

Tips 2: Misalkan Anda punya

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

Ini adalah indeks "meliputi":

INDEX(person_id, pet_id, name)

Artinya, seluruh permintaan dapat dilakukan di dalam indeks BTree. EXPLAIN akan mengatakan "Menggunakan indeks".

Rick James
sumber