【问题标题】:Does dropping an index reset the performance gain?删除索引是否会重置性能增益?
【发布时间】:2014-08-14 17:58:08
【问题描述】:

我尝试在包含数千万行的表中的外键列上添加索引,而我正在运行的半复杂查询的执行时间从 4 秒缩短到 10 毫秒。好的!

然后我尝试再次删除它以进行更多测试,但是当我执行相同的查询时,它仍然需要 10 毫秒(与添加索引之前需要 4 秒相比)。

删除索引是否会重置性能增益?如果没有,如何彻底删除索引?

查询如下所示(此查询每分钟运行一次,并将结果存储在另一个表中):

SELECT COUNT(*) AS count
FROM (
   SELECT MAX(ze.timestamp) AS time, r.device_id
   FROM loc_zone_events ze
   INNER JOIN loc_zones z ON ze.zone_id = z.id
   INNER JOIN raw_events r ON ze.raw_event_id = r.id
   WHERE z.app_id = 1
   AND ROUND(EXTRACT('epoch' FROM NOW() - ze.timestamp) / 60) BETWEEN 0 AND 10
   GROUP BY r.device_id
   ORDER BY time DESC
   ) AS t

【问题讨论】:

  • 您的查询中是否有 distinct/group by?限制 xxx ?你有有效的统计数据吗?你有默认的配置设置吗?
  • 我在编辑中添加了查询。我正在使用基本的 Windows 安装配置进行测试(生产环境位于 Amazon RDS r1.2xlarge 上)。至于有效的统计数据,我不确定你的意思
  • 这是您实际的查询吗?计算 max() 和 ORDER BY ... 效率非常低,您不需要...
  • 这不是 my 查询,但是是的,那是实际查询。你会做些什么来增强它?
  • COUNT DISTINCT(r.device_id) 对我来说似乎已经足够了。日期范围选择看起来也很尴尬。 (源于mysql?)

标签: sql postgresql indexing


【解决方案1】:

DROP INDEX 完全删除索引。

事务必须在对新查询生效之前提交,但这通常不是问题。您可能会看到其他测试工件,例如:

  • 在统计数据略有变化后,Postgres 切换到不同的查询计划。这表明您的cost settings might be inappropriate or some other poor configuration.
  • 查询的重复执行已填充缓存(这可能对大表产生差异)。对于中途可比的结果,将所有候选人运行几次。
  • 您的查询基于“最近十分钟”。可能有 1000 行,而 10 分钟后,可能只有 1 行。可能会产生的不同。

查询

对于初学者,删除完全不必要的部分:

SELECT COUNT(*) AS count
FROM (
   SELECT 1
   FROM   loc_zones       z
   JOIN   loc_zone_events ze ON ze.zone_id = z.id
   JOIN   raw_events      r  ON r.id = ze.raw_event_id
   WHERE  z.app_id = 1
   AND    round(EXTRACT('epoch' FROM NOW() - ze.timestamp) / 60) BETWEEN 0 AND 10
   GROUP  BY r.device_id
   ) AS t;

或者:

SELECT COUNT(DISTINCT r.device_id) AS count
FROM   loc_zones       z
JOIN   loc_zone_events ze ON ze.zone_id = z.id
JOIN   raw_events      r  ON r.id = ze.raw_event_id
WHERE  z.app_id = 1
AND    round(EXTRACT('epoch' FROM NOW() - ze.timestamp) / 60) BETWEEN 0 AND 10

(不一定更快,count(DISTINCT col) 不是性能英雄。)

但还有更多:

您的WHERE 条件round(...) 不是sargable。要检索“过去 10 分钟”的事件,请改用:

...
AND    ze.timestamp >= now() - interval '10 min'
AND    ze.timestamp <  now();  -- only if there can be timestamps in the future 

这是可搜索的,可以使用ze.timestamp 上的索引。

注意:你的表达式使用round()而不是trunc(),这实际上 涵盖(-0.5, 10.5) 的范围,即 11 分钟(不是 10 分钟),如果没有未来的时间戳,则为 10.5 分钟。以一种或另一种方式处理这种差异......

索引

由于似乎只有最后 10 分钟是相关的,因此您可以使用部分索引进一步改进它。这里的特殊困难是移动时间框架。这个相关的答案有一个完整的解决方案:

在此基础上,您将拥有一个部分索引,例如:

CREATE INDEX ze_timestamp_recent_idx ON tbl (timestamp DESC);
WHERE  created_at > f_min_ts();

并像这样调整查询:

WHERE  ...
AND    ze.timestamp > f_min_ts()   -- to match partial index
AND    ze.timestamp >= now() - interval '10 min'
AND    ze.timestamp <  now();

另外:不要使用基本类型名称timestamp 作为列名。

【讨论】:

  • 感谢您的回答! WHERE 条件 round(...) 是因为我必须只选择最后 10 分钟内的事件。也许它对于我需要的东西来说过于复杂
  • WHERE ze.timestamp &gt;= now() - '10 min'::interval
  • @wildplasser 我不确定它是否按预期工作,因为当我尝试按照您建议的方式时,它需要 2 倍于 round(...) 并且返回的数字比它应该高得多跨度>
  • Sargable,这个词到底是从哪里来的。 +1,一如既往的教育。
  • @JohnBarça:来自:S 搜索 ARG ument ABLE。我添加了指向Wikipedia 的链接。
【解决方案2】:

这取决于您使用的数据库。

1.) 如果您有一个大型数据库(如您所描述的).. 很可能您会对它进行分区。并在分区上创建索引。

2.) 如果您在运行查询时在大型表上创建索引也会减慢它的速度。 b/c 现在你的两个进程正在使用数据库资源。

3.) 在您运行查询时,是否还有其他进程也插入/更新/删除行?你有足够的临时空间吗?您的查询是否在进行排序/分组操作?

这些都很重要,而且更重要的是你的数据库的架构......在我看来。

干杯!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-06-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-27
    • 1970-01-01
    相关资源
    最近更新 更多