我有一个大数据表 tbl1,其中包含以下键:
PRIMARY KEY (`log_id`,`entry_id`,`position`),
KEY `idx_log_id` (`log_id`)
列定义:
log_id bigint(20) unsigned NOT NULL,
entry_id mediumint(8) unsigned NOT NULL,
position tinyint(3) unsigned NOT NULL,
affiliate_id mediumint(8) unsigned NOT NULL,
还有一个较小的表 tbl2,只有一个键:
KEY `ix_log_id` (`log_id`,`affiliate_id`)
列定义:
log_id bigint(20) unsigned NOT NULL,
affiliate_id bigint(21) unsigned DEFAULT NULL,
两个表具有相同的引擎 (MyISAM) 和字符集。
我打算加入他们。
explain
select *
from
tbl1,
tbl2
where
tbl1.log_id = tbl2.log_id
and tbl1.affiliate_id = tbl2.affiliate_id;
看起来还不错。扫描 tbl2(250 万行),并使用基数为 4 的 KEY idx_log_id 将每一行与 tbl1 连接。太棒了。
现在我认为:KEY idx_log_id
索引与主键第一列相同的列。所以应该没有必要。事实上,EXPLAIN 显示 PRIMARY,idx_log_id 作为可能的键。
现在我知道了
explain
select *
from
tbl1 ignore key (idx_log_id),
tbl2
where
tbl1.log_id = tbl2.log_id
and tbl1.affiliate_id = tbl2.affiliate_id;
并且忽略 idx_log_id 我希望 mysql 使用主键。
但是没有。它没有使用 tbl1 中的键,而是对超过 40 亿行的 tbl1 进行全表扫描,并将它们与远远逊色的 tbl2 中的 ix_log_id 连接起来。
我正在使用 mySQL 5.1,但我无法解释为什么 mySQL 无法使用复合主键的第一列进行连接。 有人可以帮助我吗?
最佳答案
我在这里发现了可能的问题: mySQL 没有为主键创建正确的统计信息。复合 PK 的第一列和第二列的基数为 NULL,只有最后一列具有基数(等于表中的行数)。由于第一个键列没有基数,优化器无法使用该列进行连接。
这似乎是一个可重现的问题。当我在测试表上创建复合索引时,索引的所有列都有基数。对于同一列上的主键,除最后一列之外的所有列的基数均为 NULL。
分析表解决了这个问题,但对于大表或生产中的表不可行。
我不确定这是否是我们特定版本的错误,或者是 mySQL 的普遍错误。我们使用的版本: 5.1.73-rel14.11-log (Percona 服务器(GPL),14.11,修订版 603)
分析后的关键统计数据:
Table Non_unique Key_name Seq_in_index Column_name Cardinality
tbl1 0 PRIMARY 1 log_id 840106431
tbl1 0 PRIMARY 2 entry_id 840106431
tbl1 0 PRIMARY 3 position 4200532155
tbl1 1 idx_log_id 1 log_id 840106431
之前,它看起来像这样:
分析前的关键统计数据:
Table Non_unique Key_name Seq_in_index Column_name Cardinality
tbl1 0 PRIMARY 1 log_id <null>
tbl1 0 PRIMARY 2 entry_id <null>
tbl1 0 PRIMARY 3 position 4200532155
tbl1 1 idx_log_id 1 log_id 840106431
关于MySQL 不使用复合主键的第一列,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28875029/