【问题标题】:MySQL query taking much time to load ? How to tune the database efficientlyMySQL 查询需要很长时间才能加载?如何有效地调优数据库
【发布时间】:2014-07-21 14:13:55
【问题描述】:

我有一个表,目前包含大约 500 万行。这是一个实时数据库,其中数据是通过抓取脚本填充的。脚本将数据连续插入到表中, 例如:

企业列表网站在 API 调用时给了我一个 JSON 响应,它被解析并插入到数据库中。重复检查也会在两者之间进行。在稍后阶段,我将获取他获得的数据以获取报告。

在尝试根据存储的信息获取报告时,完成脚本执行所需的时间太长。 抓取脚本是实时的,并且将来会继续使用记录更新表。 每个月它预计会获得 0.7 到 100 万条新记录。

下面是我的表结构,

CREATE TABLE IF NOT EXISTS `biz_listing` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `lid` smallint(11) NOT NULL,
  `name` varchar(300) NOT NULL,
  `type` enum('cat1','cat2') NOT NULL,
  `location` varchar(300) NOT NULL,
  `businessID` varchar(300) NOT NULL,
  `reviewcount` int(6) NOT NULL,
  `city` varchar(300) NOT NULL,
  `categories` varchar(300) NOT NULL,
  `result_month` varchar(10) NOT NULL,
  `updated_date` date NOT NULL,
  PRIMARY KEY (`id`),
  KEY `biz_date` (`businessID`,`updated_date`),
  KEY `type_date` (`type`,`updated_date`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8;

记录分为两类,“cat1”和“cat2”。 (我打算添加一个新类别,比如 cat3)

我需要有一个相同的站点汇总报告部分,其中显示了在选定月份范围内每个月的业务 ID。

这里选择为 2014 年 6 月至 7 月。

报告总数#类别

SELECT COUNT(t.`businessID`) AS bizcount, SUM(t.reviewcount) AS reviewcount, t.`type` 
FROM `biz_listing` t 
INNER JOIN 
( SELECT `businessID`,count(*) c FROM `biz_listing` WHERE updated_date BETWEEN '2014/06/01' AND LAST_DAY('2014/07/01') GROUP 
BY `businessID`,`type` HAVING c = 2 ) t2 
ON t2.`businessID` = t.`businessID` 
WHERE updated_date BETWEEN '2014/07/01' AND LAST_DAY('2014/07/01') GROUP BY t.`type`

解释(在 400 万个备份表上完成)

报告基于城市的总数#

SELECT COUNT(t.`businessID`) AS bizcount, SUM(t.reviewcount) AS reviewcount, t.`type`, t.`location` as city  
FROM `biz_listing` t 
INNER JOIN 
( SELECT `businessID`,count(*) c FROM `biz_listing` WHERE updated_date BETWEEN '2014/06/01' AND LAST_DAY('2014/07/01') GROUP 
BY `businessID`,`type` HAVING c = 2 ) t2 
ON t2.`businessID` = t.`businessID` 
WHERE updated_date BETWEEN '2014/07/01' AND LAST_DAY('2014/07/01') GROUP BY t.`location`, t.`result_month`

这里我们选择月份范围(六月-七月),所以它会列出两个月份范围内所有常见的businessID

第一个查询将根据业务类型输出

第二次查询将根据位置输出

问题是执行查询需要相当长的时间(600 秒甚至更多),有时查询在完成之前就死掉了。

如果您发现,请建议我对查询进行优化。

我认为索引会影响抓取脚本的插入性能。 考虑到插入和检索性能,如何修改当前脚本?

提前感谢。

编辑

我尝试了建议的覆盖索引,它比平时花费了更多的时间:(

解释如下:

【问题讨论】:

  • 你听说过解释吗?
  • @JohnRuddell Sry,我现在已经添加了。
  • 好的,您是否已经设置了任何索引?日期是否以这种格式存储?感谢您的快速响应
  • 尝试更改查询中日期的格式,然后..匹配格式。您也可以查看日期的 INTERVAL,这可能对速度有所帮助.. 问题在于没有索引的 HAVING 子句
  • 您应该尝试添加索引到任何时候进行多行比较,因此当您将一个表与另一个表连接时,您应该在两个表的 PK 和 FK 上有一个索引。这将提高速度. Ollie 的回答也有助于理解索引...我建议对其进行研究以了解添加索引的最佳方法以及连接表时的关键瓶颈在哪里(性能方面)

标签: php mysql sql indexing query-optimization


【解决方案1】:

这是一个 MyISAM 表,与 InnoDB 相比,它在插入查询和报告查询之间提供更少的争用。因此,让我们首先关注报告查询。确实,索引会减慢插入速度。但是由于缺少或不正确的索引,查询会减慢很多速度。

我相信,为了解决这个性能问题,分开考虑各种子查询有助于清晰。

让我们从其中一个开始。

SELECT `businessID`,
       count(*) c 
 FROM `biz_listing`
 WHERE updated_date BETWEEN '2014/06/01' AND LAST_DAY('2014/07/01')
 GROUP BY `businessID`,`type` 
HAVING c = 2 

这个子查询很简单,基本上结构良好。它能够使用索引跳转到满足 updated_date 范围标准的第一条记录,然后线性扫描该索引以查找最后一条记录。在扫描索引时,如果在其中找到type 列,它可以在扫描索引时收集满足查询所需的记录数。真快。

但是,你没有那个索引!所以这个子查询正在做一个全表扫描。正如我们在新英格兰所说的那样,这真是太慢了。

如果您使用复合覆盖索引(type,updated_date) 索引并将其中两列的顺序交换为(updated_date,type),它将作为此查询的高性能覆盖索引。复合索引中列的显示顺序不正确,无法使索引有助于此查询。

让我们从同样的角度看一下您的第一个主查询(省略子查询)。

SELECT COUNT(t.`businessID`) AS bizcount, 
       SUM(t.reviewcount) AS reviewcount, t.`type` 
  FROM `biz_listing` t 
 WHERE updated_date BETWEEN '2014/07/01' AND LAST_DAY('2014/07/01') 
 GROUP BY t.`type`

(这里有些不清楚。你在这里说COUNT(t.businessID),但你可能想要COUNT(DISTINCT t.businesscount)。你所拥有的将给出与COUNT(*)相同的结果,因为businessID没有NULL值。如果你这样做,您可以将HAVING SUM(DISTINCT businessID) > 2 放在查询中并摆脱对子查询的需求。)

此查询的工作方式与前一个类似。它扫描updated_date 范围内的索引,然后扫描type,然后获取businessIDreviewcount 的值。因此,按此顺序的复合索引将允许通过纯索引扫描来满足此查询,这将很快。

(updated_date, type, businessID,reviewcount)

请注意,任何可以从(updated_date, type) 索引中得到满足的查询也可以从这个索引中得到满足,因此您不需要同时使用它们。

阅读有关复合覆盖索引、紧密范围扫描和松散范围扫描的信息。

同样的索引可能会大大改善您的其他查询。试试看。

您似乎有一个备份表。您可以在该表中试验各种复合索引,直到获得好的结果。

我不愿意给出这样的建议:

TL;DR:将您的索引从这个更改为那个

因为那样你可能会带着下一个问题回到 SO 并被诱惑成为支持水蛭。 Can I avoid being a "leech" when I am a beginner in a topic and only ask questions?

你知道……教人钓鱼等等。

【讨论】:

  • 为此添加注释,尤其是在没有索引的情况下,having 子句可以使查询时间增加一倍!
  • @Ollie 谢谢你,所以你建议像 **KEY type_date (updated_date,type) ** 这样的单一索引可以帮助我加快查询速度。对于我已经解释的 2 个查询,这个单一索引(带有建议的顺序)是否会有所帮助。
  • 翻转两个复合索引的列顺序,您问题中的两个查询都会执行得更好。但请注意:这两个索引可能有助于满足其他查询。
  • @JohnRuddell 因为我的子查询有 businessID 分组,我是否应该在覆盖索引中也包含 businessID ,(updated_date,businessID,type) 而不是 (updated_date,type) ?如果我错了请原谅
  • @OllieJones //如果你这样做,你可以在查询中输入 HAVING SUM(DISTINCT businessID) > 2 并摆脱你对子查询的需要。)// businessID 的总和。我希望它应该是 HAVING COUNT(DISTINCT businessID) = 2
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-20
  • 1970-01-01
相关资源
最近更新 更多