使用“group by”键中的函数优化查询?
Optimize query with functions in `group by` key?
我正在使用 MySQL 8.0 并且在大型 table 上有一个缓慢的查询需要优化。
table包含1100万行数据及其结构:
CREATE TABLE `ccu` (
`id` bigint NOT NULL,
`app_id` int NOT NULL,
`ccu` int NOT NULL,
`audit_create` datetime NOT NULL,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `ccu_game_create_time_2a10bc69_idx` (`app_id`,`audit_create`) USING BTREE,
KEY `ccu_audit_create_idx` (`audit_create`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
我的查询是:
SELECT app_id, DATE(audit_create) cal_day, MAX(ccu) pcu, ROUND(AVG(ccu)) id_acu
FROM ccu
WHERE audit_create BETWEEN DATE_SUB(DATE(NOW()), INTERVAL 29 DAY) AND DATE(NOW())
GROUP BY app_id, DATE(audit_create)
查询运行超过 2 秒。我通过 between ... and ...
添加条件来过滤有用的数据。
但是,audit_create
中存储的数据格式为yyyy-MM-dd HH:mm:ss
,我必须使用date
函数但根据执行计划只有where
条件使用索引(仍然有temporary table), group by
子句根本不使用任何索引。
我无权更改 table 结构以添加日期列。是否可以优化查询以降低查询时间?
我能够通过添加表达式索引来消除 Using temporary
:
mysql> alter table ccu add key bk1 (app_id, (cast(audit_create as date)));
Query OK, 0 rows affected (0.02 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> explain SELECT app_id, DATE(audit_create) cal_day,
MAX(ccu) pcu, ROUND(AVG(ccu)) id_acu
FROM ccu
WHERE date(audit_create) BETWEEN DATE_SUB(DATE(NOW()), INTERVAL 29 DAY) AND DATE(NOW())
GROUP BY app_id, cast(audit_create as date)\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: ccu
partitions: NULL
type: index
possible_keys: bk1
key: bk1
key_len: 8
ref: NULL
rows: 1
filtered: 100.00
Extra: Using where
不幸的是,EXPLAIN 报告显示它将使用 type: index,这是一个 index-scan,换句话说,它将检查 1100 万个索引条目中的每一个.它可能会使它 比您的查询更糟。
我唯一的其他建议是 运行 每天查询一次并将结果存储在摘要中 table。 运行 每天一次 2 秒的查询,以便您可以快速获得聚合结果应该接受 table。但是你说你没有权限添加列,所以我猜你也没有权限添加table。
在这种情况下,获得一台速度更快、内存更大的计算机。
微不足道的改进:DATE(NOW())
--> CURDATE()
主要改进:
摆脱id
并改变
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `ccu_game_create_time_2a10bc69_idx` (`app_id`,`audit_create`) USING BTREE,
只是
PRIMARY KEY (`app_id`,`audit_create`),
这避免了对每一行进行二次查找。
29 天范围内似乎有 240 万行(共 1100 万行)。优化器必须决定是否使用索引(它确实这样做了),但要承受 240 万次额外查找,而不是扫描所有 1100 万行,需要额外排序。
要检查的另一件事是 innodb_buffer_pool_size
。如果 table 太大以至于无法放入该缓存,则可能会有很多 I/O。 (同样,我的索引更改将对此有所帮助。)
是的,Bill 生成的列可能会提高性能,这与我的建议无关。
注意:
您的范围是 29 天 + 1 秒。
比尔的范围是 30 天。
无论 audit_create
的数据类型如何,这都可以在今天早上 之前得到恰好 29 天:
WHERE audit_create >= CURDATE() - INTERVAL 29 DAY
AND audit_create < CURDATE()
我正在使用 MySQL 8.0 并且在大型 table 上有一个缓慢的查询需要优化。
table包含1100万行数据及其结构:
CREATE TABLE `ccu` (
`id` bigint NOT NULL,
`app_id` int NOT NULL,
`ccu` int NOT NULL,
`audit_create` datetime NOT NULL,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `ccu_game_create_time_2a10bc69_idx` (`app_id`,`audit_create`) USING BTREE,
KEY `ccu_audit_create_idx` (`audit_create`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
我的查询是:
SELECT app_id, DATE(audit_create) cal_day, MAX(ccu) pcu, ROUND(AVG(ccu)) id_acu
FROM ccu
WHERE audit_create BETWEEN DATE_SUB(DATE(NOW()), INTERVAL 29 DAY) AND DATE(NOW())
GROUP BY app_id, DATE(audit_create)
查询运行超过 2 秒。我通过 between ... and ...
添加条件来过滤有用的数据。
但是,audit_create
中存储的数据格式为yyyy-MM-dd HH:mm:ss
,我必须使用date
函数但根据执行计划只有where
条件使用索引(仍然有temporary table), group by
子句根本不使用任何索引。
我无权更改 table 结构以添加日期列。是否可以优化查询以降低查询时间?
我能够通过添加表达式索引来消除 Using temporary
:
mysql> alter table ccu add key bk1 (app_id, (cast(audit_create as date)));
Query OK, 0 rows affected (0.02 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> explain SELECT app_id, DATE(audit_create) cal_day,
MAX(ccu) pcu, ROUND(AVG(ccu)) id_acu
FROM ccu
WHERE date(audit_create) BETWEEN DATE_SUB(DATE(NOW()), INTERVAL 29 DAY) AND DATE(NOW())
GROUP BY app_id, cast(audit_create as date)\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: ccu
partitions: NULL
type: index
possible_keys: bk1
key: bk1
key_len: 8
ref: NULL
rows: 1
filtered: 100.00
Extra: Using where
不幸的是,EXPLAIN 报告显示它将使用 type: index,这是一个 index-scan,换句话说,它将检查 1100 万个索引条目中的每一个.它可能会使它 比您的查询更糟。
我唯一的其他建议是 运行 每天查询一次并将结果存储在摘要中 table。 运行 每天一次 2 秒的查询,以便您可以快速获得聚合结果应该接受 table。但是你说你没有权限添加列,所以我猜你也没有权限添加table。
在这种情况下,获得一台速度更快、内存更大的计算机。
微不足道的改进:DATE(NOW())
--> CURDATE()
主要改进:
摆脱id
并改变
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `ccu_game_create_time_2a10bc69_idx` (`app_id`,`audit_create`) USING BTREE,
只是
PRIMARY KEY (`app_id`,`audit_create`),
这避免了对每一行进行二次查找。
29 天范围内似乎有 240 万行(共 1100 万行)。优化器必须决定是否使用索引(它确实这样做了),但要承受 240 万次额外查找,而不是扫描所有 1100 万行,需要额外排序。
要检查的另一件事是 innodb_buffer_pool_size
。如果 table 太大以至于无法放入该缓存,则可能会有很多 I/O。 (同样,我的索引更改将对此有所帮助。)
是的,Bill 生成的列可能会提高性能,这与我的建议无关。
注意:
您的范围是 29 天 + 1 秒。
比尔的范围是 30 天。
无论 audit_create
的数据类型如何,这都可以在今天早上 之前得到恰好 29 天:
WHERE audit_create >= CURDATE() - INTERVAL 29 DAY
AND audit_create < CURDATE()