代码之家  ›  专栏  ›  技术社区  ›  Rob

需要澄清一下MySQL索引

  •  6
  • Rob  · 技术社区  · 16 年前

    我最近一直在考虑我的数据库索引,过去我只是不带挑衅地把它们作为一种事后考虑,如果它们是正确的,甚至是有帮助的,我从来没有认真考虑过。我读过一些相互矛盾的信息,有人说更多的索引更好,也有人说太多的索引不好,所以我希望在这里得到一些澄清和了解。

    假设我有这个假设表:

    CREATE TABLE widgets (
        widget_id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
        widget_name VARCHAR(50) NOT NULL,
        widget_part_number VARCHAR(20) NOT NULL,
        widget_price FLOAT NOT NULL,
        widget_description TEXT NOT NULL
    );
    

    我通常会为将要联接的字段和最常排序的字段添加索引:

    ALTER TABLE widgets ADD INDEX widget_name_index(widget_name);
    

    所以现在,在查询中,例如:

    SELECT w.* FROM widgets AS w ORDER BY w.widget_name ASC
    

    这个 widget_name_index 用于对结果集排序。

    现在,如果添加搜索参数:

    SELECT w.* FROM widgets AS w 
    WHERE w.widget_price > 100.00 
    ORDER BY w.widget_name ASC
    

    我想我需要一个新的索引。

    ALTER TABLE widgets ADD INDEX widget_price_index(widget_price);
    

    但是,它会同时使用这两个索引吗?据我所知,这不会……

    ALTER TABLE widgets ADD INDEX widget_price_name_index(widget_price, widget_name);
    

    现在 widget_price_name_index 将用于选择和排序记录。但是如果我想把它转过来做这个呢:

    SELECT w.* FROM widgets AS w 
    WHERE w.widget_name LIKE '%foobar%'
    ORDER BY w.widget_price ASC
    

    威尔 小部件价格名称索引 用于这个?或者我需要一个 widget_name_price_index 也?

    ALTER TABLE widgets ADD INDEX widget_name_price_index(widget_name, widget_price);
    

    如果我有一个搜索框 widget_name , widget_part_number widget_description ?

    ALTER TABLE widgets
    ADD INDEX widget_search(widget_name, widget_part_number, widget_description);
    

    如果最终用户可以按任何列排序呢?很容易看出,我怎么可能只得到5列的十几个索引。

    如果我们添加另一个表:

    CREATE TABLE specials (
        special_id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
        widget_id INT UNSIGNED NOT NULL,
        special_title VARCHAR(100) NOT NULL,
        special_discount FLOAT NOT NULL,
        special_date DATE NOT NULL
    );
    ALTER TABLE specials ADD INDEX specials_widget_id_index(widget_id);
    ALTER TABLE specials ADD INDEX special_title_index(special_title);
    
    SELECT w.widget_name, s.special_title
    FROM widgets AS w
    INNER JOIN specials AS s ON w.widget_id=s.widget_id
    ORDER BY w.widget_name ASC, s.special_title ASC
    

    我想这个会用 widget_id_index 以及 widgets.widget_id 联接的主键索引,但是排序呢?两个都用吗 小部件名称索引 special_title_index ?

    我不想漫谈太久,有无数的场景我可以理解。显然,这在实际场景中可能会变得更复杂,而不是几个简单的表。如有任何澄清,将不胜感激。

    3 回复  |  直到 15 年前
        1
  •  5
  •   Nirmal    16 年前

    根据最佳实践,您不必在定义表示意图时创建索引。在应用程序中创建查询时,最好创建索引。在大多数情况下,您将从满足查询的单列索引开始。如果要在查询中使用多个列,可以创建覆盖索引。

    覆盖索引是包含两列或多列的索引。如果索引满足查询的所有列要求,那么存储引擎可以从索引中获取所有结果,而不是启动磁盘I/O操作。因此,在创建使用更多列的查询时,可以创建覆盖所有必需列的新索引,也可以扩展现有索引以包含更多列。

    在进行上述任何一项操作时,您必须考虑一些因素。只有当索引的最左边的列可以在查询中使用时,MySQL才会考虑索引。否则,它只需查找整个表以获取结果。因此,如果您可以在不影响所有使用该索引的查询的情况下扩展现有索引,那么这将是一个明智的选择。否则,您可以继续为新查询创建一个新索引。有时,可以调整查询以适应索引结构。

        2
  •  3
  •   Mark Byers    16 年前

    索引加速选择,但减慢插入和更新。您不需要为您能想象到的每种列组合创建索引。我通常只是创建一些明显的索引,我知道我会经常使用这些索引,并且只有当我看到在进行性能度量之后需要它们时,才会添加更多的索引。即使数据库没有覆盖查询中的所有列,它仍然可以使用索引。

        3
  •  3
  •   Richard Simões    16 年前

    查询中只能使用一个索引。幸运的是,您可以创建一个覆盖多个列的索引:

    ALTER TABLE widgets ADD INDEX name_and_price_index(widget_name, widget_price);
    

    如果按小部件名称选择,将使用上述索引 小工具名称+小工具价格(但不只是小工具价格)。

    正如Mitmaro指出的,在查询中使用explain来查看MySQL必须从哪些索引中进行选择,以及最终使用什么索引。参见 here 更多细节。