【问题标题】:MySQL indexes and when to group themMySQL 索引以及何时对它们进行分组
【发布时间】:2012-05-06 21:15:57
【问题描述】:

我仍在努力寻找在 MySQL 中使用 INDEXES 的最佳方法。您如何知道何时将它们合并在一起以及何时将它们分开?

以下是 Wordpress 帖子表中的索引。看看 post_name、post_parent 和 post_author 是如何分开的?然后他们有 type_status_date 是 4 个字段的混合?

http://img215.imageshack.us/img215/5976/screenshot20120426at431.png

我不明白这背后的逻辑?谁能赐教?

【问题讨论】:

    标签: mysql


    【解决方案1】:

    答案会有点长,但我们开始吧。请注意,我不打算在这里处理数据库引擎的差异(MyISAM 和 InnoDB 有不同的方式来实现我试图描述的内容)

    关于索引,您首先要了解的是,它是存储在磁盘上的单独数据结构。通常这是一个 b-tree 数据结构,其中包含您已索引的列,还包含指向表中行的指针(此指针通常是主键)。

    与数据一起存储的唯一索引是主键索引。 因此主键索引就是表。

    假设您有以下表定义。

    CREATE  TABLE `Student` (
     `StudentNumber` INT NOT NULL ,
     `Name` VARCHAR(32) NULL ,
     `Surname` VARCHAR(32) NULL ,
     `StudentEmail` VARCHAR(32) NULL ,
     PRIMARY KEY (`StudentNumber`) );
    

    由于我们在 StudentID 上有一个主键,因此将有一个包含主键和索引中其他列的索引。如果您必须查看索引中的数据,您可能会看到类似这样的内容。

    1 , John ,Doe ,Jdoe@gmail.com
    

    如您所见,这是表数据再次向您显示主键索引是表。

    StudentNumber 列已编入索引,这使您可以有效地在其上进行搜索,其余数据与键一起存储。因此,如果运行以下查询:

    SELECT * FROM Student WHERE StudentNumber=1
    

    MySQL 将使用主索引快速查找行并读取与索引列一起存储的数据。由于有索引 MySQL 可以使用索引对 b-tree 进行有效的二叉查找操作。

    在搜索后检索数据时,MySQL 可以从索引中读取数据,因此我们在索引中使用 1 次操作来检索数据。现在,如果我运行以下查询:

    SELECT * FROM Student WHERE Name ='Joe' 
    

    MySQL 会检查是否存在可用于加快查询速度的索引。但是在我的例子中,名称上没有索引,因此 MySQL 会从第一行到最后一行一次从表中顺序读取一行。

    在每一行,它将根据 where 子句评估该行并返回匹配的行。所以基本上它从上到下读取主键索引。记住主键索引是表。

    如果我运行以下语句:

     ALTER TABLE `TimLog`.`student` 
    ADD INDEX `ix_name` (`Name` ASC) ;
     ALTER TABLE `TimLog`.`student` 
    ADD INDEX `ix_surname` (`Surname` ASC) ;
    

    MySQL 会在 Student 表上创建新索引。这将存储在磁盘上的表之外,其中的数据如下所示:

    Data in ix_Name
    John, 1 <--PRIMARY KEY VALUE
    
    Data in ix_Surname
    Doe, 1  <--PRIMARY KEY VALUE
    

    注意 ix_Name 索引中的数据是名称和主键值。太好了,所以如果我运行前面的 select 语句,MySQL 将读取 ix_name 索引并获取匹配项的主键值,然后使用主键索引来获取其余数据。

    所以从索引中获取数据的操作次数是2。在索引中找到匹配的行,然后在主键上进行查找以获取行数据。

    您现在有以下查询:

    SELECT * FROM Student WHERE Name='John' AND surname ='Doe' 
    

    这里 MySQL 不能同时使用两个索引,因为这会浪费操作。如果 MySQL 必须在此查询中使用两个索引,则会发生以下情况(这不应该发生)。

    1 Find in the ix_Name the rows with the value John
    2 Read the primary key that matches to get the row data
    3 Store the matching results
    4 Find in the ix Surname the rows with the value Doe
    5 Read the primary key that matches to get row data.
    6 Store the matching results
    7 Take the Name results and Surname results and merge them
    8 Return query results.
    

    这确实是对 IO 的浪费,因为 MySQL 会读取该表两次。基本上使用一个索引会比尝试使用两个更好(我将在 momnet 中解释原因)。 MySQL 将选择 1 个索引用于这个简单的查询。

    那么 MySQL 如何决定使用哪个索引呢?

    MySQL 在内部保留有关索引的统计信息。这些统计数据基本上告诉 MySQL 索引的唯一性。因此,为了争论,假设姓氏索引 (ix_surname) 比名称索引(ix_name) 更独特,MySQL 将使用姓氏索引 (ix_surname)。

    因此查询检索将是这样的:

    1 Use the ix_surname and find rows that match the value Doe
    2 Read the primary key and apply the filter for the value John on the actual column data in the row.
    3 Return the matched row.
    

    如您所见,此搜索中的操作数量要少得多。我已经过度简化了很多技术细节。索引是一个有趣的东西,但你必须从我如何以最少的 IO 获取数据的角度来看待它。

    希望现在像泥巴一样清澈!

    【讨论】:

      【解决方案2】:

      MySQL 通常一次不能使用多个索引。这意味着,例如,当您有一个对两个字段进行过滤或排序的查询时,您会将它们都放入同一个索引中。

      WordPress 可能有一个通用查询,可以过滤和/或排序 post_typepost_status post_date。对它们代表什么进行有根据的猜测,这可能是 WordPress 的帖子列表页面的核心查询。所以这三个字段被放入同一个索引中。

      【讨论】:

      • 真的吗? index_merge呢?
      • 我从未在EXPLAIN 语句中看到 index_merge。并且阅读文档,它不会经常或轻易发生。
      • 索引合并并不意味着它正在合并索引。索引合并方法用于检索具有多个范围扫描的行并将它们的结果合并为一个。合并可以生成其底层扫描的并集、​​交集或交集并集。此访问方法合并来自单个表的索引扫描;它不会跨多个表合并扫描。
      • 这真的很有帮助......所以基于查询,MySQL 找出最好的索引使用?如果它完全匹配,它是否只使用索引?例如,如果搜索的是 post_type、post_status 和 post_author,它是仍然使用 type_status_date INDEX 还是不使用 INDEX?
      • MySQL 将根据所请求的内容以及索引的其他统计信息来确定要使用的索引。它还将使用部分索引,因此仅对post_type 的简单搜索过滤将使用复合索引。顺便说一句,关于索引如何工作的 MySQL 文档非常好。
      猜你喜欢
      • 2014-07-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-29
      • 2020-12-10
      • 2012-09-07
      相关资源
      最近更新 更多