PostgreSQL:在具有数百万行的表中加速 SELECT 查询

标签 postgresql performance select

我有一个超过 450 万行的表,而我的 SELECT 查询对于我的需求来说太慢了。

创建表时使用:

CREATE TABLE all_legs (
                carrier TEXT,
                dep_hub TEXT,
                arr_hub TEXT,
                dep_dt TIMESTAMP WITH TIME ZONE,
                arr_dt TIMESTAMP WITH TIME ZONE,
                price_ct INTEGER,
                ... 5 more cols ...,
                PRIMARY KEY (carrier, dep_hub, arr_hub, dep_dt, arr_dt, ...3 other cols...)
                )

当我想对某个日期的所有行进行 SELECT 时,查询速度太慢;它需要 12 秒到 20 秒。我的目标是最多需要 1 秒。我希望查询返回表中包含的行的 0.1% 到 1%。

查询非常简单:

SELECT * FROM all_legs WHERE dep_dt::date = '2017-08-15' ORDER BY price_ct ASC

EXPLAIN ANALYZE 返回:

Sort  (cost=197154.69..197212.14 rows=22982 width=696) (actual time=14857.300..14890.565 rows=31074 loops=1)
  Sort Key: price_ct
  Sort Method: external merge  Disk: 5256kB
  ->  Seq Scan on all_legs  (cost=0.00..188419.85 rows=22982 width=696) (actual time=196.738..14581.143 rows=31074 loops=1)
        Filter: ((dep_dt)::date = '2017-08-15'::date)
        Rows Removed by Filter: 4565249
Planning time: 0.572 ms
Execution time: 14908.274 ms

注意:我昨天才知道这个命令,所以我仍然不能完全理解返回的所有内容。

我已经按照 here 的建议尝试使用 index-only scans ,方法是运行命令:CREATE index idx_all_legs on all_legs(dep_dt); 但我没有注意到运行时间有任何差异。我还尝试为所有列创建索引,因为我希望所有列都返回。

另一个想法是按 dep_dt 对所有行进行排序,这样搜索满足条件的所有行应该会快得多,因为它们不会分散。不幸的是,我不知道如何实现它。

有没有办法让它像我的目标一样快?


解决方案

正如 Laurenz' answer 中所建议的,通过添加索引 CREATE INDEX IF NOT EXISTS idx_dep_dt_price ON all_legs(dep_dt, price_ct); 并将 SELECT 中的条件调整为 WHERE dep_dt >= '2017-08-15 00:00:00' AND dep_dt < '2017-08-16 00:00:00' 已将运行时间减少到 1/4。即使这是一个非常好的改进,这也意味着运行时间在 2 到 6 秒之间。

任何进一步减少运行时间的额外想法将不胜感激。

最佳答案

索引无济于事。

两种解决方案:

  1. 您可以将查询更改为:

    WHERE dep_dt >= '2017-08-15 00:00:00' AND dep_dt < '2017-08-16 00:00:00'
    

    然后就可以使用索引了。

  2. 在表达式上创建索引:

    CREATE INDEX ON all_legs(((dep_dt AT TIME ZONE 'UTC')::date));
    

    (或不同的时区)并将查询更改为

    WHERE (dep_dt AT TIME ZONE 'UTC')::date = '2017-08-16'
    

    AT TIME ZONE 是必需的,否则转换结果将取决于您当前的 TimeZone 设置。

第一个解决方案更简单,但第二个的优点是您可以像这样将 price_ct 添加到索引中:

CREATE INDEX ON all_legs(((dep_dt AT TIME ZONE 'UTC')::date), price_ct);

那么您就不再需要排序了,您的查询将达到理论上的最快速度。

关于PostgreSQL:在具有数百万行的表中加速 SELECT 查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45320455/

相关文章:

postgresql - 我成功创建的表丢失

mysql按逗号分隔值分组

matlab - 当赋值中的索引之间的映射不是单射时的向量化

performance - 架构和索引以及主键 : Differences in lookup performance?

c++ - 获取 sqrt(n) 整数部分的最快方法?

html - 在选择选项中有一个占位符

html - Angular 2 在条件下选择了选项

php - postgresql 与lastinsertid 和zend 框架的序列问题

java - 如何通过jdbc将数据从数据流加载到postgresql中

ruby-on-rails - 数据库错误 :Migrate "uninitialized constant DeviseCreateUsers"