【问题标题】:mariadb group by slow on small subsetmariadb 在小子集上按慢速分组
【发布时间】:2018-12-26 02:59:17
【问题描述】:

我想在 mariadb 上使用此查询对一个相当大的表的子集进行分组:

第一季度

SELECT count(item_group_id) 
FROM item 
WHERE created > [-1 DAY] 
GROUP BY item_group_id

created > [-1 DAY] 的子集只有大约 200 行。整组表item超过一百万 行。

查询耗时 9 秒!

如果没有GROUPY BY,查询需要 40 毫秒!

这让我很困惑,因为我认为应该在 WHERE 语句之后应用 GROUP BY 语句。

所以我尝试了一个不同的查询,强制 mariadb 仅在 200 行的子集上应用 GROUP BY

第二季度

SELECT count(item_group_id) 
FROM (SELECT * FROM item WHERE created > [-1 DAY]) t 
GROUP BY item_group_id

不过,Q2 给了我与 Q1 完全相同的行为......

很有趣,Q3 成功了,并将执行时间从 9 秒缩短到了 100 毫秒:

第三季度

SELECT count(item_group_id) 
FROM (SELECT * FROM item WHERE created > [-1 DAY] LIMIT 100000000) t 
GROUP BY item_group_id

解释第二季度

+----+-------------+-------+-------+---------------+---------+---------+------+--------+-------------+
| id | select_type | table | type  | possible_keys |   key   | key_len | ref  |  rows  |    Extra    |
+----+-------------+-------+-------+---------------+---------+---------+------+--------+-------------+
|  1 | SIMPLE      | item  | index | NULL          | PRIMARY |     108 | NULL | 643167 | Using index |
+----+-------------+-------+-------+---------------+---------+---------+------+--------+-------------+

解释第三季度

+----+-------------+------------+------+---------------+------+---------+------+--------+---------------------------------+
| id | select_type | table      | type | possible_keys | key  | key_len | ref  | rows   | Extra                           |
+----+-------------+------------+------+---------------+------+---------+------+--------+---------------------------------+
| 1  | PRIMARY     | <derived2> | ALL  | NULL          | NULL | NULL    | NULL | 643207 | Using temporary; Using filesort |
+----+-------------+------------+------+---------------+------+---------+------+--------+---------------------------------+
| 2  | DERIVED     | item       | ALL  | NULL          | NULL | NULL    | NULL | 643207 |                                 |
+----+-------------+------------+------+---------------+------+---------+------+--------+---------------------------------+

使用LIMIT [large number] 完全符合我的要求并创建了临时表之前 应用GROUP BY

LIMIT 究竟做了什么让 mariadb 表现不同?谁能解释一下?

干杯!

编辑:SHOW CREATE TABLE

CREATE TABLE `item` (
  `id` char(36) COLLATE utf8_unicode_ci NOT NULL COMMENT '(DC2Type:uuid)',
  `item_group_id` char(36) COLLATE utf8_unicode_ci NOT NULL COMMENT '(DC2Type:uuid)',
  `content_id` char(36) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '(DC2Type:uuid)',
  `section_id` char(36) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '(DC2Type:uuid)',
  `person_id` char(36) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '(DC2Type:uuid)',
  `created` datetime NOT NULL,
  `updated` datetime NOT NULL,
  PRIMARY KEY (`id`),
  KEY `IDX_D4707EBD57B8F0DE` (`item_group_id`),
  KEY `IDX_D4707EBDD07ECCB6` (`content_id`),
  KEY `IDX_D4707EBDF639F774` (`section_id`),
  KEY `IDX_D4707EBD9395C3F3` (`person_id`),
  CONSTRAINT `FK_D4707EBD57B8F0DE` FOREIGN KEY (`item_group_id`) REFERENCES `item_group` (`id`),
  CONSTRAINT `FK_D4707EBD9395C3F3` FOREIGN KEY (`person_id`) REFERENCES `pseron` (`id`) ON DELETE SET NULL,
  CONSTRAINT `FK_D4707EBDD07ECCB6` FOREIGN KEY (`content_id`) REFERENCES `content` (`id`) ON DELETE SET NULL,
  CONSTRAINT `FK_D4707EBDF639F774` FOREIGN KEY (`section_id`) REFERENCES `section` (`id`) ON DELETE SET NULL
  ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

【问题讨论】:

  • 请提供SHOW CREATE TABLE。我们需要查看索引、引擎和其他内容。
  • @RickJames 我已经添加了节目创建。谢谢。
  • 生成临时表的成本很高;分类成本很高;可能是缓存导致更复杂的查询更快。运行两种口味两次;报告时间。 (缓存是速度差异的一个重要因素。)
  • CHAR(36) -- 闻起来像 UUID;他们是吗?表有多大 (GB)? innodb_buffer_pool_size的设置是什么?
  • 是的,Uuid。此外,innodb_buffer_pool_size : 134217728。该表只有大约 230mb(650 000 行)。

标签: mysql sql performance mariadb


【解决方案1】:

created 上没有索引,因此无法优化 WHERE 子句,因此需要进行全表扫描。添加这个:

INDEX(created, item_group_id)

一旦表变得大于innodb_buffer_pool_size,UUID 对性能非常不利。这是由于 UUID 的严重随机性,导致缓存无用,查询严重依赖 I/O。

更多

EXPLAIN 中的“行”数字不一定正确,有时甚至不正确。让我们进一步研究它。对查询的每个变体执行以下操作:

FLUSH STATUS;
SELECT ...;
SHOW SESSION STATUS LIKE 'Handler%';

这将提供被触摸的行数的实际计数。它通常是比较两个竞争查询的好指标。

真正的问题是 UUID 的随机性。

  • 假设您至少有 4GB 的 RAM,请将 innodb_buffer_pool_size 更改为 600M。您所拥有的可能是磁盘抖动。仅此一项,就可能将时间从 9 秒缩短到 1 秒。
  • 考虑使用 UUID 以外的其他内容。
  • 十六进制字符串不需要utf8
  • 可以将 UUID 从当前大小 36(或者可能是 108,由于 utf8)缩小到 BINARY(16)(16 字节),从而缩小数据和索引的大小。这是另一种减少 I/O 的方法。更多:http://mysql.rjweb.org/doc.php/uuid

【讨论】:

  • aitem_group_id(更新了问题)。这也不能真正回答我的问题。没有 GROUP BY,我没有任何问题。普通的 WHERE 查询速度非常快。我只是不明白 mariadb 以某种方式将 GROUP BY 应用于整个表......
  • 什么版本的 MySQL/MariaDB?查看我在答案中添加的内容;如果修复不充分,它可能会提供解决方案和深入挖掘的方法。
猜你喜欢
  • 2017-03-18
  • 1970-01-01
  • 1970-01-01
  • 2021-08-13
  • 2015-10-29
  • 2012-12-08
  • 2016-07-25
  • 2017-11-01
  • 2020-07-04
相关资源
最近更新 更多