MySQL中的全表掃描案例

  • 2019 年 11 月 6 日
  • 筆記

MySQL中的全表掃描案例

這兩天看到了兩種可能會導致全表掃描的sql,這裡給大家看一下,希望可以避免踩坑:

情況1:

強制類型轉換的情況下,不會使用索引,會走全表掃描。

舉例如下:

首先我們創建一個表

 CREATE TABLE `test` (    `id` int(11) NOT NULL AUTO_INCREMENT,    `age` int(11) DEFAULT NULL,    `score` varchar(20) NOT NULL DEFAULT '',    PRIMARY KEY (`id`),    KEY `idx_score` (`score`)  ) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8

我們可以看到,這個表有三個欄位,其中兩個int類型,一個varchar類型。varchar類型的欄位score是一個索引,而id是主鍵。

然後我們給這個表裡面插入一些數據,插入數據之後的表如下:

mysql:yeyztest 21:43:12>>select * from test;  +----+------+-------+  | id | age  | score |  +----+------+-------+  |  1 |    1 | 5     |  |  2 |    2 | 10    |  |  5 |    5 | 25    |  |  8 |    8 | 40    |  |  9 |    2 | 45    |  | 10 |    5 | 50    |  | 11 |    8 | 55    |  +----+------+-------+  7 rows in set (0.00 sec)

這個時候,我們使用explain語句來查看兩條sql的執行情況,分別是:

explain select * from test where score ='10';

explain select * from test where score =10;

結果如下:

mysql:yeyztest 21:42:29>>explain select * from test where score ='10';  +----+-------------+-------+------------+------+---------------+-----------+---------+-------+------+----------+-------+  | id | select_type | table | partitions | type | possible_keys | key       | key_len | ref   | rows | filtered | Extra |  +----+-------------+-------+------------+------+---------------+-----------+---------+-------+------+----------+-------+  |  1 | SIMPLE      | test  | NULL       | ref  | idx_score     | idx_score | 62      | const |    1 |   100.00 | NULL  |  +----+-------------+-------+------------+------+---------------+-----------+---------+-------+------+----------+-------+  1 row in set, 1 warning (0.00 sec)    mysql:yeyztest 21:43:06>>explain select * from test where score =10;  +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+  | id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |  +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+  |  1 | SIMPLE      | test  | NULL       | ALL  | idx_score     | NULL | NULL    | NULL |    7 |    14.29 | Using where |  +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+  1 row in set, 3 warnings (0.00 sec)

可以看到,如果我們使用的是varchar類型的值,那麼結果中掃描的行數rows就是1,而當我們使用的是整數值10的時候,掃描行數變為了7,證明,如果出現了強制類型轉換,則會導致索引失效。

情況2:

反向查詢不能使用索引,會導致全表掃描。

創建一個表test1,它的主鍵是score,然後插入6條數據:

CREATE TABLE `test1` (    `score` varchar(20) not null default '' ,    PRIMARY KEY (`score`)  ) ENGINE=InnoDB DEFAULT CHARSET=utf8    mysql:yeyztest 22:09:37>>select * from test1;  +-------+  | score |  +-------+  | 111   |  | 222   |  | 333   |  | 444   |  | 555   |  | 666   |  +-------+  6 rows in set (0.00 sec)

當我們使用反向查找的時候,不會使用到索引,來看下面兩條sql:

explain select * from test1 where score='111';

explain select * from test1 where score!='111';

mysql:yeyztest 22:13:01>>explain select * from test1 where score='111';  +----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+  | id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra       |  +----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+  |  1 | SIMPLE      | test1 | NULL       | const | PRIMARY       | PRIMARY | 62      | const |    1 |   100.00 | Using index |  +----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+  1 row in set, 1 warning (0.00 sec)    mysql:yeyztest 22:13:08>>explain select * from test1 where score!='111';  +----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+  | id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref  | rows | filtered | Extra                    |  +----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+  |  1 | SIMPLE      | test1 | NULL       | index | PRIMARY       | PRIMARY | 62      | NULL |    6 |   100.00 | Using where; Using index |  +----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+  1 row in set, 1 warning (0.00 sec)

可以看到,使用!=作為條件的時候,掃描的行數是表的總記錄行數。因此如果想要使用索引,我們就不能使用反向匹配規則。

情況3:

某些or值條件可能導致全表掃描。

首先我們創建一個表,並插入幾條數據:

CREATE TABLE `test4` (    `id` int(11) DEFAULT NULL,    `name` varchar(20) DEFAULT NULL,    KEY `idx_id` (`id`)  ) ENGINE=InnoDB DEFAULT CHARSET=utf8  1 row in set (0.00 sec)    [email protected]:yeyztest 22:23:44>>select * from test4;  +------+------+  | id   | name |  +------+------+  |    1 | aaa  |  |    2 | bbb  |  |    3 | ccc  |  |    4 | yeyz |  | NULL | yeyz |  +------+------+  5 rows in set (0.00 sec)

其中表test4包含兩個欄位,id欄位是一個索引,而name欄位是varchar類型,我們來看下面三個語句的掃描行數:

explain select * from test4 where id=1;

explain select * from test4 where id is null;

explain select * from test4 where id=1 or id is null;

mysql:yeyztest 22:24:12>>explain select * from test4 where id is null;  +----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-----------------------+  | id | select_type | table | partitions | type | possible_keys | key    | key_len | ref   | rows | filtered | Extra                 |  +----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-----------------------+  |  1 | SIMPLE      | test4 | NULL       | ref  | idx_id        | idx_id | 5       | const |    1 |   100.00 | Using index condition |  +----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-----------------------+  1 row in set, 1 warning (0.00 sec)    mysql:yeyztest 22:24:17>>explain select * from test4 where id=1;  +----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------+  | id | select_type | table | partitions | type | possible_keys | key    | key_len | ref   | rows | filtered | Extra |  +----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------+  |  1 | SIMPLE      | test4 | NULL       | ref  | idx_id        | idx_id | 5       | const |    1 |   100.00 | NULL  |  +----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------+  1 row in set, 1 warning (0.00 sec)    mysql:yeyztest 22:24:28>>explain select * from test4 where id=1 or id is null;  +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+  | id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |  +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+  |  1 | SIMPLE      | test4 | NULL       | ALL  | idx_id        | NULL | NULL    | NULL |    5 |    40.00 | Using where |  +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+  1 row in set, 1 warning (0.00 sec)

可以看到單獨使用id=1和id is null,都只會掃描一行記錄,而使用or將二者連接起來就會導致掃描全表而不使用索引。

簡單總結一下:

1.強制類型轉換的情況下,不會使用索引,會走全表掃描

2.反向查詢不能使用索引,會導致全表掃描。

3.某些or值條件可能導致全表掃描。