【问题标题】:Group By MariaDB very slow按 MariaDB 分组非常慢
【发布时间】:2017-03-18 05:07:40
【问题描述】:

我在 Linux 下使用 MariDB 10.1.18。

我有一个简单的表 (t),结构如下:

| id | a | b |  c |
------------------- 
|  1 | 3 | 7 | 10 |
|  2 | 4 | 6 | 9  |
|  3 | 2 | 7 | 11 |
|  4 | 3 | 5 | 10 |
|  5 | 4 | 8 | 12 |
|  6 | 2 | 9 | 6  |


id is primary key
a - has BTREE index
b - has HASH index
c - has HASH index

我假设主键会自动被索引。 我的查询很简单:

SELECT * FROM t GROUP BY a

出于性能目的,使用的引擎是MEMORY

在 500 万行上,上述查询需要 1 秒 完成,并且使用一个 CPU 的线程到 100%。现在 a 列有大约 150 个唯一值。

我认为如果我使用松散索引搜索可以解决这个问题。不幸的是,这似乎在 MariaDB 中不起作用,因为它从未使用过。松散扫描设置为开启。

我试过了

SELECT MAX(a) FROM t GROUP BY a

我的数据库需要 1.1 秒。

问题是,我怎样才能让这个选择变得快速?比如 0.05 秒。

谢谢!

【问题讨论】:

  • 请发布解释结果
  • 一个警告:使用标准开发技术可以实现的功能有限。您可能需要请 DBA 配置您的 MySQL 实例以获得更高的性能。
  • 目的是过滤掉某些行,然后返回按a分组的c最高的行。例如:选择 * FROM t WHERE b IN (5,6,7) AND a IN (2,3) GROUP BY a SORT BY c DESC 。但是,这不会给出正确的结果,因此需要加入。但我不会进入那个。
  • 选择所有列无效,但GROUP BY只选择一列。重新考虑该查询。
  • @Shadow - 自世纪之交以来,CPU 速度没有太大变化;扫描 RAM 中的 5M 行需要时间。 0.05s 不重写查询是不可能的,避免触及所有 5M。

标签: mysql performance group-by mariadb


【解决方案1】:

这取决于你真正想要什么。您的两个查询都没有多大意义。

SELECT MAX(a) FROM t GROUP BY a

可以改写为

SELECT a FROM t GROUP BY a

SELECT DISTINCT a FROM t

它需要“零”时间。

您的第一个查询将返回每个组的第一行。假设您没有全表索引-它将是按 id 排序的第一行。所以它相当于“查找每组最旧的记录”,并且可以重写为

select t.*
from (
    select min(id) as id
    from t
    group by a
) m
join t using(id)

而且也是“无暇”执行。

但是像这样的查询

select count(id) as id
from t
group by a

会很慢。与SUM()AVG() 相同,因为引擎需要读取每一行。而MIN()MAX() 每组只需要读取一行。

我在具有 370 万行和 30 个组的 InnoDB 表上进行了类似的查询。

【讨论】:

  • 实际上 SELECT DISTINCT a FROM t 需要大约 0.7 秒来处理 5 百万行。
  • SELECT a FROM t GROUP BY a - 也需要大约 0.7 秒。
  • 我已经在 30 组的 370 万行 InnoDB 表(mysql 5.6.21)上测试了我的查询。将副本转换为 MyISAM 后,一些查询变得非常慢。所以试试 InnoDB!
  • 不要为了速度而使用MEMORY;坚持使用 InnoDB。
  • 我在磁盘 InnoDB 上试过,它比 MEMORY 慢得多,几乎低了 10 倍。但是,我创建了一个 RAM 磁盘并使用 InnoDB 引擎将数据库写入 RAM 磁盘。这样在使用 InnoDB 时仍然可以保持速度。松散索引扫描似乎也只适用于 InnoDB 而不是 Memory。
【解决方案2】:

所以经过大量工作和测试,这是迄今为止最快的解决方案:

  1. 使用内存引擎 - 它比存储在 RAMDISK 上的 InnoDB 至少快 10 倍

  2. 对每个“a”列元素进行单独查询,而不是使用 Group BY 并在 PHP 中合并结果
    前任。选择 id 从 t WHERE b IN (3,4,5) AND c IN (6,7,8) AND a=1;

  3. 为每一列设置复合索引,例如 INDEX ON (a,b) , INDEX ON (a,c) 为规划器提供任何类型的查询足够的灵活性。索引必须是 BTREE。

现在对 5 百万行表执行非常复杂的查询大约需要 0.35 秒。

【讨论】:

    猜你喜欢
    • 2016-03-23
    • 2017-05-07
    • 2019-09-29
    • 1970-01-01
    • 2018-12-26
    • 2020-11-21
    • 1970-01-01
    • 2017-09-02
    • 1970-01-01
    相关资源
    最近更新 更多