0

最近我在缓存到内存缓存之前的查询一直在处理!在此示例中,它需要 10 秒。在这种情况下,我要做的就是获得 10 个最近的热门歌曲。

我感觉它加载了所有 125,592 行然后只返回 10,对吗?

# 用户@主机:root[root]@localhost[]
# Query_time: 10 Lock_time: 0 Rows_sent: 10 Rows_examined: 125592
SELECT * FROM hits WHERE campaign_id = 30 ORDER BY id DESC LIMIT 10;

这是另一个慢查询:

# 时间:090214 5:00:40
# 用户@主机:root[root]@localhost[]
# Query_time: 3 Lock_time: 0 Rows_sent: 1 Rows_examined: 128879
SELECT count(DISTINCT(ip_address)) AS count_distinct_ip_address FROM `hits` WHERE (campaign_id = 30);

运行查询 phpMyAdmin 时,需要 1.3395 秒。虽然只是做一个SELECT * FROM hits只需要 0.0001 秒。我觉得很奇怪,返回所有匹配项所需的时间少于对它们进行排序,或者仅仅是我正在对它们进行排序?

对于那些想看我的桌子的人:

创建表`命中`(
  `id` int(11) unsigned NOT NULL auto_increment,
  `hostname` varchar(255) NOT NULL,
  `url` tinytext 不为空,
  `user_agent` tinytext NOT NULL,
  `created_at` 时间戳 NOT NULL 默认 CURRENT_TIMESTAMP,
  `ip_address` varchar(15) NOT NULL,
  `campaign_id` int(11) NOT NULL,
  主键(`id`),
  KEY `campaign_id`(`campaign_id`),
  KEY `ip_address` (`ip_address`)
);
4

5 回答 5

5

您的campaign_id索引似乎选择性低,即有很多具有此值的记录。

订购这么多唱片需要很多时间。

尝试INDEX SCANPRIMARY KEY订购时使用:

/* Edited, as MySQL does not use live feed from the derived source with ORDER BY */
SELECT *
FROM hits
WHERE IFNULL(campaign_id, campaing_id) = 30
ORDER BY id DESC
LIMIT 10;

至于您的第二个查询,可以做的不多,因为campaign_id = 30无论如何您都需要完整扫描,无论是它TABLE SCAN还是INDEX SCAN.

事实上,TABLE SCAN可以更快:

SELECT count(DISTINCT(ip_address)) AS count_distinct_ip_address
FROM `hits`
WHERE IFNULL(campaign_id, campaign_id)  = 30;

如果不是,您可以在该索引上创建一个索引(campaign_id, ip_address)并使用技巧来模仿INDEX GROUP BY该索引:

CREATE INDEX ix_hits_campaign_ip ON hits(campaign_id, ip_address)

SELECT SUM(cnt)
FROM (
SELECT CASE WHEN @r = ip_address THEN 0 ELSE 1 END AS cnt,
  @r := ip_address
FROM
  (SELECT @r:='') r,
  (
  SELECT ip_address
  FROM hits
  WHERE campaign_id = 30
  ORDER BY ip_address
  ) i
) o

这里的技巧很简单:我们不需要结果,只需要计数,因此不需要扫描实际值。索引扫描就足够了。

不幸的是,尽管 MySQL 文档在此处对松散索引扫描进行了说明,但它们实际上并不适用于复合索引。这就是我们需要模仿的原因INDEX SCAN WITH GROUP BY

我们通过强制 MySQL 使用INDEX RANGE SCAN它来检索所有带有campaign_id = 30sorted by 的记录来做到这一点ip_address。然后我们使用在第一个子查询中初始化为空字符串DISTINCT ip_address的会话变量来计算 'es 。@r

在第一个字段中,我们将变量设置为0前一个ip_address(存储在变量中)等于当前一个时;否则我们将其设置为1. 在第二个字段中,我们将当前值ip_address赋给变量。

最后我们检索SUM当然会给我们的第一个字段COUNT (DISTINCT ip_address)

于 2009-02-14T11:54:12.770 回答
2

上的索引(campaign_id,id)应该很好地照顾第一个。但独特的有点棘手......

编辑: MySQL 不为一个查询使用多个索引;所以是的,您需要一个涵盖查询中涉及的所有字段的索引。

于 2009-02-14T05:15:34.880 回答
1

如果查询处理时间过长,通常是因为缺少索引、磁盘 IO 不佳或其他一些瓶颈。一个有 120 000 行的表并不是很多数据,查询真的不应该花那么长时间。我真的会检查磁盘 io。

上面的答案 1 是加快查询 1 的一种方法。要加快查询 2,您可能需要创建一个聚合表,该表会随每次点击更新,或者每晚在批处理运行中更新,然后您可以添加点击天数尚未汇总的。日期范围的索引应该会相对较快。

您还应该对您的查询运行“解释”并查看它使用的索引(如果有)。您为 mysql 使用什么存储引擎?这也会产生影响。如果您使用 MISAM 存储引擎并同时进行插入和读取,这可能会对性能产生很大影响。

通过定期对较大的表格运行“分析”,确保您的表格统计信息得到更新。这有助于查询引擎选择最佳查询计划。

于 2009-02-14T06:00:57.440 回答
1

您需要使用 EXPLAIN 来了解它是如何执行您的查询的。您需要在生产或类似生产的数据上执行此操作,但显然不应该在生产系统上执行此操作(当然,您需要在开发和生产中使用相同的软件进行此练习) - 以上表明它正在执行全表扫描;这可能是因为它没有任何可以使用的索引,或者它选择不使用它们,因为它们的基数低等。

然后,您需要评估可以添加哪些索引来改进它,尝试添加它们,再次测试它,然后尝试通过检查添加索引不会破坏应用程序中的任何其他内容并且不会降低其他地方的性能来对更改进行 QA . 您将需要分析空间和性能影响——同样,这可以通过您的测试系统上的类似生产的数据来完成(性能测试当然需要在生产规格的硬件上完成)。

一旦您确定添加索引是正确的做法,您就可以像往常一样将这些更改滚动到软件版本中。不过要注意大表上的 ALTER TABLE,它可能需要一些时间并且会阻塞对表的写入(然而,120k 行可能不是一个大表)。在推出更改之前,请确保您知道需要多长时间以及它将对生产产生什么影响。

于 2009-02-14T23:23:43.440 回答
0

只是一个猜测。

SELECT * FROM hits WHERE (campaign_id = 30 AND id > 0) ORDER BY id DESC LIMIT 10;

希望 MySQL 将合并索引。祝你好运。

于 2009-02-14T23:20:38.330 回答