Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/database/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Database postgresql 9.6.4:大表上的时间戳范围查询永远耗时_Database_Postgresql_Database Performance - Fatal编程技术网

Database postgresql 9.6.4:大表上的时间戳范围查询永远耗时

Database postgresql 9.6.4:大表上的时间戳范围查询永远耗时,database,postgresql,database-performance,Database,Postgresql,Database Performance,我需要一些帮助来分析在一个包含83660.142万行的大表上执行的查询的不良性能,根据系统负载的不同,该查询需要25分钟到一个多小时的计算时间 我创建了下表,该表由一个复合键和3个索引组成: CREATE TABLE IF NOT EXISTS ds1records( userid INT DEFAULT 0, clientid VARCHAR(255) DEFAULT '', ts TIMESTAMP, site VARCHAR(50) DEFAULT '', code VARCHA

我需要一些帮助来分析在一个包含83660.142万行的大表上执行的查询的不良性能,根据系统负载的不同,该查询需要25分钟到一个多小时的计算时间

我创建了下表,该表由一个复合键和3个索引组成:

CREATE TABLE IF NOT EXISTS ds1records(
 userid INT DEFAULT 0,
 clientid VARCHAR(255) DEFAULT '',
 ts TIMESTAMP,
 site VARCHAR(50) DEFAULT '',
 code VARCHAR(400) DEFAULT '');

CREATE UNIQUE INDEX IF NOT EXISTS primary_idx ON records (userid, clientid, ts, site, code);
CREATE INDEX IF NOT EXISTS userid_idx ON records (userid);
CREATE INDEX IF NOT EXISTS ts_idx ON records (ts);
CREATE INDEX IF NOT EXISTS userid_ts_idx ON records (userid ASC,ts DESC);   
在spring批处理应用程序中,我执行的查询如下所示:

SELECT * 
  FROM records 
 WHERE userid = ANY(VALUES (2), ..., (96158 more userids) ) 
  AND ( ts < '2017-09-02' AND ts >= '2017-09-01' 
        OR ts < '2017-08-26' AND ts >= '2017-08-25' 
        OR ts < '2017-08-19' AND ts >= '2017-08-18' 
        OR ts < '2017-08-12' AND ts >= '2017-08-11') 
我已经调整了一些Postgres调优参数的值(不幸的是没有成功):

  • 有效_缓存_大小=15GB(可能没用,因为查询只执行一次)
  • 共享缓冲区=15GB
  • 工作记忆=3GB
该应用程序运行计算代价高昂的任务(例如数据融合/数据注入),并消耗大约100GB的内存,因此系统硬件的尺寸足够大,具有125GB的RAM和16个内核(OS:Debian)

我想知道为什么postgres在其执行计划中没有使用组合索引
userid\u ts\u idx
?由于索引中的时间戳列按相反顺序排序,我希望postgres使用它来查找查询范围部分的匹配元组,因为它可以依次遍历索引,直到条件
ts<'2017-09-02 00:00:00
保持为真,并返回所有值,直到条件
ts>=2017-09-01满足00:00:00
。相反,postgres使用昂贵的位图堆扫描,如果我理解正确,它会执行线性表扫描。我是否误解了db设置,或者我是否有概念上的误解

更新

不幸的是,评论中建议的CTE没有带来任何改进。位图堆扫描已被顺序扫描取代,但性能仍然很差。以下是更新后的执行计划:

Merge Join  (cost=20564929.37..20575876.60 rows=685277 width=106) (actual time=2218133.229..2222280.192 rows=3907472 loops=1)
  Merge Cond: (ids.id = r.userid)
  Buffers: shared hit=2408684 read=181785
  CTE ids
    ->  Values Scan on "*VALUES*"  (cost=0.00..1289.70 rows=103176 width=4) (actual time=0.002..28.670 rows=103176 loops=1)
  CTE ts
    ->  Values Scan on "*VALUES*_1"  (cost=0.00..0.05 rows=4 width=32) (actual time=0.002..0.004 rows=4 loops=1)
  ->  Sort  (cost=10655.37..10913.31 rows=103176 width=4) (actual time=68.476..83.312 rows=103176 loops=1)
    Sort Key: ids.id
    Sort Method: quicksort  Memory: 7909kB
    ->  CTE Scan on ids  (cost=0.00..2063.52 rows=103176 width=4) (actual time=0.007..47.868 rows=103176 loops=1)
  ->  Sort  (cost=20552984.25..20554773.54 rows=715717 width=102) (actual time=2218059.941..2221230.585 rows=8085760 loops=1)
    Sort Key: r.userid
    Sort Method: quicksort  Memory: 1410084kB
    Buffers: shared hit=2408684 read=181785
    ->  Nested Loop  (cost=0.00..20483384.24 rows=715717 width=102) (actual time=885849.043..2214665.723 rows=8085767 loops=1)
          Join Filter: (ts.r @> r.ts)
          Rows Removed by Join Filter: 707630821
          Buffers: shared hit=2408684 read=181785
          ->  Seq Scan on records r  (cost=0.00..4379760.52 rows=178929152 width=70) (actual time=0.024..645616.135 rows=178929147 loops=1)
                Buffers: shared hit=2408684 read=181785
          ->  CTE Scan on ts  (cost=0.00..0.08 rows=4 width=32) (actual time=0.000..0.000 rows=4 loops=178929147)
Planning time: 126.110 ms
Execution time: 2222514.566 ms

若您将时间戳转换为日期并按值列表过滤,那个么您应该得到不同的计划

CREATE INDEX IF NOT EXISTS userid_ts_idx ON records (userid ASC,cast(ts AS date) DESC);

SELECT *
  FROM records
 WHERE userid = ANY(VALUES (2), ..., (96158 more userids) )
  AND cast(ts AS date) IN('2017-09-01','2017-08-25','2017-08-18','2017-08-11');
它是否会更好地执行取决于您的数据和日期范围,因为在我的案例中,我发现Postgres将继续使用该索引,即使日期值覆盖了整个表(所以序列扫描会更好)


它是否有可能受到磁盘读取的限制?下次请使用
解释(分析、缓冲)
。这将使您对缓冲有一些了解。正如我们所看到的,堆扫描占用了大部分时间。在这组日期范围内有几个OR,我认为它不会
保持为真
最晚的日期向后移动到最早的给定日期。过滤器指示它处理每个范围
(…或…)和(…或…)和(…或…)以及(…或…)和(…或…)
几个快速点。你真的是说83660142万行吗?与83x10^12行相同?还是只有8300万行?差别很大。第二,如果您的返回集很大,并且得到任何类型的排序等,它将溢出到磁盘。默认情况下,溢出到存储数据的同一磁盘。将临时表空间移到其他地方会有很大的不同,更大的work\u mem(在一定程度上)是否有机会将
Any(值(2),…,(96158个用户ID))
放入子查询中?同样,如果没有正确的括号,TS过滤器和运算符将完全不执行任何操作。。。它应该是
和((ts=…)或(ts=…)…)
类似的内容:将时间戳转换为日期不是一个选项,因为数据是基于小时粒度的。这个查询将过滤掉大量的结果集。那么你真的这样做了吗<代码>ts<'2017-08-12 05:00:00'和ts>='2017-08-11 13:00:00'对不起,我不知道('2017-09-01'、'2017-08-25'、'2017-08-18'、'2017-08-11')中的此部分
在范围内过滤。我将测试你的解决方案。这只是因为时间戳在这种情况下被转换为日期,所以基本上它去掉了时间部分,只比较日期。卢卡斯你是个天才!!!它在大约11分钟内执行了查询,是的:),正如您所说,它现在正在运行索引扫描!非常感谢你
CREATE INDEX IF NOT EXISTS userid_ts_idx ON records (userid ASC,cast(ts AS date) DESC);

SELECT *
  FROM records
 WHERE userid = ANY(VALUES (2), ..., (96158 more userids) )
  AND cast(ts AS date) IN('2017-09-01','2017-08-25','2017-08-18','2017-08-11');