如何在 MySQL/MariaDB 中加入两个巨大的表?

Posted

技术标签:

【中文标题】如何在 MySQL/MariaDB 中加入两个巨大的表?【英文标题】:How to join two huge tables in MySQL/MariaDB? 【发布时间】:2020-12-17 02:56:27 【问题描述】:

我有两个表 kw 有 250000 条记录,t_n 有 100000 条记录。 我加入了两个表以在以下查询中获得集体结果:

SELECT kw.id AS kw_id,
       kw.word AS kw_word,
       t_n.translation AS t_n_translation
FROM kw, t_n 
WHERE kw.id = t_n.keyword_id
  AND kw.word LIKE '1%' 
GROUP BY t_n.translation 
LIMIT 10

我还尝试了以下简单连接作为上述查询的替代方法:

SELECT kw.id AS kw_id,
       kw.word AS kw_word,
       t_n.translation AS t_n_translation
FROM kw 
INNER JOIN t_n
    ON kw.id = t_n.keyword_id
   AND kw.word LIKE '1%' 
GROUP BY t_n.translation 
LIMIT 10

但是这两个查询都需要大约 60 到 150 秒,具体取决于内存和处理的系统资源。 这种执行超时对于扩展数据库和增加多个用户来说是难以忍受的。有没有什么有效的方法可以连接两个巨大的表?

更新 此查询用于 JQuery UI 自动完成。我使用 GROUP BY 子句来获取不同的值。是否有任何其他合适的解决方案来获得不同的值。

表格结构

--
-- Table structure for table `kw`
--

CREATE TABLE `kw` (
  `id` int(32) NOT NULL,
  `word` varchar(100) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
  `pos` varchar(12) NOT NULL,
  `definition` text NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

-- --------------------------------------------------------

--
-- Table structure for table `t_n`
--

CREATE TABLE `t_n` (
  `id` int(16) NOT NULL,
  `keyword_id` int(16) NOT NULL,
  `translation` varchar(64) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
  `created_on` datetime NOT NULL,
  `user_id` varchar(64) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

--
-- Indexes for dumped tables
--

--
-- Indexes for table `kw`
--
ALTER TABLE `kw`
  ADD PRIMARY KEY (`id`);

--
-- Indexes for table `t_n`
--
ALTER TABLE `t_n`
  ADD PRIMARY KEY (`id`);

--
-- AUTO_INCREMENT for dumped tables
--

--
-- AUTO_INCREMENT for table `kw`
--
ALTER TABLE `kw`
  MODIFY `id` int(32) NOT NULL AUTO_INCREMENT;

--
-- AUTO_INCREMENT for table `t_n`
--
ALTER TABLE `t_n`
  MODIFY `id` int(16) NOT NULL AUTO_INCREMENT;
COMMIT;

【问题讨论】:

能否附上执行计划? 在没有任何聚合函数的情况下,GROUP BY 子句永远不合适。如需进一步帮助,请参阅meta.***.com/questions/333952/… 1) 删除 GROUP BY - 这在逻辑上是错误的。 2)根据索引创建(我建议测试kw (word, id)t_n (keyword_id, translation) 没有排序的限制很少有意义。 请提供SHOW CREATE TABLE 【参考方案1】:

我猜你想要:

SELECT kw.id as kw_id, kw.word as kw_word, t_n.translation as t_n_translation
FROM kw INNER JOIN
     t_n
     ON kw.id = t_n.keyword_id AND kw.word LIKE '1%' ;

对于此查询,您需要在 kw(word, id)t_n(keyword_id) 上建立索引:

create index idx_kw_word_id on kw(word, id);
create index idx_t_n_keyword_id on t_n(keyword_id);

【讨论】:

我尝试了您提供的查询,但仍然需要很长时间。我还在我的问题更新中提供了表格结构。我认为这是由于您在答案的最后一行中提到的一些缺少索引。 @Rashid 。 . .您还没有添加答案中推荐的索引。 你能告诉我在kw(word,id)t_n(keyword_id)的字段上添加索引的过程吗? 好极了 - 现在添加索引后查询速度非常快。【参考方案2】:

正确的做法是用JOIN ON表示表是如何关联的,用WHERE表示过滤:

SELECT kw.id AS kw_id,
       kw.word AS kw_word,
       t_n.translation AS t_n_translation
FROM kw 
INNER JOIN t_n
    ON kw.id = t_n.keyword_id
WHERE kw.word LIKE '1%' 
ORDER BY t_n.translation 
LIMIT 10

尚不清楚GROUP BY 是否必要或正确。请解释它的目的,并告诉我们没有它会发生什么。没有ORDER BYLIMIT 很少有意义。也许您的意思是ORDER BY 而不是 GROUP BY? (所以我做了那个改变。)

假设您没有任何 TEXT 列,这些索引应该会有所帮助:

kw:   INDEX(word, id)
t_n:  INDEX(keyword_id, translation)

这些索引是“复合的”,但不是很相关,并且是“覆盖”的,可以提高性能。

如果我能看到SHOW CREATE TABLE,可能还有进一步的改进,比如修改PRIMARY KEY

更多

int(32) -- (32) 没有任何意义。 INT 始终是一个 32 位(4 字节)的数字; SMALLINT 是一个 16 位(2 字节)的数字。

似乎keyword_idt_n 中是独一无二的。如果是这样,请将其设为PRIMARY KEY,如果是id,则完全删除。

到那时,这两个表似乎可以合并为一个?有很多不同的词,每个词都有一个翻译?但也许不是,因为我看到两张桌子的大小不同。请解释架构。

【讨论】:

。 .我在更新我的问题时提供了表格结构。 @Rashid - 我又添加了一些。【参考方案3】:

我可以建议下一个解决方案:

    添加索引:

    CREATE INDEX keyword_id_ix on t_n(keyword_id);

    使用LIKE过滤大表:

    SELECT * FROM kw WHERE  kw.word LIKE '1%'

    使用JOIN 将结果与第二个表合并:

    SELECT
        kw.id as kw_id, kw.word as kw_word, t_n.translation as t_n_translation
    FROM (
        SELECT * FROM kw WHERE  kw.word LIKE '1%'
    ) kw 
    INNER JOIN t_n ON kw.id = t_n.keyword_id;
    
    

SQL 小提琴here

【讨论】:

不需要“派生表”(子查询);只是JOIN 两张桌子在一起。 @RickJames,当然我们可以在没有子查询的情况下使用直接 JOIN,但是在大表子查询上有助于减少连接前的数据集并提高性能。确定解决方案的效率必须在真实数据上进行测试

以上是关于如何在 MySQL/MariaDB 中加入两个巨大的表?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Nhibernate 中加入两个表

如何在 DynamoDb 中加入两个表?

如何在 JPA JPQL 查询中加入两个实体集合?

如何在 symfony 5 中加入两个表?

如何在 Flutter 中加入来自两个 Firestore 集合的数据?

如何在pyspark中加入具有多个重叠的两个数据框