【问题标题】:Composite index not used in mysqlmysql中未使用复合索引
【发布时间】:2013-01-19 18:23:09
【问题描述】:

根据 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 更新3: (注意这是在尝试其他一些测试时删除 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)

【问题讨论】:

  • “不会与主键正确连接”是什么意思?你的意思是结果是错误的,还是EXPLAIN SELECT 没有显示pgr 正在使用的主键?您能否删除新索引并发布另一个 EXPLAIN SELECT 以说明当只有主键可用时怀疑的错误行为?
  • 我的意思是,如果我强制它使用应该工作的索引,它显然不是基于解释(与 player_date 索引相比,pgr 表中的行太多) .它不使用索引,查询时间太长。如果我删除 player_date 索引,它将选择 insert_date,并且由于生成索引需要 12 多个小时,我宁愿不删除它。

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


【解决方案1】:

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

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

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

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

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

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

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

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

【讨论】:

  • 谢谢,很高兴知道我并不是完全疯了。如果我理解正确,您认为索引可能由于碎片而效率低下,但我在所有字段上使用相同的索引没有问题。我 100% 肯定,当我强制索引时,它只使用一个字段。我最近还修复了表,这与重建/优化相同,对吗?由于其他原因,我可能最终需要一个索引 (player_id, game_id),所以我可能只是交换订单并称之为完成,但我想知道实际发生了什么。
  • 您运行的 MySQL 服务器的完整版本号是多少?另外,我想说修复表和优化/分析是相似的,但看起来并不完全相同……而且 MyISAM 有几个不同级别的“修复”。你不使用 InnoDB 有什么原因吗?
  • 版本 5.5.27-log(添加到原始版本)。在初始测试期间,我反复使用 InnoDB,但它使用了大约 3 倍的磁盘,我无法判断增长率。这几乎完全是一个读取数据库(每天一批更新,其余全部读取)。批处理非常慢(7-8 小时,不是因为 DB),在这种情况下 InnoDB 的好处并不引人注目。
  • SHOW INDEXES IN player_game_record 看起来像什么?表中有多少行?
  • 添加到初始问题。
猜你喜欢
  • 2011-06-03
  • 2014-12-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-04
  • 2019-08-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多