MySQL 不使用复合主键的第一列

标签 mysql join primary-key composite-primary-key

我有一个大数据表 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/

相关文章:

php - 审核 MySQL 表并仅在批准后显示更改

mysql - 棘手的 MySQL ORDER BY 问题

mysql - 在经过净化的输入存储过程中使用准备好的语句有什么意义吗?

c# - Linq、模拟联接和 Include 方法

sql - 更改主键

mysql - 如何更新 MySQL 中的主键?

mysql - 在有限的情况下使用 REGEXP 将空格更改为连字符

MySQL查询来自2张表的困惑

php - MySQL 使用 php 连接两个表

c# - 重置主键