Việc có khóa chính là cột cuối cùng trong chỉ mục phụ tổng hợp trong bảng InnoDB để làm gì?


8

Nói rằng tôi có mối quan hệ 1-N (person_id, pet_id). Tôi có một bảng trong đó pet_idlà khóa chính.

Tôi hiểu rằng một chỉ mục phụ InnoDB về cơ bản là một cây B trong đó các giá trị là các giá trị khóa chính tương ứng cho hàng.

Bây giờ, giả sử một người có thể có hàng ngàn thú cưng và tôi thường muốn có thú cưng theo thứ tự pet_id. Sau đó, sẽ có vấn đề nếu các bản ghi trong chỉ mục phụ được sắp xếp theo (person_id, pet_id)hoặc chỉ person_idvới các pet_id'không person_idđược sắp xếp. Đoán sau.

Vì vậy, nếu person_idkhông phải là duy nhất, các bản ghi được sắp xếp vật lý theo (person_id, pet_id)hoặc CHỈ pet_id?

Cảm ơn


1
Tôi cho rằng câu hỏi cuối cùng thực sự là: "Vì vậy, nếu person_idkhông phải là duy nhất, các bản ghi được sắp xếp vật lý theo (person_id, pet_id)hoặc CHỈ person_id?"
ypercubeᵀᴹ

Câu trả lời:


7

Không. Nếu bảng của bạn có công cụ InnoDB và PRIMARY KEY(pet_id), thì xác định chỉ mục phụ là (person_id)hoặc (person_id, pet_id)không có sự khác biệt.

Chỉ mục bao gồm cả pet_idcột cũng vì vậy các giá trị được sắp xếp như (person_id, pet_id)trong cả hai trường hợp.

Một truy vấn giống như truy vấn bạn có:

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

sẽ chỉ cần truy cập vào chỉ mục để nhận các giá trị và thậm chí nhiều hơn, nó sẽ không cần thực hiện bất kỳ sắp xếp nào, vì các pet_idgiá trị đã được sắp xếp trong chỉ mục. Bạn có thể xác minh điều này bằng cách xem các kế hoạch thực hiện ( EXPLAIN):


Đầu tiên, chúng tôi thử với bảng 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)

Lưu ý các tập tin!

Bây giờ, MyISAM với chỉ số tổng hợp:

 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 đã biến mất , như mong đợi.


Bây giờ, hãy thử tương tự với công cụ 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)

Không có tập tin nào cả! Mặc dù chỉ mục không rõ ràng có pet_idcột, các giá trị vẫn ở đó và được sắp xếp. Bạn có thể kiểm tra xem nếu bạn xác định chỉ mục với (person_id, pet_id), EXPLAINthì giống hệt nhau.

Hãy thực sự làm điều đó, với InnoDB và chỉ mục tổng hợp:

 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)

Kế hoạch giống hệt với trường hợp trước.


Để chắc chắn 100%, tôi cũng chạy 2 trường hợp cuối cùng (công cụ InnoDB, với các chỉ mục đơn và tổng hợp) cho phép file_per_tablecài đặt và thêm một vài nghìn hàng trong bảng:

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

Trong cả hai trường hợp, kiểm tra kích thước tệp thực tế, mang lại kết quả giống hệt nhau :

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
Giả sử InnoDB hoạt động tương tự trong lĩnh vực này để MS SQL Server, có một sự khác biệt giữa một chỉ mục trên (<some_column>)(<some_column>, <pk>)ON (<some_column>)tương đương với ON (<some_column>) INCLUDE (<pk>)và không ON (<some_column>, <pk>). Trong hầu hết các trường hợp, điều này có ý nghĩa khá lớn, nhưng nếu PK của bạn là ngẫu nhiên (nghĩa là UUID) thì ON (<s_c>,<pk>)có thể dẫn đến phân mảnh thêm hoặc nếu PK của bạn có ý nghĩa ngoài việc là khóa và bạn có thể ORDER BY s_c, pksẽ nhanh hơn khi chỉ mục đã hoàn toàn theo thứ tự.
David Spillett

@DavidSpillett Phải. MySQL không có INCLUDE (columns)chức năng mặc dù. Đó là một lý do khác mà tôi kết luận rằng (s_c)chỉ số này tương đương với (s_c, pk).
ypercubeᵀᴹ

Tôi không thể tìm thấy tài liệu để sao lưu cho tôi (vì vậy tôi có thể đang đánh giá sai) nhưng tôi khá chắc chắn rằng tôi đã đọc rằng InnoDB không giữ PK theo thứ tự ổn định trong các chỉ mục phụ trừ khi được yêu cầu. Mặc dù sự khác biệt là nhỏ dù sao. Khi tôi có thời gian chơi với myQuery, tôi sẽ phải kiểm tra lý thuyết ...
David Spillett

@DavidSpillett - blog.jcole.us/2013/01/10/... các chỉ số Secondary phần - "Có một điều đáng chú ý đối với chỉ số thứ trang không lá: các lĩnh vực then chốt clustered (PKV) có trong hồ sơ và là được coi là một phần của khóa hồ sơ, không phải giá trị của nó. " vì vậy nó ra lệnh cho chúng ít nhất là ở cấp độ trang. Không chắc chắn chính xác nó nằm trong một trang từ mô tả đó như thế nào, nhưng ngay cả khi chúng không, điều đó chỉ được giải quyết bằng một bộ đệm nhỏ - đọc PK từ một trang, sắp xếp (tối đa 500? Mục) và tìm nạp theo thứ tự để có thể không liên quan.
jkavalik

2

Theo Tài liệu MySQL về các chỉ mục cụm và chỉ số phụ

Làm thế nào các chỉ mục phụ liên quan đến chỉ số cụm

Tất cả các chỉ mục khác với chỉ mục được nhóm được gọi là chỉ mục phụ. Trong InnoDB, mỗi bản ghi trong một chỉ mục phụ chứa các cột khóa chính cho hàng, cũng như các cột được chỉ định cho chỉ mục phụ . InnoDB sử dụng giá trị khóa chính này để tìm kiếm hàng trong chỉ mục được nhóm.

Nếu khóa chính dài, các chỉ mục phụ sử dụng nhiều không gian hơn, do đó, có lợi khi có khóa chính ngắn.

Do đó, việc thêm KHÓA CHÍNH vào chỉ mục phụ chắc chắn là dư thừa. Mục nhập của bạn muốn (person_id, pet_id, pet_id). Điều này cũng sẽ không cần thiết làm tăng chỉ số phụ bằng cách có 2 bản sao của PRIMARY KEY.

Đối với chỉ mục với (person_id), nếu bạn chạy một truy vấn như thế này

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

Điều PRIMARY KEYnày sẽ được tham gia đầy đủ vào truy vấn này và tạo ra kết quả theo thứ tự PRIMARY KEYnào. Từ quan điểm vật lý, các hàng được sắp xếp theo thứ tự chèn. Nếu pet_id là AUTO_INCREMENT, thì đó là thứ tự theo số tự động.


1
Afaik InnoDB sẽ không "phình to" chỉ mục bằng cách thêm cột PK lần thứ hai khi nó đã có mặt. Thậm chí, bạn có thể sử dụng nó để chỉ định một thứ tự khác nhau của các cột PK cho khóa nhiều màu: khi bạn có PK (owner_id, pet_id)nhưng bạn có thể tạo một khóa (vet_id, pet_id[, owner_id])để sử dụng thứ tự cột khác nhau.
jkavalik

2

Mẹo 1:

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

là hoàn toàn hợp lệ. Nó có lợi thế về hiệu suất là hiệu quả hơn khi nhiều truy vấn cần tìm nhiều hàng WHERE x = 123. Đó là, nó hiệu quả hơn một chút so với 'rõ ràng'

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

Quy tắc duy nhất về AUTO_INCREMENT(đối với InnoDB) là đó idphải là cột đầu tiên trong một số chỉ mục. Lưu ý rằng quy tắc đó nói gì về PRIMARYhay UNIQUEhay 'cột chỉ'.

Mẹo này rất hữu ích cho các bảng lớn thường được tìm nạp xcùng với các thứ khác.

Mẹo 2: Giả sử bạn có

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

Đây là một chỉ số "bao phủ":

INDEX(person_id, pet_id, name)

Đó là, toàn bộ truy vấn có thể được thực hiện bên trong BTree của chỉ mục. EXPLAIN sẽ nói "Sử dụng chỉ mục".

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.