MySQL 不使用复合主键的第一列
MySQL does not use first column of a composite primary key
我有一个大数据 table 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,
和一个较小的 table tbl2,只有一个键:
KEY `ix_log_id` (`log_id`,`affiliate_id`)
列定义:
log_id bigint(20) unsigned NOT NULL,
affiliate_id bigint(21) unsigned DEFAULT NULL,
两个 table 具有相同的引擎 (MyISAM) 和字符集。
我打算加入他们。
explain
select *
from
tbl1,
tbl2
where
tbl1.log_id = tbl2.log_id
and tbl1.affiliate_id = tbl2.affiliate_id;
看起来还不错。 tbl2 被扫描(2.5m 行)并且每一行都使用 KEY idx_log_id
与基数为 4 的 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 进行了完整的 table 扫描,并将它们与来自 tbl2 的 ix_log_id 相结合,后者远远逊色。
我正在使用 mySQL 5.1,我无法解释为什么 mySQL 无法使用复合主键的第一列进行连接。
有人帮我吗?
我在这里发现了可能的问题:
mySQL 没有为主键创建正确的统计信息。复合 PK 的第一列和第二列的基数为 NULL,只有最后一列有基数(等于 table 中的行数)。由于第一个键列没有基数,优化器无法使用此列进行连接。
这似乎是一个可重现的问题。当我在测试 table 上创建复合索引时,索引的所有列都有一个基数。对于同一列上的主键,除最后一列外的所有列的基数都是 NULL。
ANALYZE TABLE 解决了这个问题,但对于生产中的大 tables 或 tables 是不可行的。
我不确定这是我们特定版本的错误,还是 mySQL 的一般错误。我们使用的版本:
5.1.73-rel14.11-日志
(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
我有一个大数据 table 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,
和一个较小的 table tbl2,只有一个键:
KEY `ix_log_id` (`log_id`,`affiliate_id`)
列定义:
log_id bigint(20) unsigned NOT NULL,
affiliate_id bigint(21) unsigned DEFAULT NULL,
两个 table 具有相同的引擎 (MyISAM) 和字符集。
我打算加入他们。
explain
select *
from
tbl1,
tbl2
where
tbl1.log_id = tbl2.log_id
and tbl1.affiliate_id = tbl2.affiliate_id;
看起来还不错。 tbl2 被扫描(2.5m 行)并且每一行都使用 KEY idx_log_id
与基数为 4 的 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 进行了完整的 table 扫描,并将它们与来自 tbl2 的 ix_log_id 相结合,后者远远逊色。
我正在使用 mySQL 5.1,我无法解释为什么 mySQL 无法使用复合主键的第一列进行连接。 有人帮我吗?
我在这里发现了可能的问题: mySQL 没有为主键创建正确的统计信息。复合 PK 的第一列和第二列的基数为 NULL,只有最后一列有基数(等于 table 中的行数)。由于第一个键列没有基数,优化器无法使用此列进行连接。
这似乎是一个可重现的问题。当我在测试 table 上创建复合索引时,索引的所有列都有一个基数。对于同一列上的主键,除最后一列外的所有列的基数都是 NULL。
ANALYZE TABLE 解决了这个问题,但对于生产中的大 tables 或 tables 是不可行的。
我不确定这是我们特定版本的错误,还是 mySQL 的一般错误。我们使用的版本: 5.1.73-rel14.11-日志 (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