【问题标题】:wierd MySql join behavior奇怪的 MySql 加入行为
【发布时间】:2014-12-30 17:54:11
【问题描述】:

我有一个带有 4 个表的 MySql 数据库(实际上远不止这些,但只有这 4 个与问题相关),我们称它们为 A、B、C 和 D。这是架构:

CREATE TABLE A
(
  pKey INT NOT NULL AUTO_INCREMENT,
  name NVARCHAR(50),
  PRIMARY KEY(pKey),
  UNIQUE INDEX(name)
);

CREATE TABLE C
(
  pKey INT NOT NULL AUTO_INCREMENT,
  PRIMARY KEY(pKey)
);

CREATE TABLE B
(
  pKey INT NOT NULL AUTO_INCREMENT,
  aKey INT NOT NULL,
  cKey INT NOT NULL,
  PRIMARY KEY(pKey),
  UNIQUE INDEX UniqueKey (aKey, cKey),
  FOREIGN KEY(aKey) REFERENCES A(pKey),
  FOREIGN KEY(cKey) REFERENCES C(pKey)
);

CREATE TABLE D
(
  pKey INT NOT NULL AUTO_INCREMENT,
  cKey INT NOT NULL,
  PRIMARY KEY(pKey),
  INDEX(cKey),
  FOREIGN KEY(cKey) REFERENCES C(pKey)
);

我正在运行以下查询:

SELECT 
    --stuff...
FROM A 
INNER JOIN B
    ON A.pKey=B.aKey
INNER JOIN C
    ON B.cKey=C.pKey
INNER JOIN D
    ON D.cKey=C.pKey
WHERE
    A.name=parameter_1;

问题是,这是一个运行在单台服务器上的大型数据库,大多数表都有 100K+ 记录,一张表打破 1000 万条记录并不少见。一张表有超过 2 亿条记录。

抛开 MySql 和体系结构的任何问题(我都被这两个问题困扰),当我在这个查询上使用解释时,上面的查询出现了一些奇怪的行为。由于这种行为,我有几个问题。我将首先展示奇怪的行为。

如果我只是在 MySql 中解释上述查询,那么我会在 EXPLAIN 输出的 ref 列中得到我期望的引用。但是,我需要将此查询作为更大查询的子查询运行。解释较大的查询为上述查询提供了类似的信息(这只是与此查询中的表相对应的较大查询的行):

+----+-------------+-------+-------+---------------------+---------+---------+-----------------+-------+--------------------------+
| id | select_type | table | type  | possible_keys       | key     | key_len | ref             | rows  | Extra                    |
+----+-------------+-------+-------+---------------------+---------+---------+-----------------+-------+--------------------------+
|  1 | SIMPLE      | A     | const | PRIMARY,key1,key2   | key1    | 38      |                 |     1 |                          |
|  1 | SIMPLE      | D     | index | key3                | key3    | 12      | NULL            | 73868 | Using index              |
|  1 | SIMPLE      | C     | index | PRIMARY,key3        | PRIMARY | 8       | NULL            |     1 |                          |
|  1 | SIMPLE      | B     | ref   | key4,UniqueKey,key6 | key4    | 12      | const,DB.D.key3 |     1 | Using where; Using index |
+----+-------------+-------+-------+---------------------+---------+---------+-----------------+-------+--------------------------+

MySql 正在执行两次索引扫描和一次 ref 类型连接。如果我使用索引提示,我可以稍微改善这一点,但只能稍微改善一点。我之前说过,这个查询是作为子查询运行的。这是其他查询的格式:

SELECT
  --stuff
FROM
(
  --sub-query1
) a
INNER JOIN
(
  --the query I have a question about
) b
ON a.c1=b.c2

优化器完全忽略表 C,转而对两个外键列 B.cKey=D.cKey 进行连接。 那么这里的问题1)为什么优化器会这样忽略表C?

接下来,即使我确实使用了索引提示,并且它忽略了表 C,它仍然会进行索引扫描以连接 B 和 D,尽管有适当的索引。 为什么?

在上面的解释中,显示表D中有73868行。在这个问题的时候实际上有73568行。正在查询的其他表之一(此问题中未显示)大约有 1 亿行,因此优化这一点非常重要。对于完整查询,rows 列的乘积约为 2.37E42。是的,我已经考虑过减少查询中表数量的方法;我需要获取的信息需要我正在访问的每个表,并且我无法更改数据库的架构。

最后,我可以在这里更改的唯一内容是查询和索引/约束。因为这是一个预先存在的系统,所以我坚持使用其他所有内容。 还有其他方法可以进一步优化此操作吗?

谢谢!

编辑:我修复了超级查询的格式。

【问题讨论】:

    标签: mysql join indexing query-optimization


    【解决方案1】:

    你可以给这个架构添加索引吗?

    如果是这样,我建议您将复合索引 (name, pKey) 添加到您的表 A。不要费心对其施加独特的约束;您已经使用其他索引处理了该问题。该复合将允许您的选择标准A.name=parameter_1 和您的联接满足一次索引扫描。

    您使用单列表 C 只是为了消除不在该表中的结果集行。我不会担心它会从 EXPLAIN 中丢失,除非您的查询存在可怕的性能问题。

    一般来说,在处理这些多路连接操作时,您应该尝试使用复合覆盖索引来提高性能。您可以阅读有关此类索引的信息。

    【讨论】:

    • 我加了你建议的索引,没有区别。表 C 实际上有比一个主键更多的列,我正在从其中一些列中选择值。由于我认为这些附加列与 JOIN 操作无关,因此我没有将它们包括在内。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-10-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多