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

Rob*_*Rob 6 mysql database indexing database-design

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

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

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
);
Run Code Online (Sandbox Code Playgroud)

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

ALTER TABLE widgets ADD INDEX widget_name_index(widget_name);
Run Code Online (Sandbox Code Playgroud)

现在,在一个查询中,例如:

SELECT w.* FROM widgets AS w ORDER BY w.widget_name ASC
Run Code Online (Sandbox Code Playgroud)

widget_name_index是用来排序的结果集.

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

SELECT w.* FROM widgets AS w 
WHERE w.widget_price > 100.00 
ORDER BY w.widget_name ASC
Run Code Online (Sandbox Code Playgroud)

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

ALTER TABLE widgets ADD INDEX widget_price_index(widget_price);
Run Code Online (Sandbox Code Playgroud)

但是,它会使用两个索引吗?据我了解,它不会......

ALTER TABLE widgets ADD INDEX widget_price_name_index(widget_price, widget_name);
Run Code Online (Sandbox Code Playgroud)

现在widget_price_name_index将用于选择和订购记录.但是,如果我想转过头来做这件事怎么办:

SELECT w.* FROM widgets AS w 
WHERE w.widget_name LIKE '%foobar%'
ORDER BY w.widget_price ASC
Run Code Online (Sandbox Code Playgroud)

widget_price_name_index用于此?或者我widget_name_price_index也需要一个吗?

ALTER TABLE widgets ADD INDEX widget_name_price_index(widget_name, widget_price);
Run Code Online (Sandbox Code Playgroud)

现在,如果我有一个搜索的搜索框widget_name,widget_part_numberwidget_description

ALTER TABLE widgets
ADD INDEX widget_search(widget_name, widget_part_number, widget_description);
Run Code Online (Sandbox Code Playgroud)

如果最终用户可以按任何列排序怎么办?我很容易看到如何只用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
Run Code Online (Sandbox Code Playgroud)

我假设这将使用widget_id_index和连接的widgets.widget_id主键索引,但排序呢?这将同时使用widget_name_indexspecial_title_index

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

Nir*_*mal 5

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

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

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