1

根据 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 version 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)
4

1 回答 1

1

我同意您MEMORY对其中一张表的存储引擎的使用在这里根本不是问题,因为我们正在谈论另一张表。

我也同意索引的最左边前缀可以完全按照您尝试使用它的方式使用,并且我想不出为什么不能以与任何其他索引完全相同的方式使用主键的任何原因。

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

当您开始插入数据时,主键上的索引可能在表上,而新索引是在大部分或所有数据已经​​存在时添加的。这表明您的新索引很好并且内部组织干净,而您的主键索引可能是高度碎片化的,是在加载数据时构建的。

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

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

基于这两个想法,我建议的事情正在OPTIMIZE TABLE你的桌子上运行。如果构建新索引需要 12 个小时,那么优化表很可能需要那么长或更长的时间。

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

于 2013-01-20T07:54:27.653 回答