MySQL 优化 - 大表连接

Posted

技术标签:

【中文标题】MySQL 优化 - 大表连接【英文标题】:MySQL optimization - large table joins 【发布时间】:2012-05-15 15:57:07 【问题描述】:

首先是所涉及表格的简化版本。

tbl_map 大约有 4,000,000 行,tbl_1 大约有 120 行,tbl_2 大约有 5,000,000 行。鉴于 Google、Yahoo! 等使用更大的数据集,我知道数据不应考虑那么大。所以我只是假设我错过了一些东西。

    CREATE TABLE `tbl_map` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT,
      `tbl_1_id` bigint(20) DEFAULT '-1',
      `tbl_2_id` bigint(20) DEFAULT '-1',
      `rating` decimal(3,3) DEFAULT NULL,
      PRIMARY KEY (`id`),
      KEY `tbl_1_id` (`tbl_1_id`),
      KEY `tbl_2_id` (`tbl_2_id`)
    ) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

    CREATE TABLE `tbl_1` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT,
      PRIMARY KEY (`id`)
   ) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

    CREATE TABLE `tbl_2` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT,
      `data` varchar(255) NOT NULL DEFAULT '',
      PRIMARY KEY (`id`),
    ) ENGINE=InnoDB  DEFAULT CHARSET=utf8;

感兴趣的查询:也可以用ORDERY BY t.id DESC 代替ORDERY BY t.id DESC。该查询需要 5~10 秒,并且在用户查看此页面时会导致相当长的等待时间。

EXPLAIN SELECT t.data, t.id , tm.rating
FROM tbl_2 AS t
JOIN tbl_map AS tm 
ON t.id = tm.tbl_2_id
WHERE tm.tbl_1_id =94
AND tm.rating IS NOT NULL
ORDER BY t.id DESC
LIMIT 200 

1   SIMPLE  tm  ref     tbl_1_id, tbl_2_id  tbl_1_id    9   const   703438  Using where; Using temporary; Using filesort
1   SIMPLE  t   eq_ref  PRIMARY     PRIMARY     8   tm.tbl_2_id     1 

我只想加快查询速度,确保我有正确的索引等。 我感谢 DB 大师的任何建议!谢谢。

【问题讨论】:

【参考方案1】:

SUGGESTION : 索引表如下:

ALTER TABLE tbl_map ADD INDEX (tbl_1_id,rating,tbl_2_id);

【讨论】:

感谢您的快速回答 :) 我会试一试,让您知道它是如何工作的。很高兴收到一位真正的 DBA 的来信! 索引绝对是一个巨大的帮助。但是,我现在对 SQL 语句的重构不返回任何内容。我会继续看看这是否只是我的一个错误。 它将留下评级为 NULL 的连接条目。实际上,添加您提到的索引后,我的原始查询和 DRapp 工作正常。 :) tm 子查询明确表示AND rating IS NOT NULL。当且仅当t.id = tm.tbl_2_id 对给定行失败时,tm 子查询才会产生 NULL 评级。 据我了解,第一个选择从tbl_2 中提取 lass 200 个条目,来自 tm 的 LEFT JOINs。因为它是LEFT JOIN,所以当第二个 Select 查询没有产生结果(即 rating IS NULL)时,NULL 将作为 tm.rating 返回。【参考方案2】:

按照 Rolando 的说法,是的,您肯定需要在地图表上建立索引,但我会扩展到还包括 tbl_2_id,它用于表 2 的 ID 的 ORDER BY 子句(与地图在同一个表中,所以只需使用该索引。此外,由于索引现在包含所有 3 个字段,并且基于关键字搜索的 ID 和评级为空(或不)的标准,因此第三个元素已经为您的 ORDER BY 子句提供了它们.

INDEX (tbl_1_id,rating, tbl_2_id);

然后,我将查询作为

SELECT STRAIGHT_JOIN 
      t.data, 
      t.id , 
      tm.rating
   FROM 
      tbl_map tm
         join tbl_2 t
            on tm.tbl_2_id = t.id
   WHERE 
          tm.tbl_1_id = 94
      AND tm.rating IS NOT NULL
   ORDER BY 
      tm.tbl_2_id DESC
   LIMIT 200 

【讨论】:

在添加 Rolando 提到的 INDEX 后,此查询从 60 秒变为 1.2 秒。那很好。我预先解释了它,它也显示出更好的结果。谢谢 @KennyCason,在索引上还可以,但是我的版本显然是如何获得赞誉的……IT 的解释和整体性能与 1.2 相比如何。那么哪个结果更好...... 我主要排除了您的查询,因为查询有效,但是是的,鉴于最终 Rolando 首先提到了 INDEXing,我可以给予他信任。至于查询性能,在对行进行索引后,我们的查询运行大致相同。谢谢。

以上是关于MySQL 优化 - 大表连接的主要内容,如果未能解决你的问题,请参考以下文章

在 3 个大表上使用内连接优化 SQL 查询

MySQL表连接之驱动表与被驱动表

如何在大表上优化这个 mysql 连接?

了解MySQL联表查询中的驱动表,优化查询,以小表驱动大表

MySQL查询在大表上很慢

mysql中两个大表之间的连接查询