MySQL中的短,单字段索引或巨大的覆盖索引

时间:2014-01-29 22:41:50

标签: mysql indexing query-optimization myisam

我试图准确理解多字段索引中的确切内容和用途。我已阅读this existing question(以及更多)以及其他网站/资源(MySQL性能博客,Percona slidehares等),但我并不完全相信我在该主题上发现的内容是最新且准确的。因此,请在我重复一些我认为我知道的事情时请耐心等待。

  • 通过明智地建立索引,我不仅可以减少匹配查询条件所需的时间,还可以减少在查询结果中获取所需字段所需的时间。

  • 索引只是完整数据的有序重复子集,与指针(MyISAM)或PKs(InnoDB)配对,我可以比全表更有效地搜索。

  • 鉴于上述情况,使用索引来匹配我的条件实际上就像获取我想要的结果一样,除了我创建了这个特殊目的表(索引),它得到了我的中间结果设置得很快;使用这个中间结果集,我可以比执行全表扫描更有效地检索我最终所需的结果集。

  • 此外,如果索引覆盖了我的查询中的所有字段(不仅仅是条件),而不是中间结果集,索引将为我提供所需的一切,而无需从整个表中获取任何行

  • InnoDB表聚集在PK上,因此具有连续PK的行可能存储在同一个块中(每个块给定多行),并且我可以相当有效地获取具有连续PK的行范围。

  • 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 /代理ID添加到每个较小的索引是否有任何好处,希望查询优化器能够处理某种索引合并魔术?

如果这是一个InnoDB索引,那么PK已经存在,但是因为它是MyISAM,所以它有指向完整行的指针。因此,如果我正确理解事物,那么将PK添加到任何其他索引是没有意义的(没有双关语),除非这样做允许直接从索引中检索所需的结果集。这不太可能。

我理解,如果我觉得我很难进行优化,也许我就是这样,但我需要使用这个数据库执行的任务需要花费几周时间,所以每一点都有帮助。

1 个答案:

答案 0 :(得分:1)

你必须理解一个概念。索引(InnoDB或MyiSAM,ether Primary或​​secondary)是一种称为" B + tree"的数据结构。

B +树中的每个节点都是一对(k,v),其中k是键,v是值。如果你在last_name上建立索引,你的密钥将是" Smith"," Johnson"," Kuzminsky"等。

索引中的值是一些数据。如果索引是辅助索引,则数据是主键值。

因此,如果您在last_name上构建索引,则每个节点都是一对(last_name,id),例如: (" Smith",5)。

主索引是一个索引,其中k是主键,数据是所有其他字段。

请记住以上内容让我评论一些观点:

  

通过明智地建立索引,我不仅可以减少匹配查询条件所需的时间,还可以减少在查询结果中获取所需字段所需的时间。

不完全是。如果您的二级索引是好的,您可以根据查询条件快速找到v。例如。你可以通过姓氏快速找到PK。

  

索引只是完整数据的有序重复子集,与指针(MyISAM)或PK(InnoDB)配对,我可以比完整表更有效地搜索。

索引是B +树,其中每个节点都是一对索引字段值和PK。

  

鉴于上述情况,使用索引来匹配我的条件实际上就像获取我想要的结果一样,除了我创建了这个专用表(索引),它让我很快得到了一个中间结果集;使用这个中间结果集,我可以比执行全表扫描更有效地检索我最终所需的结果集。

不完全是。如果没有索引,则您必须扫描整个表并仅选择last_name =" Smith"的记录。但你有索引(last_name,PK),所以有关键" Smith"你可以快速找到所有PK,其中last_name =" Smith"。然后你可以快速找到你的完整结果(因为你不仅需要姓氏,还需要名字)。所以你是对的,查询如SELECT * FROM table WHERE last_name =" Smith"分两步执行:

  1. 查找所有PK
  2. 通过PK查找完整记录。
  3.   

    此外,如果索引覆盖了我的查询中的所有字段(不仅仅是条件),而不是中间结果集,索引将为我提供所需的一切,而无需从整个表中获取任何行。

    完全。如果您的索引实际上是(last_name,first_name,id)并且您的查询是SELECT first_name WHERE last_name =" Smith"你不做第二步。您在辅助索引中有第一个名称,因此您不必转到主索引。

      

    InnoDB表聚集在PK上,因此具有连续PK的行可能存储在同一个块中(每个块有很多行),并且我可以相当有效地获取连续PK的行范围。

    右。两个邻居PK值很可能在同一页面中。好吧,除了一个PK是页面中的最后一个值并且下一个PK值存储在下一页中的情况。 基本上,这就是B +树结构发明的原因。它不仅对搜索有效,而且在顺序访问方面也很有效。直到最近我们还有旋转硬盘。

      

    MyISAM表未集群;有一些隐藏的内部行排序与PK(或任何索引)没有固定的关系,所以每当我想要获取一组行时,我可能必须为每一行检索不同的块 - 即使这些行有连续的PK。

    右。如果将新记录插入MyISAM表,则无论PK顺序如何,记录都将添加到MYD文件的末尾。 MyISAM表的主索引将是B +树,其中包含指向MYD文件中记录的指针。

    现在谈谈你的特殊问题。我没有看到任何理由来定义UNIQUE INDEX uq_owner_complete。

      

    在坚持使用短的单字段索引(可能的前缀)和扩展每个索引以覆盖整个表之间是否有任何合理的中间立场?

    最好的是在WHERE子句中使用的所有列上都有二级索引,除了低选择性字段(如性)。最具选择性的字段必须在索引中排在第一位。例如(last_name,eye_color)是好的。 (eye_color,last_name)很糟糕。 如果覆盖索引允许避免额外的PK查找,那就非常好。但如果不是那样的话也可以接受。

      

    后者与在磁盘上存储整个数据集五次有什么不同,但每次都有不同的排序?

    是。

      

    将PK /代理ID添加到每个较小的索引是否有任何好处,希望查询优化器能够使用某种索引合并魔法?

    PK已经是索引的一部分了。(请记住,它已经存储为数据。)因此,将PK字段显式添加到二级索引是没有意义的。我认为(但不确定)MyISAM二级索引也存储PK值(主索引存储指针)。

    总结:

    • 尽可能缩短你的PK(代理PK效果很好)
    • 根据需要添加任意数量的索引,直到写入性能变得不可接受为止。