5

我正在努力了解如何使用成本和实际时间来优化查询。我的应用是带有 PostgreSQL 9.1 db 的 rails 3。Delayed_job 使用我的查询:

EXPLAIN ANALYZE SELECT  "delayed_jobs".*
FROM "delayed_jobs"
WHERE ((run_at <= '2011-05-23 15:16:43.180810' AND (locked_at IS NULL OR locked_at < '2011-01-25 11:05:28.077144') OR locked_by = 'host:foo pid:2') AND failed_at IS NULL AND queue = 'authentication_emails')
ORDER BY priority ASC, run_at ASC LIMIT 5

或者:

EXPLAIN ANALYZE SELECT  "delayed_jobs".*
FROM "delayed_jobs"
WHERE ((run_at <= '2011-05-23 15:16:43.180810' AND (locked_at IS NULL OR locked_at < '2011-01-25 11:05:28.077144') OR locked_by = 'host:foo pid:2') AND failed_at IS NULL )
ORDER BY priority ASC, run_at ASC LIMIT 5

对于第一个查询,输出等于:

Limit  (cost=7097.57..7097.57 rows=1 width=1008) (actual time=35.657..35.657 rows=0 loops=1)
  ->  Sort  (cost=7097.57..7097.57 rows=1 width=1008) (actual time=35.655..35.655 rows=0 loops=1)
        Sort Key: priority, run_at
        Sort Method: quicksort  Memory: 25kB
        ->  Seq Scan on delayed_jobs  (cost=0.00..7097.56 rows=1 width=1008) (actual time=35.648..35.648 rows=0 loops=1)
              Filter: ((failed_at IS NULL) AND ((queue)::text = 'authentication_emails'::text) AND (((run_at <= '2011-05-23 15:16:43.18081'::timestamp without time zone) AND ((locked_at IS NULL) OR (locked_at < '2011-01-25 11:05:28.077144'::timestamp without time zone))) OR (locked_by = 'host:foo pid:2'::text)))
Total runtime: 35.695 ms

该表目前有 90k 条记录,范围为 0-200k。我们注意到此查询导致 CPU 出现峰值并导致瓶颈。从上面的解释信息中可以学到什么。如果有索引,应该在哪里添加?谢谢

DB Schema.. 表有 0 个索引。

  create_table "delayed_jobs", :force => true do |t|
    t.integer  "priority",   :default => 0
    t.integer  "attempts",   :default => 0
    t.text     "handler"
    t.text     "last_error"
    t.datetime "run_at"
    t.datetime "locked_at"
    t.datetime "failed_at"
    t.text     "locked_by"
    t.datetime "created_at",                :null => false
    t.datetime "updated_at",                :null => false
    t.string   "queue"
  end
4

2 回答 2

1

分析

如果您将阅读 PostgreSQL 文档的这一部分,您将了解规划器如何使用统计信息来估算成本。这是非常有用的信息!

如果您说该表有大约 90k 条记录(并使用默认成本),那么行处理的成本将是:

90000 * (cpu_tuple_cost + cpu_operator_cost) = 90000 * 0.0125 = 1125

我们现在可以估计您的表格占用了多少页:

(7097.56-1125)/seq_page_cost = 5972.56

这使得它大约为 46Mb(默认为 8k 页面大小)。因此,我假设您的表适合shared_buffers,甚至是默认表。

查看我还假设的平均行宽,该表主要存储为MAIN.

接下来,您将使用类型字段textstring谓词。不确定它们如何映射到 PostgreSQL 内部类型,但我认为它是text. 这种类型默认是可压缩的,因此 PostgreSQL 必须对每一行执行解压缩来检查谓词。我不确定在哪个阈值压缩开始之后,请查看此消息(以及整个线程)。

结论

  1. 你没有向我们展示真正的EXPLAIN (analyze)输出,因为我也不认为 35ms 查询会导致瓶颈,除了......
  2. 您没有提到在瓶颈时刻有多少会话正在使用您的数据库,也不清楚该查询的运行频率。我认为这是一个非常受欢迎的。
  3. 您的表似乎适合内存,因此在任何情况下所有操作都将受CPU 限制。
  4. 谓词中使用的值是可压缩的,并且似乎已被压缩。

因此,我说瓶颈来自于对数据并行运行的峰值查询量,这需要额外的 CPU 周期进行解压缩。

该怎么办?

  1. 规范化您的表格。感觉“队列”列的选择性很低。考虑为其创建外部类型(如ENUM),或使用适当的外键组织字典表。我也不确定回合locked_by专栏,可以标准化吗?
  2. run_at在和locked_at列上创建索引。
  3. 索引ON priority, run_at列将使您的排序受益,但我怀疑它在这种情况下会有所帮助。我假设该priority列的选择性较低,因此规划者会更喜欢使用Bitmap AndonIndex Scansrun_atlocked_at

我希望我在这里没有大错:) 欢迎评论/更正!

PS让我知道它对你有什么好处。

于 2013-01-29T10:18:25.953 回答
0

Where should indexes be added?

如前所述,特定的 SQL 查询没有可以作为索引的优秀候选者的列:如果您有大量历史数据,则在日期时间列上使用 < 运算符将返回(可能)一个大结果集。满足条件时从索引返回的结果集越大,索引在给定查询上下文中的效率越低(减少的能力越小)。此外,在某些数据库中,NULL 不会被索引,因此对 NULL 的任何测试都需要进行表扫描。不确定 PostgreSQL 9.1 关于索引中的 NULL 值。

于 2013-02-05T18:39:21.017 回答