【问题标题】:mySQL - Query to count rows and total percentage too slowmySQL - 查询行数和总百分比太慢
【发布时间】:2017-07-25 14:45:35
【问题描述】:

名为“log”的表,目前有 5000 万行:

| id     | domainIP        |
| foo    | 158.132.34.5    |
| bob    | 128.12.244.3    |
| bob    | 128.12.244.3    |
| bob    | 19.152.134.4    |
| bob    | 168.152.34.9    |
| alice  | 178.132.64.10   |
| alice  | 188.152.214.200 |
| peter  | 208.162.36.153  |
| peter  | 208.162.36.153  |
| peter  | 208.162.36.153  |
| peter  | 198.168.94.201  |

我有以下查询,以获取id 与每个“域IP”一起使用的次数,以及每个“域IP”的百分比:

SELECT
    `log`.`id`,
    `log`.`domainIP`,
    COUNT(`log`.`domainIP`) AS "Times",
    totalsTable.Totals,
    (COUNT(`log`.`domainIP`)/totalsTable.Totals)*100 AS "Percentage"
FROM `log`
JOIN
    (
    SELECT
        `id`,
        COUNT(`domainIP`) AS Totals
    FROM `log` GROUP BY `id`
    ) AS totalsTable

ON (`log`.`id` = totalsTable.`id`)

GROUP BY `log`.`domainIP` ORDER BY `log`.`id` ASC, "Percentage"  DESC

返回:

| id     | domainIP        | Times | Totals | Percentage
| foo    | 158.132.34.5    | 1     | 1      | 100
| bob    | 128.12.244.3    | 2     | 4      | 50
| bob    | 19.152.134.4    | 1     | 4      | 25
| bob    | 168.152.34.9    | 1     | 4      | 25
| alice  | 178.132.64.10   | 1     | 2      | 50
| alice  | 188.152.214.200 | 1     | 2      | 50
| peter  | 208.162.36.153  | 3     | 4      | 75
| peter  | 198.168.94.201  | 1     | 4      | 25

结果正是我需要的,但速度太慢了(需要几分钟)。

这是从 phpmyadmin 导出的表结构。

CREATE TABLE `log` (
  `id` varchar(150) COLLATE utf8_unicode_ci DEFAULT NULL,
  `eDate` datetime DEFAULT NULL,
  `domainIP` varchar(150) COLLATE utf8_unicode_ci DEFAULT NULL,
  `event` varchar(150) COLLATE utf8_unicode_ci DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

ALTER TABLE `log`
  ADD UNIQUE KEY `logUnique` (`id`,`eDate`,`event`),
  ADD KEY `eDate` (`eDate`),
  ADD KEY `id` (`id`,`eDate`),
  ADD KEY `event` (`id`,`eDate`,`event`);

对较小版本的表进行 EXPLAIN 查询的结果:

id | select_type | table | type  | possible_keys      | key       | key_len | ref            | rows  | Extra
1 | PRIMARY | <derived2> | ALL   | NULL               | NULL      | NULL    | NULL           | 100   | Using where; Using temporary; Using filesort 
1 | PRIMARY | log        | ref   | logUnique,id,event | logUnique | 453     | totalsTable.id | 1     |  
2 | DERIVED | log        | index | NULL               | id        | 459     | NULL           | 100   |

我需要制定一个返回相同但可用的查询(以秒而不是分钟的方式返回结果),但不知道如何

注意:给 domainIP 添加索引只会稍微改善小样本的响应,但完整的表仍然需要 10 多分钟才能返回结果。

该表是为其他目的而创建的,我更愿意尽可能少地修改它的结构。

【问题讨论】:

  • 暂且不说表的设计非常糟糕,你可以做的是让你的 MySQL 使用更多的内存——这是通过使用InnoDB 作为存储引擎并增加@987654328 的值来完成的@ 系统变量。由于您使用的是 PHPMyAdmin,并且由于您的表的结构非常相似,因此我猜测您正在运行默认设置,因此 MySQL 可以在超级旧的硬件上运行。现在,至于为什么 table 的设计很糟糕 - 它并不真正相关,您要做的是将 I/O 从磁盘转移到 RAM,从而增加该 var 的值。
  • @Mjh 我会看看那个。本来这个表是为了一个完全不同的目的而设计的,很多年前,这是我现在才需要添加的一个功能,我不打算为了这么小的新需求重新设计这样的表。
  • 我了解您来自哪里,我们都曾遇到过这样的问题。无论如何,您要做的是将硬盘的缓慢性排除在外,并依靠 RAM 的读取 I/O 为 CPU 提供信息。实际的工作量将是相同的,不同之处在于计算机将从更快的存储中读取。通常,这就是 MySQL 和错误查询的魔锤。确保不要过度使用 innodb_buffer_pool_value
  • ideventlogunique 是多余的;放下那两个。将UNIQUE logunique 提升为PRIMARY KEY

标签: mysql sql database database-performance query-performance


【解决方案1】:

您可能会发现这有点快。从这个版本开始:

SELECT l.id, l.domainIP, COUNT(*) as Times,
       (SELECT COUNT(*) FROM log l2 WHERE l2.id = l.id) as Total
FROM log l
GROUP BY l.id, l.domainIP
ORDER BY l.id ASC;

您现有的以id 开头的索引应该足够了。

实际上,您甚至可以删除相关子查询来仅衡量GROUP BY 的性能。如果它不够好,那么您基本上知道您无法改进更复杂的查询。您将需要尝试其他方法,例如使用触发器来保持总计数。

【讨论】:

  • 这比我的查询执行得快很多,但似乎仍然不够。 10000 行需要 0.05 秒。 5000 万的时间(即使它是线性关系)仍然很多。我也单独执行了 GROUP BY,它也需要很长时间,所以问题似乎甚至在子查询之前。我想这张桌子太大了。
  • @ndelucca 。 . .或者您的硬件太小;)如果GROUP BY 太慢,那么您可能需要其他解决方案,例如在数据更改时使用触发器计算摘要。
【解决方案2】:

简而言之,查询花费如此长的时间并不奇怪,因为有varchar非唯一idvarchardomainIP。字符串比较可能比比较int 字段慢很多数量级。您应该考虑进行非规范化:

  1. id 字段必须是唯一标识符,例如longint
  2. 您应该声明像user_names of iduser_name 这样的表。然后你应该声明像'user_ips'这样的表,由iduser_id(实际上是user_names的一个ID)和domainIP组成。

只有这几个更改必须显着提高查询速度。希望对你有所帮助

【讨论】:

  • 比较和强制数据类型比 OP 的底层 I/O 问题多个数量级。您的回答并没有反映这一点,而是只关注将橙子与苹果进行比较和非规范化,这是他要求不要做的事情。我不会对你的答案投票,但它只对了一半。
  • 问题是关于如何提高查询速度。我做了一个简单的结论,它对更好的数据库结构也有副作用。考虑到提出问题的人谈到了表重组,这不会是多余的。我实际上是在谈论这个:SELECT id, COUNT(domainIP) AS Totals FROM log GROUP BY id 分组使用比较字符串的列值来工作。如果我错了,请告诉我
  • 我读了这个问题,这是 OP 写的:and i'd prefer to modify it's structure the least possible if anything at all,你建议改变结构。我写了我的评论,看来你要么什么都没读,要么不明白写了什么。要将任何内容与任何内容进行比较,您需要先从somewhere 读取它。在这种情况下,它从 硬盘驱动器 中读取,这是计算机中最慢的组件。因此,无论您如何对表进行非规范化或优化,最终都需要从某个地方读取。如果 somewhere 很慢,请得出结论。
  • 因此,遗憾的是,仅建议一种优化(改变结构,编写更好的查询)是实现速度的一半。
  • @Mjh 。 . .除了对数据结构的更改是否是一个好主意之外,它们不会对性能产生明显影响——尤其是它们不会更改从几分钟到几秒的查询。
猜你喜欢
  • 2012-05-01
  • 2019-08-12
  • 1970-01-01
  • 2014-01-27
  • 2016-12-30
  • 1970-01-01
  • 1970-01-01
  • 2017-10-17
  • 1970-01-01
相关资源
最近更新 更多