这是EverSQL上两篇文章的整合,分别是:
Choosing the best indexes for MySQL query optimization
https://www.eversql.com/choosing-the-best-indexes-for-mysql-query-optimization/
How to find unused indexes in a MySQL database?
https://www.eversql.com/how-to-find-unused-indexes-in-a-mysql-database/
我们应该为SQL查询创建哪些索引?
一般来说,MySQL只能为查询中的每个表使用一个索引。因此为每个查询创建多于一个的索引是没有意义的。同一个索引最好可以适用于尽量多个查询,这样可以减少数据增删改时数据库的压力。
- 最重要的是WHERE和JOIN子句中的相等条件。大部分情况向下,像name = ‘John’这种条件,可以使数据库过滤掉大部分行,只扫描一小部分数据并返回结果。因此我们应该在这些列上加索引。
- WHERE子句中的范围条件,但只能在其中一个上加索引,因为MySQL不能同时使用那么多。
- 如果查询没有范围条件,可以添加 GROUP BY 子句中的列。
- 如果查询没有 GROUP BY 子句,也可以创建一个包含ORDER BY子句列的索引。
- 可以创建一个独立的索引来保存 ORDER BY 子句的列,但索引需要包含 ORDER BY 子句中的所有列,它们应该全部在 ORDER BY 子句中用相同的顺序(ASC / DESC)指定。但这不代表数据库优化器一定会使用这个索引,但值得一试。
- 如果索引不是太大(一般5-7个就算大了),也可以将SELECT字句中的列加入索引。创建覆盖索引允许数据库不仅使用索引进行过滤,而且还可以直接从索引中获取SELECT子句所需的信息,从而节省了宝贵的I/O操作。
看一个例子:
SELECT
id, first_name, last_name, age
FROM
employees
WHERE
first_name = 'John'
AND last_name = 'Brack'
AND age > 25
ORDER BY age ASC;
在这个查询中,首先我们会添加等于判断中的first_name和last_name列。然后添加范围判断中的age列。不需要添加ORDER BY子句中的列,因为age列已经在索引中了。然后将SELECT子句中的id添加到索引,使之更完备。
所以根据查询特性,我们应该添加索引:
employees (first_name, last_name, age, id)
添加索引或者写SQL时不要做什么?
下面整理了常见的查询错误和索引添加错误。
- 在一个表中分别为不同列添加单独的索引。
大部分时候,MySQL不会在一个查询中为一个表使用多个索引。因此在一个表中创建单独的索引,数据库只能使用索引执行其中一个搜索操作,其余的将显着减慢,因为数据库不能使用索引来执行它们。我们建议使用复合索引而非单一索引。 - 过滤条件中的OR操作。
大部分时候,MySQL无法在OR条件中使用索引,所以上面的查询无法使用索引执行。因此我们建议避免使用OR条件,而是先分成两部分查询,再使用UNION DISTINCT(如果两部分查询没有重复项,那么使用UNION ALL会更好)结合起来。SELECT a, b FROM tbl WHERE a = 3 OR b = 8;
- 索引中列的顺序很重要
比如我的手机通讯录中按名字排列,需要统计有多少名叫John的人,那么只需要将页面导航到包含以John开头的地方,并从那里开始计数。如果通讯录按姓排序,此时再统计叫John的人,那就困难许多了。数据库也在这种情况下摸不着头脑。
下面看一个SQL查询,模拟MySQL优化器的行为:
这里索引contacts (first_name, last_name)是比较理想的,以为这个索引第一个是过滤条件中的列,并且包含查询子句中的列。但是索引contacts (last_name, first_name)则没什么用处,因为我们的过滤条件是索引中的第二列而不是第一列。这个例子说明索引中列的顺序非常重要。SELECT first_name, last_name FROM contacts WHERE first_name = 'John';
- 添加冗余索引
在尝试优化SQL查询时,索引非常出色,可以显着提高性能。但其实索引也会影响效率。因为每个索引都需要在数据库发生变化时进行更新和同步。数据库每执行一次增删改的操作,所有相关索引都要更新。这会耗费一定的时间,对于大型表或者索引则更甚。因此不要创建不确定是否需要的索引。强烈建议偶尔分析一下数据库,查找一下冗余的索引并将其移除。
如何查找无用的索引?
从MySQL 5.6开始,数据库会保存索引的使用数据作为PERFORMANCE SCHEMA的一部分。视图schema_unused_indexes中可以查到没被使用过的索引。但这里面的数据是从MySQL重启后开始记录的,所以需要MySQL运行一段时间再检索比较靠谱。
运行:
select * from sys.schema_unused_indexes;
结果:
object_schema | object_name | index_name |
---|---|---|
(N/A) | (N/A) | (N/A) |
删除索引:
DROP INDEX `index_name` on table