mysql - MySQL中的短,单字段索引或大量覆盖索引

标签 mysql indexing query-optimization myisam

我正试图准确地理解在多字段索引中什么是有用的,什么不是有用的。我已经阅读了this existing question(以及更多)以及其他站点/资源(MySQL Performance Blog、Percona slideshares等),但我并不完全相信我在这个主题上的发现是最新和准确的。所以请容忍我重复我认为我知道的一些事情。
通过明智地索引,我不仅可以减少匹配查询条件所需的时间,还可以减少在查询结果中获取字段所需的时间。
索引只是完整数据的一个已排序、重复的子集,与指针(MyISAM)或PKs(InnoDB)配对,我可以比完整表更有效地搜索它们。
鉴于上述情况,使用索引来匹配我的条件实际上与获取所需结果的方式相同,只是我创建了一个特殊用途的表(索引),它可以非常快速地获取中间结果集;有了这个中间结果集,我可以比执行完整的表扫描更有效地检索我最终想要的结果集。
此外,如果索引覆盖了查询中的所有字段(不仅仅是条件),而不是中间结果集,那么索引将提供所需的所有信息,而无需从完整表中获取任何行。
InnoDB表聚集在PK上,因此具有连续PKs的行很可能存储在同一个块中(每个块有许多行),我可以相当高效地获取具有连续PKs的一系列行。
MyISAM表不是聚集的;有一些隐藏的内部行排序与PK(或任何索引)没有固定关系,因此每当我想要获取一组行时,可能必须为每一行检索不同的块,即使这些行具有连续的PK。
假设以上至少是准确的,这是我的困惑。我有一个缓慢变化的维度表,定义了以下列(或多或少),并使用MyISAM:

dim_owner_ID INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
person_ID INT UNSIGNED NOT NULL,
raw_name VARCHAR(92) NOT NULL,
first VARCHAR(30),
middle VARCHAR(50),
last VARCHAR(30),
suffix CHAR(3),
flag CHAR(1)

每个“所有者”都是具有特定名称的特定个人的唯一实例,因此,如果Sue Smith将其名称更改为Sue Brown,则会导致两行相同,但last字段和代理项键除外。我的理解是,在内部强制执行此约束的唯一方法是:
UNIQUE INDEX uq_owner_complete (person_ID, raw_name, first, middle, last, suffix, flag)

这基本上会复制整个表(除了代理项键)。
我还需要为其他一些字段编制索引,以便快速连接和搜索。虽然会有一些写操作,而且磁盘空间既不是空闲的,也不是无限的,但在这里,读取性能绝对是第一位的。这些较小的索引应该能够很好地覆盖将针对表运行的查询的条件,但是在几乎所有情况下,都需要选择整行。
考虑到这一点:
在坚持使用短的单字段索引(在可能的情况下使用前缀)和扩展每个索引以覆盖整个表之间是否存在合理的中间点?
后者与将整个数据集存储在磁盘上五次有什么不同,但每次排序都不同?
将PK/surrogate ID添加到每个较小的索引中,希望查询优化器能够发挥某种索引合并的魔力,这有什么好处吗?
如果这是一个InnoDB索引,PK就已经存在了,但是因为它是MyISAM,所以它有指向整行的指针。所以,如果我理解正确,就没有必要(没有双关语)将PK添加到任何其他索引中,除非这样做可以直接从索引中检索所需的结果集。这在这里是不可能的。
我明白,如果我看起来太努力去优化,也许我是,但我需要执行的任务使用这个数据库一次需要几个星期,所以每一点点帮助。

最佳答案

你必须理解一个概念。索引(InnoDB或MyiSAM、ether Primary或secondary)是一种称为“B+树”的数据结构。
B+树中的每个节点都是一对(k,v),其中k是一个键,v是一个值。如果你用姓建立索引,你的密钥将是“史密斯”,“约翰逊”,“库兹明斯基”等。
索引中的值是一些数据。如果索引是辅助索引,则数据是主键值。
因此,如果在姓氏上建立索引,每个节点将是一对(姓氏,id),例如(“Smith”,5)。
主索引是一个索引,其中k是主键,数据是所有其他字段。
考虑到上述情况,我想谈几点:
通过明智地索引,我不仅可以减少匹配查询条件所需的时间,还可以减少在查询结果中获取字段所需的时间。
不完全是。如果你的二级索引是好的,你可以根据你的查询条件快速找到v。你可以很快找到姓PK的人。
索引只是完整数据的一个已排序、重复的子集,与指针(MyISAM)或PKs(InnoDB)配对,我可以比完整表更有效地搜索它们。
索引是B+树,其中每个节点是一对索引字段值和PK。
鉴于上述情况,使用索引来匹配我的条件实际上与获取所需结果的方式相同,只是我创建了一个特殊用途的表(索引),它可以非常快速地获取中间结果集;有了这个中间结果集,我可以比执行完整的表扫描更有效地检索我最终想要的结果集。
不完全是。如果没有索引,则必须扫描整个表并只选择最后一个“Smith”位置的记录。但是您有索引(last_name,PK),所以有了密钥“Smith”,您可以快速找到last_name=“Smith”所在的所有PK。然后你可以很快找到你的完整结果(因为你不仅需要姓氏,还需要名字)。所以您说得对,像SELECT*FROM table这样的查询在last_name=“Smith”中分两步执行:
查找所有主键
通过PK查找完整记录。
此外,如果索引覆盖了查询中的所有字段(不仅仅是条件),而不是中间结果集,那么索引将提供所需的所有信息,而无需从完整表中获取任何行。
确切地。如果索引实际上是(last_name,first_name,id),并且查询是选择first_name WHERE last_name=“Smith”,则不执行第二步。在辅助索引中有名字,所以不必转到主索引。
InnoDB表聚集在PK上,因此具有连续PKs的行很可能存储在同一个块中(每个块有许多行),我可以相当高效地获取具有连续PKs的一系列行。
正确的。两个相邻的PK值很可能在同一页中。好吧,除了一个PK是一个页面中的最后一个值,而下一个PK值存储在下一个页面中的情况。
基本上,这就是为什么B+树结构被发明的原因。它不仅对搜索有效,而且对顺序访问也有效。直到最近我们还拥有旋转硬盘。
MyISAM表不是聚集的;有一些隐藏的内部行排序与PK(或任何索引)没有固定关系,因此每当我想要获取一组行时,可能必须为每一行检索不同的块,即使这些行具有连续的PK。
正确的。如果在MyISAM表中插入新记录,则记录将添加到MYD文件的末尾,而不考虑主键顺序。
MyISAM表的主索引将是B+树,其中包含指向MYD文件中记录的指针。
现在谈谈你的特殊问题。我不认为有任何理由定义唯一索引uq_owner_complete。
在坚持使用短的单字段索引(在可能的情况下使用前缀)和扩展每个索引以覆盖整个表之间是否存在合理的中间点?
最好是在WHERE子句中使用的所有列上都有二级索引,除了低选择性字段(如sex)。在索引中,选择最多的字段必须排在第一位。例如(姓氏,眼睛颜色)是好的。(眼睛颜色,姓氏)不好。
如果覆盖索引允许避免额外的PK查找,那就太好了。但如果不是,那也是可以接受的。
后者与将整个数据集存储在磁盘上五次有什么不同,但每次排序都不同?
对。
将PK/surrogate ID添加到每个较小的索引中,希望查询优化器能够发挥某种索引合并的魔力,这有什么好处吗?
PK已经是索引的一部分了(记住,它是作为数据存储的)所以,显式地向二级索引添加PK字段是没有意义的。我认为(但不确定)MyISAM二级索引也存储PK值(而一级索引确实存储指针)。
总结一下:
使你的PK尽可能短(代理PK很好用)
添加所需数量的索引,直到写入性能变得不可接受为止。

关于mysql - MySQL中的短,单字段索引或大量覆盖索引,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21444252/

相关文章:

java - 创建 2 个表 - ORA-00904 : : invalid identifier

PHP:SQL 查询数组的最佳方法是什么? (如果你可以的话)

sql - 需要帮助理解 JOIN 查询与带有子选择的查询的 SQL 解释

r - 按时间和 id r 传播字符列

mysql - 什么更快 : to call a user-declareed function within a query or to set the value by trigger?

mysql - 性能改进 : Left outer Join, Order By, Subquery: Index not picked by the main query

php - 更新 MySql、PHP 时将值从一个表插入到另一个表

mysql - 我的 mysql 查询出错了,尽管看起来完全没问题

indexing - Elasticsearch : How to list each analyzer used by a specific index

sql - 向表中添加大量索引是否有不利之处?