【问题标题】:How to optimize a query when grouping on a date / unix timestamp?在日期/unix时间戳上分组时如何优化查询?
【发布时间】:2013-09-11 17:44:12
【问题描述】:

我不太习惯 MySQL,但我认为它可以比现在快得多。

这是我的桌子:

CREATE TABLE `crashes` (
`id` int(11) NOT NULL AUTO_INCREMENT,
 `added_date` int(11) NOT NULL,
 `status` int(11) NOT NULL,
 `issue_id` varchar(32) NOT NULL,
 `report_id` text NOT NULL,
 `app_version_code` text NOT NULL,
 `app_version_name` text NOT NULL,
 `package_name` varchar(80) NOT NULL,
 `package_name_id` tinyint(4) NOT NULL,
 `file_path` text NOT NULL,
 `phone_model` text NOT NULL,
 `android_version` text NOT NULL,
 `build` text NOT NULL,
 `brand` text NOT NULL,
 `product` text NOT NULL,
 `total_mem_size` int(11) NOT NULL,
 `available_mem_size` int(11) NOT NULL,
 `custom_data` text NOT NULL,
 `stack_trace` text NOT NULL,
 `initial_configuration` text NOT NULL,
 `crash_configuration` text NOT NULL,
 `display` text NOT NULL,
 `user_comment` text NOT NULL,
 `user_app_start_date` text NOT NULL,
 `user_crash_date` text NOT NULL,
 `dumpsys_meminfo` text NOT NULL,
 `dropbox` text NOT NULL,
 `logcat` text NOT NULL,
 `eventslog` text NOT NULL,
 `radiolog` text NOT NULL,
 `is_silent` text NOT NULL,
 `device_id` text NOT NULL,
 `installation_id` text NOT NULL,
 `user_email` text NOT NULL,
 `device_features` text NOT NULL,
 `environment` text NOT NULL,
 `settings_system` text NOT NULL,
 `settings_secure` text NOT NULL,
 `shared_preferences` text NOT NULL,
 `application_log` text NOT NULL,
 `media_codec_list` text NOT NULL,
 `thread_details` text NOT NULL,
 `user_ip` text NOT NULL,
 PRIMARY KEY (`id`),
 KEY `package_name_id` (`package_name_id`)
) ENGINE=MyISAM AUTO_INCREMENT=202364 DEFAULT CHARSET=utf8

如您所见,它填充了 200k 行。我想检索行 added_date(unix 时间戳,int(11)),以及当天的行数。

所以我选择日期、日期(如 Y-M-D)和计数:

SELECT date_format(from_unixtime(added_date), '%Y-%c-%d') as date, added_date, count(*) as nb_crashes FROM crashes WHERE package_name = 'net.bicou.redmine' GROUP BY date ORDER BY date ASC

很慢!在我主机上的专用 mysql 服务器上几乎 1.5 秒。

所以我想我可以稍微优化一下:我添加了一个 tinyint 的 package_name_id,每个 package_name 都是唯一的(我在那 200k 行上有 5 个不同的 package_name 值)。我将其设为INDEX,以便 MySQL 可以更快地浏览它。
结果:0.9 秒。这好多了,但仍然没有达到我的预期!

我怎样才能优化这个东西?我想在每一行上创建日期,然后分组非常昂贵。但是我不知道我怎样才能让它更快......

编辑:

这是我为更新表格所做的:

ALTER TABLE  `crashes` ADD  `temp` DATETIME NOT NULL
UPDATE crashes SET temp = FROM_UNIXTIME( added_date )
ALTER TABLE  `crashes` ADD INDEX (  `temp` )

这是更新后的查询:

SELECT added_date, count(*) as nb_crashes FROM crashes WHERE package_name_id=3 GROUP BY year(temp),month (temp),dayofmonth(temp) ORDER BY temp ASC

我还有大约一秒的执行时间...我做错了什么吗?

【问题讨论】:

    标签: mysql optimization indexing


    【解决方案1】:

    如果您需要以类似日期的方式查询该字段,则不应使用 unix 时间戳值。您应该使用日期、日期时间或时间戳字段类型。

    为什么?

    因为如果您想查询特定日期的结果或按日期分组记录,您将始终必须使用FROM_UNIXTIME() 来执行此操作。如果您尝试将其用于排序、连接、过滤器、组等,此函数调用将阻止您在日期值上使用任何类型的索引。除了使用正确的查询之外,您无能为力数据类型,然后索引您将用于排序、过滤器、连接、组等的字段。

    实际上,在 DB 中使用 unix 时间戳似乎被缺乏经验的 PHP 开发人员激增,他们认为在 PHP 中使用这种格式的日期更容易(要么他们懒得将 date/datetime DB 输出转换为 unix 时间戳在 PHP 中,或者他们还没有弄清楚如何使用 dateTime 和 dateInterval PHP 类来使 PHP 中的日期处理更容易)。

    我的建议是现在改掉这个习惯,开始学习如何在 MySQL 中使用日期/日期时间字段。

    我建议查看您的表格 DDL 的另一件事是您可能应该重新审视那里的文本字段的使用。大多数情况看起来可能最好是 varchar 字段。

    【讨论】:

    • 非常感谢。我是一个 10 年前作为业余爱好者学习的肮脏的 php 开发人员,我刚刚重新开始。到时候我会做的,非常感谢您的提示。
    • 嘿,我已经更新了我的问题,因为我仍然无法按照我想要的方式优化我的表格。关于那些 text 字段,它们不用于选择、加入或分组,所以我认为这无关紧要。
    【解决方案2】:

    如果需要性能,则需要索引,如果需要索引,则需要在执行查询之前创建包含正确类型数据的列。

    这需要创建一个额外的列并使用转换结果填充它,并插入正确填充该列的所有新行。

    您为什么将日期存储为时间戳而不是正确的DATE 列?

    【讨论】:

    • 谢谢。请参阅我对迈克回答的评论。我会将字段转换为日期字段!
    • 好计划,迈克确实有更彻底的答案。
    【解决方案3】:

    您可以在 package_nameadded_date 列上创建索引。此外,将您的ORDER BY 更改为按added_date 而非date 排序,因为按date 排序可能需要文件排序步骤。

    无论如何EXPLAIN 有助于更好地诊断查询。

    【讨论】:

    • 这仍然无济于事,因为他仍然需要按计算日期字段进行分组,这根本无法使用索引。这里唯一的解决方案是在字段中使用正确的日期/日期时间数据类型。
    猜你喜欢
    • 1970-01-01
    • 2022-10-07
    • 2010-10-02
    • 2012-03-06
    • 1970-01-01
    • 2023-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多