MYSQL查询优化(需要提高速度)

Posted

技术标签:

【中文标题】MYSQL查询优化(需要提高速度)【英文标题】:MYSQL Query Optimization (need to increase speed) 【发布时间】:2011-06-10 01:05:32 【问题描述】:

我有一个保存 Google Analytics 数据的 mysql 表:

CREATE TABLE IF NOT EXISTS `analytics_data` (
  `ga_profile_id` int(11) NOT NULL,
  `page` varchar(200) NOT NULL,
  `source` varchar(150) NOT NULL,
  `medium` varchar(50) NOT NULL,
  `keyword` varchar(200) NOT NULL,
  `bounces` int(11) NOT NULL,
  `entrances` int(11) NOT NULL,
  `exits` int(11) NOT NULL,
  `new_visits` int(11) NOT NULL,
  `page_views` int(11) NOT NULL,
  `unique_page_views` int(11) NOT NULL,
  `time_on_page` int(11) NOT NULL,
  `visits` int(11) NOT NULL,
  `date` date NOT NULL,
  KEY `ga_profile_id` (`ga_profile_id`,`source`,`medium`,`date`),
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

我有一个查询要根据给定时间段内的谷歌分析配置文件 ID (ga_profile_id) 计算访问者的总和:

SELECT 
    SUM( `visits` ), ( UNIX_TIMESTAMP( `date` ) - 21600 ) * 1000 AS date 
FROM `analytics_data` 
WHERE 
    `date` >= '2011-05-09' AND `date` <= '2011-06-08' AND `ga_profile_id` = [...]
GROUP BY `date`

我们有大约 450 万条记录。

索引数据:

Type: BTREE
Fields/Cardinality:
ga_profile_id / 100
source / 10196
medium / 10196
date / 149893

解释选择 - id:1 - select_type:简单 - 表格:analytics_data - 类型:参考 - possible_keys:ga_profile_id - :ga_profile_id - 参考:常量 - :219555 - extra:使用where;使用临时的;使用文件排序

平均执行时间:1 秒

我们在虚拟专用服务器上,大多数查询在 0.0003 - 0.03 秒内执行。长查询(我打算在某个时候优化)通常是 0.3 秒。

我尝试过调整键,忽略一些,更改一些值,但似乎没有任何积极的影响。考虑到这是页面上众多查询之一。

我正在考虑将 MyISAM 更改为记忆 - 欢迎任何想法。

【问题讨论】:

如果创建复合索引ga_profile_id + date会怎样?来自 EXPLAIN 的 key_length(如果我没记错的话)可能会有用 您的查询有点奇怪:每隔一秒您就会得到一个分组。这真的是你想要的吗?这似乎是一个很好的分解。 看看在***.com/questions/6236416/…中提到的优化技巧。 @zerkms,我展示了来自 EXPLAIN 的所有信息——仅此而已。 @Bohemian - 每个日期都有一个分组 - 这是一个日期字段,而不是日期时间 :) 【参考方案1】:

您需要按此特定顺序创建复合索引ga_profile_id + date。并且您将通过此类查询获得最好的结果。

进一步可能的优化是预先计算每个日期的访问总和并将其用于快速计算。

【讨论】:

这将查询降低到 0.09 秒。完美的。谢谢【参考方案2】:

我有一个查询要根据给定时间段内的谷歌分析配置文件 ID (ga_profile_id) 计算访问者的总和

似乎已经很优化了...在您撰写此答案时的问题中,您已经删除了查询中最有趣的部分(ga_profile_id 上的实际子句),这是最具选择性的很有可能——因此是当前的索引使用情况。

最好的情况是,如果将date 上的索引放在多列索引中,例如(date, ga_profile_id) 或其他方式取决于您的使用模式和表格统计信息。

见indexes dos and donts。

【讨论】:

我以为我使用的是多列索引——那里有 4 个项目(另外 2 个用于其他查询),但我可以尝试删除它们。 你确定索引应该从date开始吗?这是WHERE 中的范围条件,因此第二部分将永远用于快速查找。 请务必查看它们的插入顺序。从左到右,选择性 -> 排序标准。如果您正在查看的两列之间有一些东西,那么在 MySQL 中甚至不会考虑索引(在 Postgres 中,在后一种情况下,您实际上会更好地使用单列索引和位图扫描) . @zerkms:我根本不是,因此“或其他方式取决于您的使用模式和表格统计信息” 谢谢@Denis——所有这些信息对我来说都非常有用。【参考方案3】:

运行索引将是第一个也是最简单的选择,但如果这没有帮助,我建议更多地研究一些基本的数据库管理策略,例如表分区。

【讨论】:

【参考方案4】:

@Kerry,看看 Denis 的解决方案......他提供的唯一替代方案是在 PROFILE ID FIRST 上设置索引,然后是日期,否则,您的索引将被合并到其他任何人在同一时间段内采取行动...

此外,@Bohemian 分组到第二个的观点是一个强项……您可能希望根据完整日期/时间列结果的 DATE ONLY 部分进行排序。

【讨论】:

丹尼斯的解决方案行不通,因为它是范围比较。【参考方案5】:

如果您的查询中有典型的日期范围,那么您可以考虑对表进行水平分区。当您的大部分数据“过时”并且您在一个或多个分区上只有您需要的“新鲜”数据而在另一个分区上只有所有这些旧数据时,也许它也会有所帮助。 RANGE Partitioning

【讨论】:

总是最后 30 天 -- 这是一个很好的参考,但是否可以对过去 30 天进行分区,而不是“DATE LESS THAN”? 首先我要说:您的查询现在非常快。我不建议您现在满意时使用分区。我知道 MySQL 也有一些问题。与 Oracle 数据库相比,这是 MySQL 的“新功能”,我不知道还存在什么问题。关于“过去 30 天”的事情:我不确定,但我认为这是不可能的,因为它会导致不断的重新分区。但是你可以每月、每年左右添加一个分区。

以上是关于MYSQL查询优化(需要提高速度)的主要内容,如果未能解决你的问题,请参考以下文章

mysql处理海量数据时的一些优化查询速度方法

Mysql处理海量数据时的一些优化查询速度方法

关于mysql的查询优化

关于mysql数据库优化

mysql提高查询速度

Oracle查询速度优化问题