数据库调优(一)

开篇

在上一篇文章中,我们有一个表,里面的内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
mysql> select * from org_copy;
+--------+-----------------+---------------+----------+
| org_id | org_name | org_parent_id | org_copy |
+--------+-----------------+---------------+----------+
| 1 | 一级部门 | 0 | 1 |
| 2 | 一一级部门 | 0 | 2 |
| 3 | 1.1级部门 | 1 | 3 |
| 4 | 1.2级部门 | 1 | 4 |
| 5 | 1.1.1部门 | 3 | 5 |
| 6 | 1.1.2部门 | 3 | 6 |
| 7 | 1.1.1.1部门 | 5 | 7 |
| 8 | 1.3部门 | 1 | 8 |
| 9 | 1.2.1部门 | 4 | 9 |
+--------+-----------------+---------------+----------+
9 rows in set (0.00 sec)

这应该是一个很基本的一个mysql表,同时我们在上一篇文章中,也执行了如下SQL。

1
2
3
4
5
6
7
8
mysql> explain SELECT * FROM org_copy WHERE org_name>'一级部门' and  org_parent_id=1;
+----+-------------+----------+------------+-------+---------------+------------+---------+------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------+------------+-------+---------------+------------+---------+------+------+----------+-----------------------+
| 1 | SIMPLE | org_copy | NULL | range | index_name | index_name | 768 | NULL | 1 | 11.11 | Using index condition |
+----+-------------+----------+------------+-------+---------------+------------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)

那么如果此时我们换一个SQL来进行查询呢?

1
2
3
4
5
6
7
mysql> explain SELECT * FROM org_copy WHERE org_name>'1' and  org_parent_id=1;
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | org_copy | NULL | ALL | index_name | NULL | NULL | NULL | 9 | 11.11 | Using where |
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

此时是不是会一脸疑惑…为啥我的这个SQL,明明一样呀,但是为啥第一个就可以走索引呢???
因为在mysql里面,会判断当前where条件查询的数据量,由于索引是由一个B+树的形式存在,所以当通过org_name来进行比较筛选的时候,是可以很快的定位出大致需要查询的数据量。

而当数据量大于30%的时候,mysql就会采用一种全表扫描的方式来进行查询,这也就是为什么不建议在区分度低的字段上建立索引了,假设一个字段只有三个值或者两个值,那么极有可能mysql会直接通过全表扫描的方式进行查询。

索引覆盖

这个是mysql调优中经常忽略的一点,由于mysql在建立索引的时候会一次性将索引字段存入到索引树中,所以如果我们的where条件中所包含的字段均可以在索引中找到的话,那么mysql就会直接从索引中去取数据,而不会进行回表。例如如下SQL:

1
2
3
4
5
6
7
8
mysql> explain  SELECT org_parent_id FROM org_copy WHERE org_copy=1;
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | org_copy | NULL | ALL | NULL | NULL | NULL | NULL | 4325 | 10.00 | Using where |
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

由于org_copy字段非索引,所以此时只能是通过回表的方式进行查询。

总结

  1. 由于mysql会在数据量大于30%的时候进行全表扫描,所以最好就是不要在区分度低的字段上建立索引,避免进行了全表扫描。
  2. 对于过滤字段的使用,应该是尽最大的努力让SQL采用索引覆盖,如果无法避免的话就尽量让最左原则生效。进而加快查询速度。
作者

Somersames

发布于

2019-06-09

更新于

2021-12-05

许可协议

评论