mysql中没有使用复合索引

时间:2013-01-19 18:23:09

标签: mysql indexing composite-key composite-primary-key

根据MySQL文档,如果最左边的字段是条件的一部分,仍将使用复合索引。但是,此表将无法与主键正确连接;我必须添加左侧两个字段的另一个索引然后使用。

其中一个表是内存,我知道默认情况下内存使用的哈希索引不能用于组/顺序。但是我使用内存表的所有行而不是索引,所以我认为这与问题无关。

我错过了什么?

mysql> show create table pr_temp;
| pr_temp | CREATE TEMPORARY TABLE `pr_temp` (
`player_id` int(10) unsigned NOT NULL,
`insert_date` date NOT NULL,
[...]
PRIMARY KEY (`player_id`,`insert_date`) USING BTREE,
KEY `insert_date` (`insert_date`)
) ENGINE=MEMORY DEFAULT CHARSET=utf8  |

mysql> show create table player_game_record;
| player_tank_record | CREATE TABLE `player_game_record` (
`player_id` int(10) unsigned NOT NULL,
`game_id` smallint(5) unsigned NOT NULL,
`insert_date` date NOT NULL,
[...]
PRIMARY KEY (`player_id`,`insert_date`,`game_id`),
KEY `insert_date` (`insert_date`),
KEY `player_date` (`player_id`,`insert_date`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 DATA DIRECTORY='...' INDEX DIRECTORY='...' |

mysql> explain select pgr.* from player_game_record pgr inner join pr_temp on   pgr.player_id = pr_temp.player_id and pgr.insert_date = pr_temp.date_prev;                      
+----+-------------+---------+------+---------------------------------+-------------+---------+-------------------------------------------------------------------------+--------+-------+
| id | select_type | table   | type | possible_keys                   | key         | key_len | ref                                                                     | rows       | Extra |
+----+-------------+---------+------+---------------------------------+-------------+---------+-------------------------------------------------------------------------+--------+-------+
|  1 | SIMPLE      | pr_temp | ALL  | PRIMARY                         | NULL        | NULL    | NULL                                                                    | 174683   |       |
|  1 | SIMPLE      | pgr     | ref  | PRIMARY,insert_date,player_date | player_date | 7       | test_gamedb.pr_temp.player_id,test_gamedb.pr_temp.date_prev |     21 |       |
+----+-------------+---------+------+---------------------------------+-------------+---------+-------------------------------------------------------------------------+--------+-------+
2 rows in set (0.00 sec)

mysql> explain select pgr.* from player_game_record pgr force index (primary) inner join pr_temp on pgr.player_id = pr_temp.player_id and pgr.insert_date = pr_temp.date_prev;
+----+-------------+---------+------+---------------+---------+---------+-------------------------------------------------------------------------+---------+-------+
| id | select_type | table   | type | possible_keys | key     | key_len | ref                                                                       | rows    | Extra |
+----+-------------+---------+------+---------------+---------+---------+-------------------------------------------------------------------------+---------+-------+
|  1 | SIMPLE      | pr_temp | ALL  | PRIMARY       | NULL    | NULL    | NULL                                                                    |  174683 |       |
|  1 | SIMPLE      | pgr     | ref  | PRIMARY       | PRIMARY | 7       | test_gamedb.pr_temp.player_id,test_gamedb.pr_temp.date_prev | 2873031 |       |
+----+-------------+---------+------+---------------+---------+---------+-------------------------------------------------------------------------+---------+-------+
2 rows in set (0.00 sec)

我认为主键应该有效,使用了两个左列(player_id,insert_date)。但是默认情况下它会使用player_date索引,如果我强制它使用主索引,它看起来只使用一个字段而不是两个字段。

Update2:Mysql版本5.5.27-log UPDATE3: (注意这是在尝试其他测试时删除了player_date索引之后)

mysql> show indexes in player_game_record;
+--------------------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table              | Non_unique | Key_name    | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+--------------------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| player_game_record |          0 | PRIMARY     |            1 | player_id   | A            |        NULL |     NULL | NULL   |      | BTREE      |         |               |
| player_game_record |          0 | PRIMARY     |            2 | insert_date | A         |        NULL |     NULL | NULL   |      | BTREE      |         |               |
| player_game_record |          0 | PRIMARY     |            3 | game_id     | A         |   576276246 |     NULL | NULL   |      | BTREE      |         |               |
| player_game_record |          1 | insert_date |            1 | insert_date | A         |       33304 |     NULL | NULL   |      | BTREE      |         |               |
+--------------------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
4 rows in set (1.08 sec)


mysql> select count(*) from player_game_record;
+-----------+
| count(*)  |
+-----------+
| 576276246 |
+-----------+
1 row in set (0.00 sec)

1 个答案:

答案 0 :(得分:1)

我同意你在其中一个表中使用MEMORY存储引擎根本不应该成为一个问题,因为我们正在谈论另一个表。

我也同意索引的最左前缀可以准确地用于你如何使用它,我想不出为什么主键不能以与任何其他索引完全相同的方式使用的任何原因。

这令人头疼。您创建的新索引“应该”与主键的左侧相同,那么为什么它们的行为方式不一样呢?我有两个想法,这两个想法都引导我提出同样的建议,尽管我对MyISAM的内部结构并不像InnoDB那样熟悉。 (顺便说一句,我推荐InnoDB而不是MyISAM。)

当你开始插入数据时,主键上的索引可能就在桌面上,而新的索引是在大部分或全部数据已经存在的情况下添加的。这表明您的新索引在内部很好且组织得很干净,而您的主键索引可能是高度分散的,已经在加载数据时构建。

优化程序显示的行数基于索引统计信息,由于插入顺序,主键上可能不准确。

碎片理论可以解释为什么用主键查询,因为你的索引不是那么快;索引统计理论可以解释为什么优化器提出了如此不同的行计数它可以解释为什么优化器可能选择了全表扫描而不是使用该索引(这只是一个猜测) ,因为我们没有可用的解释。)

我建议根据这两个想法在你的桌子上运行OPTIMIZE TABLE。如果构建新索引需要12个小时,那么optimizing the table可能会花费很长时间或更长时间。

可能有帮助:http://www.dbasquare.com/2012/07/09/data-fragmentation-problem-in-mysql-myisam/