需要对MySQL索引进行一些澄清

时间:2009-11-21 03:59:21

标签: mysql database-design indexing database

我最近一直在考虑我的数据库索引,过去我只是非常挑衅地将它们作为事后的想法投入其中,并且如果它们是正确的甚至是帮助的话,从未真正考虑过。我读过相互矛盾的信息,有人说更多的索引更好,有些则索引太多,所以我希望得到一些澄清,并在这里学到一点。

假设我有这个假设的表格:

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_price_name_index会用于此吗?或者我还需要widget_name_price_index吗?

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

现在如果我有一个搜索widget_namewidget_part_numberwidget_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_indexwidgets.widget_id主键索引进行连接,但排序呢?它会同时使用widget_name_indexspecial_title_index吗?

我不想长时间徘徊,我可以想到无数的场景。显然,对于真实场景而言,这可能会变得更加复杂,而不是几个简单的表格。任何澄清将不胜感激。

3 个答案:

答案 0 :(得分:5)

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

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

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

答案 1 :(得分:3)

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

答案 2 :(得分:3)

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

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

如果您按widget_name widget_name + widget_price(但不仅仅是widget_price)进行SELECT,则将使用上述索引。

正如MitMaro所指出的那样,在查询中使用EXPLAIN来查看MySQL必须选择的索引,以及它最终使用的索引。有关更多详细信息,请参阅here