Mysql 如何优化此内部联接查询以减少查询时间

Mysql 如何优化此内部联接查询以减少查询时间,mysql,sql,mariadb,Mysql,Sql,Mariadb,我有一张桌子,现在大约有一百万行。下面的查询需要大约5秒钟才能完成。您建议如何优化查询速度 # Thread_id: 14 Schema: defrop_defrop QC_hit: No # Query_time: 5.573048 Lock_time: 0.591625 Rows_sent: 0 Rows_examined: 1006391 # Rows_affected: 1 UPDATE `backlinks` as a INNER JOIN(SELECT b.`id` as

我有一张桌子,现在大约有一百万行。下面的查询需要大约5秒钟才能完成。您建议如何优化查询速度

# Thread_id: 14  Schema: defrop_defrop  QC_hit: No
# Query_time: 5.573048  Lock_time: 0.591625  Rows_sent: 0  Rows_examined: 1006391
# Rows_affected: 1
UPDATE `backlinks` as a
INNER JOIN(SELECT b.`id` as bid
           FROM `backlinks` b
           WHERE b.`googlebot_id` IS NULL AND b.`used_time` IS NULL AND 
b.`campaign_id` IN  (SELECT `id` FROM `campaigns` WHERE `status`=true) GROUP BY b.`campaign_id` ORDER BY RAND() limit 1
           ) as c
 ON (a.id = c.bid)
SET a.`crawler_id` = '10.0.0.13', a.`used_time`=NOW();
活动id、谷歌机器人id是外键、索引器。 已用时间和爬虫id是索引器 表phpmyadmin的屏幕截图

这是经过格式化的查询,因此我可以更好地阅读:

UPDATE backlinks bl JOIN
       (SELECT bl2.id as bid
        FROM backlinks bl2
        WHERE bl2.googlebot_id IS NULL AND
              bl2.used_time IS NULL AND 
              bl2.campaign_id IN (SELECT c.id FROM campaigns c WHERE status = true)
       GROUP BY b.campaign_id
       ORDER BY RAND() 
       LIMIT 1
     ) bl2
     ON bl.id = bl2.bid
    SET bl.crawler_id = '10.0.0.13',
        bl.used_time = NOW();
首先,不需要子查询中的GROUPBY。我将用EXISTS替换IN:

这会有一点帮助,但可能不会太多。我的猜测是,性能问题是外部排序的大小,或者等价地,是查询中GROUPBY所需的数据的大小

您还可以完全删除子查询:

UPDATE backlinks bl
    SET bl.crawler_id = '10.0.0.13',
        bl.used_time = NOW()
WHERE bl.googlebot_id IS NULL AND
      bl.used_time IS NULL AND 
      EXISTS (SELECT 1 FROM campaigns c WHERE bl.campaign_id = c.id AND c.status = true)
ORDER BY RAND()
LIMIT 1;
这影响很小,但它稍微理清了逻辑

我的猜测是,WHERE条件不是很有选择性,因此优化它们不会有多大帮助

在这一点上,问题是兰德的订单。如果您知道子查询返回了多少行,那么可以使用RAND进行预筛选。例如,假设至少有1000行被返回。然后:

UPDATE backlinks bl
    SET bl.crawler_id = '10.0.0.13',
        bl.used_time = NOW()
WHERE bl.googlebot_id IS NULL AND
      bl.used_time IS NULL AND 
      EXISTS (SELECT 1 FROM campaigns c WHERE bl.campaign_id = c.id AND c.status = true) AND
      RAND() < 0.01  -- keep about 1/100
ORDER BY RAND()
LIMIT 1;

这大大加快了排序速度,因为它位于数据的第100位。但是,如果没有足够的行与条件匹配,它可以过滤掉所有行。

这是格式化的查询,因此我可以更好地阅读它:

UPDATE backlinks bl JOIN
       (SELECT bl2.id as bid
        FROM backlinks bl2
        WHERE bl2.googlebot_id IS NULL AND
              bl2.used_time IS NULL AND 
              bl2.campaign_id IN (SELECT c.id FROM campaigns c WHERE status = true)
       GROUP BY b.campaign_id
       ORDER BY RAND() 
       LIMIT 1
     ) bl2
     ON bl.id = bl2.bid
    SET bl.crawler_id = '10.0.0.13',
        bl.used_time = NOW();
首先,不需要子查询中的GROUPBY。我将用EXISTS替换IN:

这会有一点帮助,但可能不会太多。我的猜测是,性能问题是外部排序的大小,或者等价地,是查询中GROUPBY所需的数据的大小

您还可以完全删除子查询:

UPDATE backlinks bl
    SET bl.crawler_id = '10.0.0.13',
        bl.used_time = NOW()
WHERE bl.googlebot_id IS NULL AND
      bl.used_time IS NULL AND 
      EXISTS (SELECT 1 FROM campaigns c WHERE bl.campaign_id = c.id AND c.status = true)
ORDER BY RAND()
LIMIT 1;
这影响很小,但它稍微理清了逻辑

我的猜测是,WHERE条件不是很有选择性,因此优化它们不会有多大帮助

在这一点上,问题是兰德的订单。如果您知道子查询返回了多少行,那么可以使用RAND进行预筛选。例如,假设至少有1000行被返回。然后:

UPDATE backlinks bl
    SET bl.crawler_id = '10.0.0.13',
        bl.used_time = NOW()
WHERE bl.googlebot_id IS NULL AND
      bl.used_time IS NULL AND 
      EXISTS (SELECT 1 FROM campaigns c WHERE bl.campaign_id = c.id AND c.status = true) AND
      RAND() < 0.01  -- keep about 1/100
ORDER BY RAND()
LIMIT 1;

这大大加快了排序速度,因为它位于数据的第100位。但是,如果没有足够的行与条件匹配,它可以过滤掉所有行。

您好,首先感谢您的快速响应。我使用GROUP BY是因为我希望每个活动只获得backlinks.id\u id,这样所有活动都有完全相同的机会提供1个backlinks.id.@Heopas。您只返回一行,所以这无关紧要。如果您希望每个活动都有一个,那么请提出一个新问题,解释您想要实现的逻辑,并提供示例数据和期望的结果。您的查询仅更新一行。如果你改变这个问题,你将使这个答案无效,这可能会导致选票下降——这是相当粗鲁的。嗨,首先感谢你的快速回答。我使用GROUP BY是因为我希望每个活动只获得backlinks.id\u id,这样所有活动都有完全相同的机会提供1个backlinks.id.@Heopas。您只返回一行,所以这无关紧要。如果您希望每个活动都有一个,那么请提出一个新问题,解释您想要实现的逻辑,并提供示例数据和期望的结果。您的查询仅更新一行。如果你改变这个问题,你将使这个答案无效,这可能会拉低选票,这是相当粗鲁的。