【问题标题】:How can make this join of two huge MySQL Tables finish?如何使两个巨大的 MySQL 表的连接完成?
【发布时间】:2016-10-19 10:00:17
【问题描述】:

我有两张桌子

table1:

column1: varchar(20)
column2: varchar(20)
column3: varchar(20)

table2:

column1: varchar(20)
column2: varchar(20)
column3: varchar(20) <- empty
  • column1 和column2 在table1 中都有单独的全文索引

  • 两个表都包含 2000 万行

我需要通过将table2 中的table2column1column2 中的column2 匹配来填充table2 中的column3,然后从@ 中获取column3 中的值987654332@ 并放入column3table2column1 & column2 可能不完全匹配,所以我使用的查询是:

UPDATE table1, table2 
SET table2.column3 = table1.column3 
WHERE table2.column1 LIKE table1.'%column1%' AND 
      table2.column2 LIKE table1.'%column2%';

此查询永远不会完成。我让它运行了 2 周,但仍然没有产生任何结果。它 100% 使用了一个 CPU 内核,SSD IO 很少,显然需要以某种方式进行优化。

我愿意接受有关查询优化、索引优化甚至 DBMS 优化(甚至是迁移,如果有帮助的话)的任何建议,因为我将来需要更频繁地执行此类查询。

EDIT1

有很多优化指南,请使用谷歌。您可以增加配置中的线程(InnoDB)。对于更新本身,我建议先创建一个 temp_table,然后复制到 db2

我知道这一点,但无法通过这些指南完全解决我的情况。我也知道这个问题的所有可能组合排列的问题(巨大的数据库、性能、瓶颈、查询设计)无处不在,也在 stackoverflow 上。但是,直到今天,我还想不出解决这种特定问题组合的最佳方法是什么,并希望在这里获得帮助。话虽如此: - 更多线程需要分片或分区才能使用多个 CPU 内核,如果我可以通过其他方式解决问题,我想避免这种情况 - 你建议如何在这里创建这样的临时表?

如果不使用通配符,为什么还要使用 like 运算符?将它们替换为 =。另外,您是否在每个表的 where 条件中的 3 列上有多列索引?请分享解释的输出,以及 2 个表中的任何现有索引。

  • 我在示例中省略了这些字符,但希望在基本查询工作后使用它们,很抱歉造成混淆。不过,我不完全确定如何将这些通配符放入列比较中。
  • 我有两个单独的索引,我应该创建一个 2 列索引吗? (where 条件中只有 2 列)
  • 您更喜欢我现在拥有的结构的解释,还是更喜欢具有 2 列索引的结构的解释?

我猜你说的是数据库,但你说的是表,对吧?

没错,很抱歉造成混乱。

您编写的查询将执行 20m x 20m 的查找(对于表 1 中的每一行,查找表 2 中的所有行)。如果你有一个 SSD 或一个好的 CPU,你不能写任何东西并期望它工作。如果您已经到了这一点,那么是时候在开始编写 SQL 之前进行思考了。你需要做什么,你有什么工具可以使用,中间有什么你不知道的——这些是你每次发出 4000 亿查询查询之前需要回答的问题。

这就是我所面临的情况。老实说,我不希望它像现在这样工作,所以我正在寻找可能使这成为可解决方案的指针。基本的“更新这个,匹配的地方”查询显然不适用于这里。所以我试图找出一种更高级的解决方案。非常欢迎任何批评,所以感谢您的意见。您建议如何在这里进行?

EDIT2

给我们一些样本值和不精确的比较。

table1:

+---------+---------+-------------+---------+---------+---------+
| column1 | column2 | column3     | column4 | column5 | columnN |
+---------+---------+-------------+---------+---------+---------+
| John    | Doe_    | employee001 | xyz     | 12345   | ...     |
| Jim     | Doe     | employee002 | abc     | 67890   | ...     |
+---------+---------+-------------+---------+---------+---------+

table2:

+---------+---------+---------+
| column1 | column2 | column3 |
+---------+---------+---------+
| John    | Doe     |         |
| Jim     | Doe     |         |
+---------+---------+---------+

这里,如果 LIKE 查询匹配“Doe_”的“Doe”,它将填充表 2 的两行。但是通过写下来,我刚刚意识到 LIKE 查询在这里不是选项,因为变体不会限制为表 1 中 column2 的后缀,而是需要各种可能的喜欢(前导和尾随变体)表)。这反过来又会增加所需匹配的数量。 所以让我们忘记 LIKE,只关注精确匹配。

FULLTEXT 和 LIKE 没有任何关系。

“可能不完全匹配”——您需要对这种非限制进行更多限制。否则,任何查询尝试都将持续数周时间。

t2.c1 LIKE CONCAT('%', t1.c1, '%') 需要检查 t1 的每一行与 t2 的每一行;那是 400 万亿次测试。没有任何硬件可以在合理的时间内做到这一点。

FULLTEXT 适用于“单词”。如果您的 c1 和 c2 是字符串,那么使用 FULLTEXT 是有希望的。 FULLTEXT 比 LIKE 快得多,因为它具有基于单词的索引结构。

然而,即使是 FULLTEXT 也远不及 t2.c1 = t1.c1 的速度。尽管如此,这仍需要一个复合 INDEX(c1, c2) 然后它将是一个表的全表扫描(20M 行),再加上通过 BTree 索引对另一个表进行的 20M 探测。这就像 40M 操作——比 LIKE 的 400T 好很多。

为了继续,请仔细考虑您对“可能不完全匹配”的定义,并提出您可以接受的最佳情况。

好的,既然我决定放弃 LIKE 要求,那么您究竟打算使用什么作为索引? 我是这样看你的帖子的:

ALTER TABLE `table1` ADD FULLTEXT INDEX `indexname1` (`column1`, `column2`);

ALTER TABLE `table2` ADD FULLTEXT INDEX `indexname2` (`column1`, `column2`);

UPDATE `table1`, `table2` 
SET `table2`.`column3` = `table1`.`column3 `
WHERE CONCAT(`table1`.`column1`, `table1`.`column2`) = CONCAT(`table2`.`column1`, `table2`.`column2`);

这对吗?

不过有两个后续问题: 1)您认为更新是否与创建新表一样快,更快或更慢,即:

CREATE TABLE `merged` AS
SELECT `table1`.`column1`, `table1`.`column2`, `table1`.`column3`
FROM `table1`, `table2`
WHERE CONCAT(`table1`.`column1`, `table1`.`column2`) = CONCAT(`table2`.`column1`, `table2`.`column2`);

2) indizes 和/或匹配是否区分大小写?如果是,是否可以调整查询而无需将 column1 和 column2 更改为全部大写(或全部小写)?

【问题讨论】:

  • 优化指南很多,请自行google。您可以增加配置中的线程(InnoDB)。对于更新本身,我建议首先创建一个 temp_table,然后复制到 db2
  • 如果不使用通配符,为什么还要使用like运算符?将它们替换为=。另外,您是否在每个表的 where 条件中的 3 列上有多列索引?请分享解释的输出,以及 2 个表中的任何现有索引。
  • 我猜你说的是数据库,但你说的是表,对吧?
  • 您编写的查询将执行 20m x 20m 的查找(对于表 1 中的每一行,查找表 2 中的所有行)。如果你有一个 SSD 或一个好的 CPU,你不能写任何东西并期望它工作。如果您已经到了这一点,那么是时候在开始编写 SQL 之前进行思考了。您需要做什么,您可以使用哪些工具以及您不知道的中间部分是什么——这些是您每次发布前都需要回答的问题4000 亿次查找查询。
  • 给我们一些样本值和非精确比较。

标签: mysql join optimization


【解决方案1】:

FULLTEXTLIKE 彼此无关。

“可能不完全匹配”——您需要对这种非限制进行更多限制。否则,任何查询尝试都将持续数周时间。

t2.c1 LIKE CONCAT('%', t1.c1, '%') 要求检查 t1 的每一行与 t2 的每一行;这是 400 个万亿次测试。没有任何硬件可以在合理的时间内做到这一点。

FULLTEXT 与“单词”一起使用。如果你的 c1 和 c2 是字符串的话,那么有一些希望使用FULLTEXTFULLTEXTLIKE 快得多,因为它具有基于单词的索引结构。

然而,即使FULLTEXT 的速度也远不及t2.c1 = t1.c1。尽管如此,这仍需要一个复合 INDEX(c1, c2) 然后它将是一个表的全表扫描(20M 行),加上通过 BTree 索引到另一个表的 20M 探针。这就像 4000 万次操作——比 LIKE 的 400T 好很多。

为了继续,请仔细考虑您对“可能不完全匹配”的定义,并提出您可以接受的最佳情况。

编辑

WHERE CONCAT(t1.c1, t1.c2) = CONCAT(t2.c1, t2.c2)WHERE t1.c1=t2.c2 AND t1.c2 = t2.c2 更糟糕很多。后者将使用INDEX(c1,c2) 快速运行。

【讨论】:

  • 瑞克,我刚刚发现你的博客非常棒!你应该把我指出来作为参考,我本可以用我的微不足道的问题来饶恕其他评论者。
【解决方案2】:

试试这个: 1. 在 db1 和 db2 中添加一个带有 字符的新列,该字符从不在 column1 和 column2 中出现,例如 @

ALTER TABLE `db1` ADD  `column4` VARCHAR(40) NOT NULL ;

UPDATE db1 SET column4 = column1 + '@' + column2

2。对 db2 执行相同的操作。然后在第 4 列(在 db1 和 db2 中)创建一个索引 (BTREE)。

ALTER TABLE  `db1` ADD INDEX (  `column4` ) ;

ALTER TABLE  `db2` ADD INDEX (  `column4` ) ;

3。然后运行下一个查询:

UPDATE db1, db2 SET db2.column3 = db1.column3 WHERE db1.column4 = db2.column4;

它应该运行得足够快。 完成后 - 只需删除 column4 和它的索引

【讨论】:

  • (在 MySQL 的 SQL 变体中使用 CONCAT(...) 而不是 +。)
猜你喜欢
  • 1970-01-01
  • 2020-12-17
  • 1970-01-01
  • 2013-08-10
  • 1970-01-01
  • 2011-04-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多