Answers:
否。如果您的表具有InnoDB引擎且PRIMARY KEY
is为(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)
注意文件排序!
现在,具有复合索引的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)
也没有文件排序!即使索引没有显式包含该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%的确定,我还运行了后两种情况(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
(<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
的索引排序可能会更快已经完全正常了。
INCLUDE (columns)
功能。这是我得出的结论(s_c)
等于的另一个原因(s_c, pk)
。
根据有关簇索引和二级索引的MySQL文档
二级索引如何与聚簇索引相关
除聚集索引以外的所有索引都称为辅助索引。在InnoDB中,二级索引中的每个记录都包含该行的主键列以及为二级索引指定的列。InnoDB使用此主键值在聚集索引中搜索行。
如果主键较长,则辅助索引将使用更多空间,因此具有短主键是有利的。
因此,将PRIMARY KEY添加到二级索引绝对是多余的。您的索引条目想要(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,则按自动编号排序。
(owner_id, pet_id)
但可以创建键(vet_id, pet_id[, owner_id])
以利用不同的列顺序时。
秘诀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会说“正在使用索引”。
person_id
不是唯一的,记录是按物理方式(person_id, pet_id)
还是按逻辑排序person_id
?”