【问题标题】:Particular index not used by MySQLMySQL 未使用的特定索引
【发布时间】:2012-06-22 12:19:26
【问题描述】:

我正在尝试在我的数据库中运行以下查询:

SELECT * FROM ts_cards WHERE ( cardstatus= 2 OR cardstatus= 3 ) AND ( cardtype= 1 OR cardtype= 2 ) ORDER BY cardserial DESC LIMIT 10;

所有三个字段(cardstatus、cardtype 和carderial)都被索引:

    mysql> SHOW INDEX FROM ts_cards;
    +----------+------------+----------------+--------------+-------------------+-----------+-------------+----------+--------+------+------------+---------+
    | Table    | Non_unique | Key_name       | Seq_in_index | Column_name       | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
    +----------+------------+----------------+--------------+-------------------+-----------+-------------+----------+--------+------+------------+---------+
    | ts_cards |          0 | PRIMARY        |            1 | card_id           | A         |    15000134 |     NULL | NULL   |      | BTREE      |         |
    | ts_cards |          1 | CardID         |            1 | cardserial        | A         |    15000134 |     NULL | NULL   |      | BTREE      |         |
    | ts_cards |          1 | CardType       |            1 | cardtype          | A         |          17 |     NULL | NULL   |      | BTREE      |         |
    | ts_cards |          1 | CardHolder     |            1 | cardstatusholder  | A         |          17 |     NULL | NULL   |      | BTREE      |         |
    | ts_cards |          1 | CardExpiration |            1 | cardexpiredstatus | A         |          17 |     NULL | NULL   |      | BTREE      |         |
    | ts_cards |          1 | CardStatus     |            1 | cardstatus        | A         |          17 |     NULL | NULL   |      | BTREE      |         |
    +----------+------------+----------------+--------------+-------------------+-----------+-------------+----------+--------+------+------------+---------+
    6 rows in set (0.22 sec)

(是的,我知道索引的名字很烂)

然而,默认情况下,MySQL 只使用 cardstatus 的索引:

    mysql> EXPLAIN SELECT * FROM `ts_cards` WHERE ( cardstatus= 2 OR cardstatus= 3 ) AND ( cardtype= 1 OR cardtype= 2 ) ORDER BY cardserial DESC LIMIT 10;
    +----+-------------+----------+-------+---------------------+------------+---------+------+---------+-----------------------------+
    | id | select_type | table    | type  | possible_keys       | key        | key_len | ref  | rows    | Extra                       |
    +----+-------------+----------+-------+---------------------+------------+---------+------+---------+-----------------------------+
    |  1 | SIMPLE      | ts_cards | range | CardType,CardStatus | CardStatus | 1       | NULL | 3215967 | Using where; Using filesort |
    +----+-------------+----------+-------+---------------------+------------+---------+------+---------+-----------------------------+
    1 row in set (0.00 sec)

(它甚至不考虑carderial上的索引,但我想这是另一个问题。)

使用“USE KEY”或“FORCE KEY”可以使其使用cardtype的索引,但不能同时使用cardtype和cardstatus:

    mysql> EXPLAIN SELECT * FROM `ts_cards` FORCE KEY (CardType) WHERE ( cardstatus= 2 OR cardstatus= 3 ) AND ( cardtype= 1 OR cardtype= 2 ) ORDER BY cardserial DESC LIMIT 10;
    +----+-------------+----------+-------+---------------+----------+---------+------+---------+-----------------------------+
    | id | select_type | table    | type  | possible_keys | key      | key_len | ref  | rows    | Extra                       |
    +----+-------------+----------+-------+---------------+----------+---------+------+---------+-----------------------------+
    |  1 | SIMPLE      | ts_cards | range | CardType      | CardType | 1       | NULL | 6084861 | Using where; Using filesort |
    +----+-------------+----------+-------+---------------+----------+---------+------+---------+-----------------------------+
    1 row in set (0.00 sec)

    mysql> EXPLAIN SELECT * FROM `ts_cards` FORCE KEY (CardType,CardStatus) WHERE ( cardstatus= 2 OR cardstatus= 3 ) AND ( cardtype= 1 OR cardtype= 2 ) ORDER BY cardserial DESC LIMIT 10;
    +----+-------------+----------+-------+---------------------+------------+---------+------+---------+-----------------------------+
    | id | select_type | table    | type  | possible_keys       | key        | key_len | ref  | rows    | Extra                       |
    +----+-------------+----------+-------+---------------------+------------+---------+------+---------+-----------------------------+
    |  1 | SIMPLE      | ts_cards | range | CardType,CardStatus | CardStatus | 1       | NULL | 3215967 | Using where; Using filesort |
    +----+-------------+----------+-------+---------------------+------------+---------+------+---------+-----------------------------+
    1 row in set (0.00 sec)

如何强制 MySQL 使用 BOTH 索引来加快查询速度? cardtype 和 cardstatus 索引的定义方式似乎相同,但 cardstatus 似乎优先于 cardtype。

【问题讨论】:

    标签: mysql indexing innodb


    【解决方案1】:

    IIRC,MySQL 不能在同一个查询中使用两个不同的索引。要使用这两个索引,MySQL 需要将它们合并为一个 (link to manual)。如果这样的合并,这里是an example(点击“查看执行计划”)。注意第一个SELECT 的“index_merge”。

    免责声明:我绝对确定上述信息。

    在您的情况下,尽管有您的提示,优化器仍然认为直接扫描第二个表比合并索引更快(您的表可能有非常多的行,因此非常大,操作成本高索引)。

    我建议:

    ALTER TABLE ADD INDEX CardTypeStatus (cardtype, cardstatus);
    

    这会在两列上创建一个索引。您的查询可能能够使用此索引。您可能想在之后删除您的 CardType 索引:查询仍然可以使用两列索引,即使他们仅搜索 cardtype 列(但如果他们仅搜索 cardstatus )。

    更多关于多列索引的信息:http://dev.mysql.com/doc/refman/5.5/en/multiple-column-indexes.html

    【讨论】:

      猜你喜欢
      • 2012-12-03
      • 2015-08-29
      • 1970-01-01
      • 1970-01-01
      • 2016-07-29
      • 2011-06-03
      • 1970-01-01
      • 2012-08-24
      • 2012-08-06
      相关资源
      最近更新 更多