2011-06-10 65 views
4

我有一个MySQL表保存谷歌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 

EXPLAIN SELECT
- ID:1个
- SELECT_TYPE:SIMPLE
- :analytics_data
- TY PE:REF
- possible_keys:ga_profile_id
- 关键:ga_profile_id
- 参考:常量
- :219555
- 额外:使用其中;使用临时;使用filesort

平均执行时间:1秒

我们在一个虚拟的私人服务器上,大多数查询在.0003 - 0.03秒内执行。 LONG查询(我将在某个时间点进行优化)通常为0.3秒。

我尝试调整键,忽略一些,改变一些值,似乎没有任何东西以积极的方式影响它。考虑到这是网页上许多查询中的一个。

我正在寻找将MyISAM更改为内存 - 任何想法都欢迎。

+1

如果您创建了一个组合索引'ga_profile_id + date',该怎么办?另外''key_length'(如果我没记错的话)来自EXPLAIN可能很有用 – zerkms 2011-06-10 01:09:32

+0

你的查询有点奇怪:你会得到一个不同秒的分组。那是*真的吗?你想要什么?这似乎是一个非常好的分解。 – Bohemian 2011-06-10 01:12:50

+0

@ zerkms,我展示了来自EXPLAIN的所有信息 - 这就是它的全部内容。 – 2011-06-10 01:16:51

回答

4

您需要按此特定顺序创建复合索引ga_profile_id + date。你会得到最好的,你可以得到这样的查询。

其他可能的优化是预先计算每个日期的访问总数,并将其用于快速计算。

+0

这会将查询放到.09秒。完善。谢谢 – 2011-06-10 01:24:12

1

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

它似乎在相当优化已经...在你的问题在写这个答案的时候,你已经删除了查询中最有趣的部分(ga_profile_id上的实际条目),这在所有可能性中是最具选择性的 - 因此是目前的索引使用情况。

在最好的情况下,如果您将它放在多列索引中,例如date,您可以设法使用索引。 (date, ga_profile_id)或其他方式取决于您的使用模式和表统计。

请参阅indexes dos and donts

+0

我以为我在使用多列索引 - 其中有4个项目(其他2个用于其他查询),但我可以尝试删除它们。 – 2011-06-10 01:18:22

+0

你确定索引应该从'date'开始吗?它是'WHERE'中的范围条件,所以第二部分将不会**用于快速查找。 – zerkms 2011-06-10 01:19:09

+1

请务必查看它们的插入顺序。从左到右,选择性 - >顺序标准。如果你正在研究两列之间的东西,索引甚至不会在MySQL中考虑(在Postgres中,在后一种情况下,单列索引和位图扫描实际上会更好) 。 – 2011-06-10 01:20:56

0

@Kerry,看看丹尼斯的解决方案......他提供的唯一替代方法是在PROFILE ID FIRST,THEN Date上索引索引,否则,您的索引将被纳入其他人也在同一时间采取行动期间...

另外,@波希米亚的分组到第二个点是一个很好的观点......您可能想根据完整日期/时间列结果的DATE ONLY部分进行排序。

+0

丹尼斯的解决方案将不起作用,因为它是范围比较。 – zerkms 2011-06-10 01:24:07

1

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

0

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

+0

这总是最后30天 - 这是一个很好的参考,但是可以在过去30天内进行分区,而不是“日期不到”? – 2011-06-10 02:05:19

+1

首先我要说:你的查询现在非常快。当你现在快乐时,我不会推荐使用分区。 我知道MySQL也有一些问题。与Oracle数据库相比,这是MySQL的“新功能”,我不知道还存在什么问题。 关于“过去30天”的事情:我不确定,但我不认为这是可能的,因为它会导致不断的重新分区。但是你可以每个月,每年添加一个分区。 – 2011-06-10 02:25:33