我最近读到关于 innodb 表,在 (something,primary_key) 上放置索引是多余的,因为主键自动与所有二级索引聚集在一起。 因此,为了减小索引大小,我复制了我的表,删除了冗余主键,并进行了一些测试查询,我发现它的行为与具有“冗余”主键的原始表不同。
Explain 告诉我它正在做相交:
Using intersect(idx_faver_idx_id,PRIMARY);
下面是查询。如果我删除 "AND Favorite
.id
< 25103182" 然后它按预期工作并使用正确的索引 (idx_faver_idx_id)。
SELECT `Item`.`id`, `Item`.`cached_image`, `Item`.`submitter_id`, `Item`.`source_title`, `Item`.`source_url`, `Item`.`source_image`, `Item`.`nudity`, `Item`.`tags`, `Item`.`width`, `Item`.`height`, `Item`.`tumblr_id`, `Item`.`tumblr_reblog_key`, `Item`.`fave_count`, `Favorite`.`id`, `Favorite`.`created`
FROM `favorites2` AS `Favorite`
LEFT JOIN `items` AS `Item`
ON (`Favorite`.`notice_id` = `Item`.`id`)
WHERE `faver_profile_id` = 1
AND `Favorite`.`removed` = 0
AND `Item`.`removed` = '0'
AND `Favorite`.`id` < 25103182
ORDER BY `Favorite`.`id` desc
LIMIT 26
最佳答案
InnoDB 二级索引叶节点包含主键值,但如果要对 ID 值进行范围查询,则需要非叶索引的节点以包含主键值。
如果您只在选择列表中选择 ID,则将主键添加到索引定义中是多余的。例如:
CREATE TABLE Favorite (
id INT AUTO_INCREMENT PRIMARY KEY,
something INT,
KEY s (something),
KEY s_with_id (something, ID)
);
任一索引都会使以下查询成为仅索引查询。 InnoDB 更喜欢更紧凑的索引 s
。它仍然可以是仅索引查询,因为索引的叶节点提供了 ID 值。
mysql> EXPLAIN SELECT something, ID FROM Favorite WHERE something = 1\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: Favorite
type: ref
possible_keys: s,s_with_id
key: s
key_len: 5
ref: const
rows: 48
Extra: Using where; Using index
但是如果您在 ID 上也有不等式或范围条件,那么在非叶节点中也包含 ID 值的索引会带来更多好处。它可以利用 ID 值在 B 树中排序的事实。
mysql> EXPLAIN SELECT something, ID FROM Favorite WHERE something = 1 and id < 10 \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: Favorite
type: ref
possible_keys: PRIMARY,s,s_with_id
key: s_with_id
key_len: 9
ref: const
rows: 1
Extra: Using where; Using index
PS:在描述复合索引时请不要使用术语“聚集”,因为聚集与索引的含义不同。聚簇索引改变表数据的存储以匹配索引的顺序。 InnoDB 主键始终是聚集索引,因为数据行存储在主键索引的叶节点中。
关于您的评论:请记住,针对主索引的“范围”查询可能优于针对二级索引的“ref”查询。
当您的查询使用二级索引时,它基本上必须对每行进行 两次 树遍历:首先搜索二级索引以到达找到主键值的叶节点,然后其次使用该主键值搜索主(聚集)索引以获取其余列。
对于您的查询而言,针对主索引执行范围查询的总体成本可能更低,因此它会找到足够小的行子集,然后将您的其他条件应用于它找到的列。它没有使用二级索引,但它仍然是一个胜利,因为它只需要对每行进行一次树遍历。
我说“可能是”不是为了使用狡猾的话,而是因为更好的选择实际上取决于有多少行与任一条件匹配。通常优化器非常擅长进行这种评估,因此没有必要使用 FORCE INDEX 来覆盖其行为。
关于mysql - innodb 聚集二级索引和范围查询的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8190467/