【问题标题】:Optimizing database query with up to 10mil rows as result优化数据库查询,结果多达 1000 万行
【发布时间】:2013-03-15 22:52:30
【问题描述】:

我有一个 MySQL 查询,我需要尽可能优化(如果可能,加载时间应该低于 5 秒)

查询如下:

SELECT domain_id, COUNT(keyword_id) as total_count
FROM tableName
WHERE keyword_id IN (SELECT DISTINCT keyword_id FROM tableName WHERE domain_id = X)
GROUP BY domain_id
ORDER BY total_count DESC
LIMIT ...
  • X 是来自输入的整数
  • domain_id 和keyword_id 已编入索引
  • 数据库在本地主机上,所以网速应该是最大的

WHERE 子句的子查询最多可以获得 1000 万个结果。另外,对于 MySQL 来说,计算 COUNT 和 ORDER BY 这个计数似乎真的很困难。

我尝试将此查询与 SOLR 混合使用,但没有结果,一次获取如此多的行数给 MySQL 和 SOLR 带来了困难

我正在寻找具有相同结果的解决方案,无论我是否必须使用不同的技术或对此 MySQL 查询进行改进。

谢谢!


查询逻辑是这样的:

我们有一个域,我们正在搜索该域上使用的所有关键字(这是子查询)。然后我们取所有使用第一个查询中找到的至少一个关键字的域,按域分组,每个域使用的关键字数量,我们必须按使用的关键字数量排序 DESC。

我希望这是有道理的

【问题讨论】:

  • 为什么需要内部查询,可以在 where 子句中使用 domain_id=x?如果不存在,则在 domain_id 上添加索引
  • @shola 请仔细看查询,是相关数据
  • 你能发布解释吗?
  • "来自 WHERE 子句的子查询最多可以获得 1000 万个结果。" - 你有 1000 万个不同的关键字?!?
  • @Vatev 还有很多,但这不是重点...

标签: mysql sql performance solr large-data


【解决方案1】:

就 MySQL 而言,您所能做的就是使用覆盖索引最小化查询的磁盘 IO,并更高效地重写它,以便查询可以从中受益。

由于keyword_id 在表的另一个副本中有匹配项,所以COUNT(keyword_id) 变为COUNT(*)

已知您使用的子查询类型对于 MySQL 来说是最坏的情况(它为每一行执行子查询),但我不确定是否应该在此处将其替换为 JOIN,因为它可能是为您的数据制定适当的策略。

您可能理解,查询如下:

SELECT domain_id, COUNT(*) as total_count
FROM tableName
WHERE keyword_id IN (X,Y,Z)
GROUP BY domain_id
ORDER BY total_count DESC

使用覆盖复合索引(keyword_id, domain_id [,...]) 将具有最佳性能,因此这是必须的。另一方面,查询如下:

SELECT DISTINCT keyword_id FROM tableName WHERE domain_id = X

将在覆盖综合索引(domain_id, keyword_id [,...]) 上具有最佳性能。所以你需要他们两个。

希望,但我不确定,当你有后一个索引时,MySQL 可以理解你不需要在子查询中选择所有keyword_id,但你只需要检查是否有条目索引,我相信如果你不使用 DISTINCT,它会更好地表达。

所以,我会尝试添加这两个索引并将查询重写为:

SELECT domain_id, COUNT(*) as total_count
FROM tableName
WHERE keyword_id IN (SELECT keyword_id FROM tableName WHERE domain_id = X)
GROUP BY domain_id
ORDER BY total_count DESC

另一种选择是将查询重写如下:

SELECT domain_id, COUNT(*) as total_count
FROM (
  SELECT DISTINCT keyword_id
  FROM tableName
  WHERE domain_id = X
) as kw
JOIN tableName USING (keyword_id)
GROUP BY domain_id
ORDER BY total_count DESC

您再次需要这两个复合索引。

哪个查询更快取决于您的tableName 中的统计信息。

【讨论】:

    【解决方案2】:

    我不是 100% 确定,但请你试试这个

    SELECT t1.domain_id, COUNT(t1.keyword_id) as total_count
    FROM tableName AS t1 LEFT JOIN
    (SELECT DISTINCT keyword_id FROM tableName WHERE domain_id = X) AS t2
    ON t1.keyword_id = t2.keyword_id
    WHERE t2.keyword_id IS NTO NULL
    GROUP BY t1.domain_id
    ORDER BY total_count DESC
    LIMIT ...
    

    我们的目标是用INNER JOIN 替换WHERE IN 子句,这样会更快。 WHERE IN 子句总是让 Mysql 服务器陷入困境,但当你处理大量数据时,它会更加明显。使用WHERE IN 仅当它使您的查询看起来更容易阅读/理解、您有一个小数据集或以其他方式不可能时(但您可能会有其他方式来做到这一点:))

    【讨论】:

      【解决方案3】:

      您可以尝试 JOIN 代替子查询:

      SELECT tableName.domain_id, COUNT(tableName.keyword_id) AS total_count
      FROM tableName
      INNER JOIN tableName AS rejoin
      ON rejoin.keyword_id = tableName.keyword_id
      WHERE rejoin.domain_id = X
      GROUP BY tableName.domain_id
      ORDER BY tableName.total_count DESC
      LIMIT ...
      

      【讨论】:

      • @GabrielCol 您可以为(原始和我的)查询发布 EXPLAIN 输出吗?
      • 应用您的方法执行查询需要 187.6582 秒才能执行,这与我的原始查询非常相似。
      • @GabrielCol 只是想比较两个查询中索引的使用方式
      猜你喜欢
      • 1970-01-01
      • 2023-03-10
      • 1970-01-01
      • 2023-01-05
      • 2011-02-27
      • 1970-01-01
      • 1970-01-01
      • 2018-12-01
      • 2019-03-30
      相关资源
      最近更新 更多