索引列仍然需要8秒,不使用索引(mysql 5)

时间:2012-06-17 22:43:29

标签: mysql indexing

以下是查询的结果,该查询花了将近8秒,并且有可用的索引但未使用。只有40,000条记录。

mysql> SELECT * FROM (`phppos_customers`) 
   JOIN `phppos_people` ON `phppos_customers`.`person_id`=`phppos_people`.`person_id`
   WHERE `deleted` = 0 ORDER BY `last_name` asc LIMIT 20
    -> ;
** PERSONAL DATA REMOVED **

mysql> EXPLAIN SELECT * FROM (`phppos_customers`) JOIN `phppos_people` ON `phppos_customers`.`person_id`=`phppos_people`.`person_id` WHERE `deleted` = 0 ORDER BY `last_name` asc LIMIT 20
    -> ;
+----+-------------+------------------+--------+-------------------+---------+---------+--------------------------------+-------+---------------------------------+
| id | select_type | table            | type   | possible_keys     | key     | key_len | ref                            | rows  | Extra                           |
+----+-------------+------------------+--------+-------------------+---------+---------+--------------------------------+-------+---------------------------------+
|  1 | SIMPLE      | phppos_customers | ref    | person_id,deleted | deleted | 4       | const                          | 22545 | Using temporary; Using filesort |
|  1 | SIMPLE      | phppos_people    | eq_ref | PRIMARY           | PRIMARY | 4       | pos.phppos_customers.person_id |     1 |                                 |
+----+-------------+------------------+--------+-------------------+---------+---------+--------------------------------+-------+---------------------------------+
2 rows in set (0.00 sec)

mysql> describe phppos_customers;
+----------------+--------------+------+-----+---------+-------+
| Field          | Type         | Null | Key | Default | Extra |
+----------------+--------------+------+-----+---------+-------+
| person_id      | int(10)      | NO   | MUL | NULL    |       |
| account_number | varchar(255) | YES  | UNI | NULL    |       |
| company_name   | varchar(255) | NO   |     | NULL    |       |
| taxable        | int(1)       | NO   |     | 1       |       |
| deleted        | int(1)       | NO   | MUL | 0       |       |
+----------------+--------------+------+-----+---------+-------+
5 rows in set (0.01 sec)

mysql> describe phppos_people;
+--------------+--------------+------+-----+---------+----------------+
| Field        | Type         | Null | Key | Default | Extra          |
+--------------+--------------+------+-----+---------+----------------+
| first_name   | varchar(255) | NO   | MUL | NULL    |                |
| last_name    | varchar(255) | NO   | MUL | NULL    |                |
| phone_number | varchar(255) | NO   |     | NULL    |                |
| email        | varchar(255) | NO   | MUL | NULL    |                |
| address_1    | varchar(255) | NO   |     | NULL    |                |
| address_2    | varchar(255) | NO   |     | NULL    |                |
| city         | varchar(255) | NO   |     | NULL    |                |
| state        | varchar(255) | NO   |     | NULL    |                |
| zip          | varchar(255) | NO   |     | NULL    |                |
| country      | varchar(255) | NO   |     | NULL    |                |
| comments     | text         | NO   |     | NULL    |                |
| person_id    | int(10)      | NO   | PRI | NULL    | auto_increment |
+--------------+--------------+------+-----+---------+----------------+
12 rows in set (0.00 sec)

编辑:show create table phppos_customers;

| phppos_customers | CREATE TABLE `phppos_customers` (
  `person_id` int(10) NOT NULL,
  `account_number` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `company_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `taxable` int(1) NOT NULL DEFAULT '1',
  `deleted` int(1) NOT NULL DEFAULT '0',
  UNIQUE KEY `account_number` (`account_number`),
  KEY `person_id` (`person_id`),
  KEY `deleted` (`deleted`),
  CONSTRAINT `phppos_customers_ibfk_1` FOREIGN KEY (`person_id`) REFERENCES `phppos_people` (`person_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci |

编辑:show create table phppos_people;

| phppos_people | CREATE TABLE `phppos_people` (
  `first_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `last_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `phone_number` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `email` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `address_1` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `address_2` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `city` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `state` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `zip` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `country` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `comments` text COLLATE utf8_unicode_ci NOT NULL,
  `person_id` int(10) NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`person_id`),
  KEY `first_name` (`first_name`),
  KEY `last_name` (`last_name`),
  KEY `email` (`email`)
) ENGINE=InnoDB AUTO_INCREMENT=45870 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci |

编辑显示指数:

mysql> show index from phppos_customers;
+------------------+------------+----------------+--------------+----------------+-----------+-------------+----------+--------+------+------------+---------+
| Table            | Non_unique | Key_name       | Seq_in_index | Column_name    | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+------------------+------------+----------------+--------------+----------------+-----------+-------------+----------+--------+------+------------+---------+
| phppos_customers |          0 | account_number |            1 | account_number | A         |           2 |     NULL | NULL   | YES  | BTREE      |         |
| phppos_customers |          1 | person_id      |            1 | person_id      | A         |       46217 |     NULL | NULL   |      | BTREE      |         |
| phppos_customers |          1 | deleted        |            1 | deleted        | A         |           2 |     NULL | NULL   |      | BTREE      |         |
+------------------+------------+----------------+--------------+----------------+-----------+-------------+----------+--------+------+------------+---------+
3 rows in set (0.00 sec)

mysql> show index from phppos_people;
+---------------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table         | Non_unique | Key_name   | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+---------------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| phppos_people |          0 | PRIMARY    |            1 | person_id   | A         |       45699 |     NULL | NULL   |      | BTREE      |         |
| phppos_people |          1 | first_name |            1 | first_name  | A         |       45699 |     NULL | NULL   |      | BTREE      |         |
| phppos_people |          1 | last_name  |            1 | last_name   | A         |       45699 |     NULL | NULL   |      | BTREE      |         |
| phppos_people |          1 | email      |            1 | email       | A         |       45699 |     NULL | NULL   |      | BTREE      |         |
+---------------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
4 rows in set (0.04 sec)

1 个答案:

答案 0 :(得分:1)

查看解释输出,这就是mysql现在所做的事情:

  1. phppos_customers表开始,使用deleted上的索引查找deleted = 0
  2. 对于每一行(其中22k),请phppos_people查找相同person_id(加入)的信息。
  3. 将所有内容放在临时表中,按last_name排序,不带任何索引,以帮助排序,因为它是已经连接的结果集,并且必须对所有行进行排序才能找到前20个。
  4. 这看起来不像是运行此查询的最佳方式,由于某些原因,优化器选择了错误的连接顺序。

    尝试使用STRAIGHT_JOIN强制执行不同的连接顺序:

    SELECT * FROM `phppos_people` STRAIGHT_JOIN `phppos_customers` 
      ON `phppos_customers`.`person_id`=`phppos_people`.`person_id` 
    WHERE `deleted` = 0 
    ORDER BY `last_name` ASC LIMIT 20;
    

    这次mysql应该:

    1. phppos_people开头,使用该列的索引按last_name排序。
    2. 每行加入phppos_customers并检查是否deleted = 0
    3. 使用限制优化并在首先找到20之后停止。