【问题标题】:Optimize MySQL Intersection Query For Large Data针对大数据优化 MySQL 交集查询
【发布时间】:2017-07-17 10:20:27
【问题描述】:

这是我的表结构:

CREATE TABLE `instagram_user_followers_mapping` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`instagram_user_id` varchar(20) NOT NULL,
`instagram_profile_id` varchar(20) NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `instagram_unique_user_follower_mapping` (`instagram_user_id`,`instagram_profile_id`),
KEY `instagram_user_followers_mapping_created_at_index` (`created_at`),
KEY `instagram_user_followers_mapping_updated_at_index` (`updated_at`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPRESSED

我在这个表中有超过 1 亿行。当我尝试在两个或多个“instagram_user_id”之间获取共同关注者时,它适用于表中少于 20,000 行的个人资料。但是对于超过 200 万行的配置文件,它的运行速度非常慢。我想让这些数据实时显示以进行分析和报告。最终用户可能会选择配置文件的任意组合,因此在这里创建汇总表并不是一个很好的选择。

我用来获取交集的查询是:

select instagram_profile_id, count(*) as myCount 
from instagram_user_followers_mapping 
where instagram_user_id IN ('1142282','346115','663620','985530') 
group by instagram_profile_id HAVING myCount >= 4

【问题讨论】:

  • 什么版本的 MySQL?
  • 基本上,HAVING... 之前的所有事情都需要完全完成——从 4 个 id 中的每一个中获取多行、排序、分组和计数。只有这样才能应用HAVING
  • mysql 版本 5.7.18

标签: mysql optimization reporting bigdata


【解决方案1】:

这应该运行得更快,但需要构造查询:

select  instagram_profile_id
    from  instagram_user_followers_mapping AS t
    WHERE  instagram_user_id = '1142282'
      AND  EXISTS
        (
        SELECT  *
            FROM  instagram_user_followers_mapping
            WHERE  instagram_profile_id = t.instagram_profile_id
              AND  instagram_user_id = '346115' 
        )
      AND  EXISTS 
        (
        SELECT  *
            FROM  instagram_user_followers_mapping
            WHERE  instagram_profile_id = t.instagram_profile_id
              AND  instagram_user_id = '663620' 
        )
      AND  EXISTS 
        (
        SELECT  *
            FROM  instagram_user_followers_mapping
            WHERE  instagram_profile_id = t.instagram_profile_id
              AND  instagram_user_id = '985530' 
        );

此公式避免了文件排序并避免收集给定 profile_id 的所有 user_id(反之亦然)。

innodb_buffer_pool_size 是否大于索引大小?

【讨论】:

  • innodb_buffer_pool_size 大于索引。
【解决方案2】:

IN 子句有点特别。使用此查询可以解决您的问题。我在 where 子句中将 count(*) 更改为 count(id)IN 语句等于。

select instagram_profile_id, count(id) as myCount 
from instagram_user_followers_mapping 
where instagram_user_id = '1142282' or instagram_user_id = '346115' or instagram_user_id = '663620' or instagram_user_id = '985530'
group by instagram_profile_id HAVING myCount >= 4

【讨论】:

  • 对不起,但这不会改变查询时间。 78763 rows in set (22.95 sec)
  • COUNT(id) 的语义是只计算非 NULL id。 COUNT(*) 跳过了额外的检查。 (这个查询的答案应该是一样的。)
【解决方案3】:

“IN”与“OR”应该不是问题。查询解释器应该认为它们是相同的(一个 EXPLAIN 应该证明这一点)。

实际上,在该查询上复制和粘贴 EXPLAIN 会非常有用...

由于这是我们在这里处理的相当多的行数,并且由于您的索引看起来足够,所以我会看 (2) 件事。首先是整体数据库配置(确保有足够的内存到 innodb_buffer_pool 等)。第二个(也是更可能的)问题是 GROUP BY 非常慢。尝试增加排序缓冲区类型参数,并在此处查看更多想法: https://dev.mysql.com/doc/refman/5.7/en/group-by-optimization.html https://dev.mysql.com/doc/refman/5.7/en/order-by-optimization.html

此外,如果可以,请尝试将每个“WHERE instagram_user_id =”作为单独的查询运行。

一般来说,这不是 MySQL 做的很快的事情,但是通过一些工作,您可能可以让它为您工作。您可能需要在应用程序方面发挥一些创意,具体取决于您需要多快。

【讨论】:

    猜你喜欢
    • 2015-02-22
    • 1970-01-01
    • 2021-12-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多