将主键作为 InnoDB 表中复合二级索引的最后一列有什么作用?

use*_*564 8 mysql innodb primary-key clustered-index

假设我有一个 1 对 N 的关系(person_id, pet_id)。我有一张表,pet_id主键在哪里。

我知道 InnoDB 二级索引本质上是一个 B 树,其中值是行的相应主键值。

现在,假设一个人可以拥有数千只宠物,而我通常希望一个人的宠物按pet_id. 那么,如果在第二个索引记录的排序会的问题(person_id, pet_id)或只是person_idpet_id的该person_id是无序。猜到后来。

那么,如果person_id是非唯一的,记录是按物理排序(person_id, pet_id)还是仅排序pet_id

谢谢

ype*_*eᵀᴹ 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 ;
Run Code Online (Sandbox Code Playgroud)

将只需要访问索引来获取值,甚至更多,它不需要进行任何排序,因为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)
Run Code Online (Sandbox Code Playgroud)

注意文件排序!

现在,带有复合索引的 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)
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

也没有文件排序!即使索引没有明确包含该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)
Run Code Online (Sandbox Code Playgroud)

与前一个案例相同的计划


为了 100% 确定,我还运行了最后两个案例(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
Run Code Online (Sandbox Code Playgroud)

在这两种情况下,检查实际文件大小都会产生相同的结果

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
Run Code Online (Sandbox Code Playgroud)