Mysql 如何优化同时依赖于计数和分组依据的查询?
我有一个查询,其目的是统计在不同时期(按月、按季度、按年等)从网站下载了多少音乐作品(曲目)。查询操作表Mysql 如何优化同时依赖于计数和分组依据的查询?,mysql,sql,performance,Mysql,Sql,Performance,我有一个查询,其目的是统计在不同时期(按月、按季度、按年等)从网站下载了多少音乐作品(曲目)。查询操作表entityusage,entityusage\u file和track 要获取属于特定专辑的曲目的下载次数,我将执行以下查询: select date_format(eu.updated, '%Y-%m-%d') as p, count(eu.id) as c from entityusage as eu inner join entityusage_file as
entityusage
,entityusage\u file
和track
要获取属于特定专辑的曲目的下载次数,我将执行以下查询:
select
date_format(eu.updated, '%Y-%m-%d') as p, count(eu.id) as c
from entityusage as eu
inner join entityusage_file as euf
ON euf.entityusage_id = eu.id
inner join track as t
ON t.id = euf.track_id
where
t.album_id = '0054a47e-b594-407b-86df-3be078b4e7b7'
and entitytype = 't'
and action = 1
group by date_format(eu.updated, '%Y%m%d')
我需要设置entitytype='t'
,因为entityusage也可以保存其他实体的下载(如果entitytype='a'
那么整个相册都会被下载,并且entityusage\u文件
会保存相册在下载时“翻译”成的所有曲目)
此查询需要40-50秒。我尝试优化这个查询已经有一段时间了,但我感觉我用了错误的方法
这是生成报告必须运行的四个类似查询中的一个。报告最好能够在用户等待时完成。现在,我在看3-4分钟。那是一段很长的等待时间
这个查询是否可以通过索引进一步优化,或者我是否需要采取另一种方法来完成这项工作
CREATE TABLE `entityusage` (
`id` char(36) NOT NULL,
`title` varchar(255) DEFAULT NULL,
`entitytype` varchar(5) NOT NULL,
`entityid` char(36) NOT NULL,
`externaluser` int(10) NOT NULL,
`action` tinyint(1) NOT NULL,
`updated` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `e` (`entityid`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `entityusage_file` (
`id` char(36) NOT NULL,
`entityusage_id` char(36) NOT NULL,
`track_id` char(36) NOT NULL,
`file_id` char(36) NOT NULL,
`type` varchar(3) NOT NULL,
`quality` int(1) NOT NULL,
`size` int(20) NOT NULL,
`updated` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `file_id` (`file_id`),
KEY `entityusage_id` (`entityusage_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `track` (
`id` char(36) NOT NULL,
`album_id` char(36) NOT NULL,
`number` int(3) NOT NULL DEFAULT '0',
`title` varchar(255) DEFAULT NULL,
`updated` datetime NOT NULL DEFAULT '2000-01-01 00:00:00',
PRIMARY KEY (`id`),
KEY `album` (`album_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 CHECKSUM=1 DELAY_KEY_WRITE=1 ROW_FORMAT=DYNAMIC;
查询中的解释提供了以下信息:
+------+-------------+-------+--------+----------------+----------------+---------+------------------------------+---------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+-------+--------+----------------+----------------+---------+------------------------------+---------+----------------------------------------------+
| 1 | SIMPLE | eu | ALL | NULL | NULL | NULL | NULL | 7832817 | Using where; Using temporary; Using filesort |
| 1 | SIMPLE | euf | ref | entityusage_id | entityusage_id | 108 | func | 1 | Using index condition |
| 1 | SIMPLE | t | eq_ref | PRIMARY,album | PRIMARY | 108 | trackerdatabase.euf.track_id | 1 | Using where |
+------+-------------+-------+--------+----------------+----------------+---------+------------------------------+---------+----------------------------------------------+
这是您的查询:
select date_format(eu.updated, '%Y-%m-%d') as p, count(eu.id) as c
from entityusage eu join
entityusage_file euf
on euf.entityusage_id = eu.id join
track t
on t.id = euf.track_id
where t.album_id = '0054a47e-b594-407b-86df-3be078b4e7b7' and
eu.entitytype = 't' and
eu.action = 1
group by date_format(eu.updated, '%Y%m%d');
我建议在track(album\u id,id)
、entityusage\u file(track\u id,entityusage\u id)
和entityusage(id,entitytype,action)
这是您的查询:
select date_format(eu.updated, '%Y-%m-%d') as p, count(eu.id) as c
from entityusage eu join
entityusage_file euf
on euf.entityusage_id = eu.id join
track t
on t.id = euf.track_id
where t.album_id = '0054a47e-b594-407b-86df-3be078b4e7b7' and
eu.entitytype = 't' and
eu.action = 1
group by date_format(eu.updated, '%Y%m%d');
我建议在track(album\u id,id)
、entityusage\u file(track\u id,entityusage\u id)
和entityusage(id,entitytype,action)
上建立索引,因为GROUP BY操作在一个包含函数的表达式上,MySQL无法使用索引来优化该操作。这将需要一个“使用文件排序”操作
考虑到当前的表定义,我相信Gordon建议的索引是最好的选择。但即使有了这些索引,“高岗”也是eu
表,它将所有这些行分块并排序
为了获得更合理的性能,您可能需要引入一个“预计算结果”表。为每件事生成计数会很昂贵。。。但是我们可以提前支付这个价格
CREATE TABLE usage_track_by_day
( updated_dt DATE NOT NULL
, PRIMARY KEY (track_id, updated_dt)
)
AS
SELECT eu.track_id
, DATE(eu.updated) AS updated_dt
, SUM(IF(eu.action = 1,1,0) AS cnt
FROM entityusage eu
WHERE eu.track_id IS NOT NULL
AND eu.updated IS NOT NULL
GROUP
BY eu.track_id
, DATE(eu.updated)
关于entityusage(跟踪id、更新、操作)的索引
可能有助于提高性能
然后,我们可以针对新的“预计算结果”表编写一个查询,从而更好地获得合理的性能
“预计算结果”表将过时,需要定期刷新
这不一定是解决该问题的最佳解决方案,但它是我们可以在数据仓库/数据集市应用程序中使用的一种技术。这让我们可以通过大量的细节行一次获取计数,然后保存这些计数以便快速访问。因为GROUP BY操作是在一个包含函数的表达式上进行的,MySQL无法使用索引来优化该操作。这将需要一个“使用文件排序”操作
考虑到当前的表定义,我相信Gordon建议的索引是最好的选择。但即使有了这些索引,“高岗”也是eu
表,它将所有这些行分块并排序
为了获得更合理的性能,您可能需要引入一个“预计算结果”表。为每件事生成计数会很昂贵。。。但是我们可以提前支付这个价格
CREATE TABLE usage_track_by_day
( updated_dt DATE NOT NULL
, PRIMARY KEY (track_id, updated_dt)
)
AS
SELECT eu.track_id
, DATE(eu.updated) AS updated_dt
, SUM(IF(eu.action = 1,1,0) AS cnt
FROM entityusage eu
WHERE eu.track_id IS NOT NULL
AND eu.updated IS NOT NULL
GROUP
BY eu.track_id
, DATE(eu.updated)
关于entityusage(跟踪id、更新、操作)的索引
可能有助于提高性能
然后,我们可以针对新的“预计算结果”表编写一个查询,从而更好地获得合理的性能
“预计算结果”表将过时,需要定期刷新
这不一定是解决该问题的最佳解决方案,但它是我们可以在数据仓库/数据集市应用程序中使用的一种技术。这让我们可以通过大量的细节行一次获取计数,然后保存这些计数以便快速访问。你能试试这个吗。没有你提供的一些样本数据,我真的无法测试它。
在这种情况下,查询首先在表跟踪中查找,然后连接其他表
SELECT
date_format(eu.updated, '%Y-%m-%d') AS p
, count(eu.id) AS c
FROM track AS t
INNER JOIN entityusage_file AS euf ON t.id = euf.track_id
INNER JOIN entityusage AS eu ON euf.entityusage_id = eu.id
WHERE
t.album_id = '0054a47e-b594-407b-86df-3be078b4e7b7'
AND entitytype = 't'
AND ACTION = 1
GROUP BY date_format(eu.updated, '%Y%m%d');
你能试试这个吗。没有你提供的一些样本数据,我真的无法测试它。
在这种情况下,查询首先在表跟踪中查找,然后连接其他表
SELECT
date_format(eu.updated, '%Y-%m-%d') AS p
, count(eu.id) AS c
FROM track AS t
INNER JOIN entityusage_file AS euf ON t.id = euf.track_id
INNER JOIN entityusage AS eu ON euf.entityusage_id = eu.id
WHERE
t.album_id = '0054a47e-b594-407b-86df-3be078b4e7b7'
AND entitytype = 't'
AND ACTION = 1
GROUP BY date_format(eu.updated, '%Y%m%d');
假设entityusage\u file
主要是一个多:多映射表,请参阅以获取改进它的提示。注意,它要求去掉id
并创建一对两列索引,其中一个是主键(track\u id,entityusage\u id)
。因为您的表有一些额外的列,所以该链接并没有涵盖所有内容
uuid可以从108字节缩减到36字节,然后通过进入BINARY(16)
并使用压缩功能缩减到16字节。存在许多(包括版本8.0中的内置对);我的
要解释一件事。。。查询执行应该从跟踪开始(假设'0054a47e-b594-407b-86df-3be078b4e7b7'
是非常有选择性的)。问题是没有索引可以从那里转到下一个表。戈登建议的索引包括这样的内容
date\u格式(eu.updated,'%Y-%m-%d')
和date\u格式(eu.updated,'%Y%m%d')
可以简化为date(eu.updated)
。(无重大性能变化。)
(其他答案和评论涉及许多问题;我在这里不再重复。)假设entityusage\u文件
主要是一个多:多映射表,请参阅以获取改进它的提示。注意,它要求去掉id
并创建一对两列索引,其中一个是主键(track\u id,entityusage\u id)
。因为您的表有几个额外的列,所以t