【问题标题】:Count the number of rows between unix time stamps for each ID计算每个 ID 的 unix 时间戳之间的行数
【发布时间】:2012-08-31 23:36:29
【问题描述】:

我正在尝试为表格填充一些数据。该查询正在包含约 5000 万条记录的表上运行。我目前使用的查询如下。它计算匹配template id 并且是BETWEEN 两个unix 时间戳的行数:

SELECT COUNT(*) as count FROM `s_log` 
WHERE `time_sent` BETWEEN '1346904000' AND '1346993271' 
AND `template` = '1'

虽然上面的查询确实有效,但在循环每个 template 时性能相当慢,有时可能有数百个。时间戳存储为int 并被正确索引。只是为了测试一下,我尝试运行下面的查询,省略了time_sent 限制:

SELECT COUNT(*) as count FROM `s_log` 
AND `template` = '1'

正如预期的那样,它运行得非常快,但显然没有将计数结果限制在正确的时间范围内。如何获得特定 template 的计数并限制该计数 BETWEEN 两个 unix 时间戳?

EXPLAIN:

1 |简单 | s_log |参考 | time_sent,模板 |模板 | 4 |常量 | 71925 |在哪里使用

SHOW CREATE TABLE s_log:

CREATE TABLE `s_log` (
 `id` int(255) NOT NULL AUTO_INCREMENT,
 `email` varchar(255) NOT NULL,
 `time_sent` int(25) NOT NULL,
 `template` int(55) NOT NULL,
 `key` varchar(255) NOT NULL,
 `node_id` int(55) NOT NULL,
 `status` varchar(55) NOT NULL,
 PRIMARY KEY (`id`),
 KEY `email` (`email`),
 KEY `time_sent` (`time_sent`),
 KEY `template` (`template`),
 KEY `node_id` (`node_id`),
 KEY `key` (`key`),
 KEY `status` (`status`),
 KEY `timestamp` (`timestamp`)
) ENGINE=MyISAM AUTO_INCREMENT=2078966 DEFAULT CHARSET=latin1

【问题讨论】:

  • template 是否也被编入索引?您是否还考虑过在两列上都使用复合索引?
  • 请您在慢查询前添加EXPLAIN
  • @john: 也添加show create table
  • 为什么要用撇号下的整数值编写查询(以字符串形式提交)?

标签: mysql performance


【解决方案1】:

在这种情况下,您可能拥有的最佳索引是复合索引 template + time_sent

CREATE INDEX template_time_sent ON s_log (template, time_sent)

PS:只要查询中的所有列都是整数不要将它们的值括在引号中(在某些情况下,它可能会导致问题,至少对于较旧的 mysql 版本)

【讨论】:

  • 我也是这么想的,但是性能一点提升都没有。
  • @john:正如我所说 - 你需要有一个复合索引。不是2分开。删除template 索引,并按此特定顺序创建具有2 列template + time_sent 的1 个索引。 PS:添加了一个关于如何创建该索引 PPS 的示例:重要在您创建我建议的索引后 - 删除 template index
  • 感谢您的示例,它让我到达了我需要的地方。我不知道我需要删除template 索引才能使template + time_sent 索引生效。
  • @john:实际上并不是“生效”,而是关于冗余。 template + any_other_column 已经将 template 列作为最左侧的部分,因此任何仅使用 template 的查询也将使用它进行优化。所以单个template索引只是多余的,只会浪费CPU、RAM和存储空间。
【解决方案2】:

首先,您必须创建一个索引,将两个列放在一起(而不是分开)。还要检查你的表类型,如果你的表是 innoDB,我认为它会很好用。

最后,以这种方式使用 WHERE 子句:

`WHEREtemplate= '1' ANDtime_sent`在“1346904000”和“1346993271”之间

首先检查模板是否为 1,如果是,则检查第二个条件,否则跳过。这肯定会给你带来性能优势

【讨论】:

  • WHERE 中的谓词顺序无关紧要。所以你的最后一段有点没有意义。
  • MySQL 足够聪明来解决这个问题。您的 WHERE 中的订单不会加快您的查询速度
【解决方案3】:

如果您必须为每个模板调用查询,使用GROUP BY 一次查询调用获取所有信息可能会更快:

SELECT template, COUNT(*) as count FROM `s_log` 
WHERE `time_sent` BETWEEN 1346904000 AND 1346993271;
GROUP BY template

这只是一种猜测,这会更快,而且您还必须重新设计代码。


您也可以尝试使用InnoDB 而不是MyISAMInnoDB 使用聚集索引,它可能在大型表上表现更好。从 MySQL 网站:

通过聚集索引访问行速度很快,因为行数据位于索引搜索所引导的同一页面上。如果表很大,与使用与索引记录不同的页面存储行数据的存储组织相比,聚集索引架构通常会节省磁盘 I/O 操作。 (例如,MyISAM 将一个文件用于数据行,将另一个文件用于索引记录。)

Stackoverflow 上有一些问题讨论了InnoDBMyISAM 之间的性能:

【讨论】:

  • @zerkms:问得好,我一直使用 HAVING 和聚合函数 COUNT(*),例如:GROUP BY id HAVING COUNT(*) > 1。但在这种情况下,没有必要。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-04
  • 2016-06-21
  • 1970-01-01
相关资源
最近更新 更多