PostgreSQL简单JOIN非常慢

我有一个简单的查询和两个表:

drilldown

CREATE SEQUENCE drilldown_id_seq;

CREATE TABLE drilldown (
    transactionid bigint NOT NULL DEFAULT nextval('drilldown_id_seq'),
    userid bigint NOT NULL default 0 REFERENCES users(id),
    pathid bigint NOT NULL default 0,
    reqms bigint NOT NULL default 0,
    quems bigint NOT NULL default 0,
    clicktime timestamp default current_timestamp,
    PRIMARY KEY(transactionid)
);

ALTER SEQUENCE drilldown_id_seq OWNED BY drilldown.transactionid;

CREATE INDEX drilldown_idx1 ON drilldown (clicktime);

querystats

CREATE SEQUENCE querystats_id_seq;

CREATE TABLE querystats (
    id bigint NOT NULL DEFAULT nextval('querystats_id_seq'),
    transactionid bigint NOT NULL default 0 REFERENCES drilldown(transactionid),
    querynameid bigint NOT NULL default 0 REFERENCES queryname(id),
    queryms bigint NOT NULL default 0,
    PRIMARY KEY(id)
);

ALTER SEQUENCE querystats_id_seq OWNED BY querystats.id;

CREATE INDEX querystats_idx1 ON querystats (transactionid);
CREATE INDEX querystats_idx2 ON querystats (querynameid);

drilldown querystats有150万条记录, querystats有1000万条记录; 当我加入两者之间时,问题就会发生。

QUERY

explain analyse
select avg(qs.queryms)
  from querystats qs
  join drilldown d on (qs.transactionid=d.transactionid)
  where querynameid=1;

QUERY PLAN

Aggregate  (cost=528596.96..528596.97 rows=1 width=8) (actual time=5213.154..5213.154 rows=1 loops=1)
   ->  Hash Join  (cost=274072.53..518367.59 rows=4091746 width=8) (actual time=844.087..3528.788 rows=4117717 loops=1)
         Hash Cond: (qs.transactionid = d.transactionid)
         ->  Bitmap Heap Scan on querystats qs  (cost=88732.62..210990.44 rows=4091746 width=16) (actual time=309.502..1321.029 rows=4117717 loops=1)
               Recheck Cond: (querynameid = 1)
               ->  Bitmap Index Scan on querystats_idx2  (cost=0.00..87709.68 rows=4091746 width=0) (actual time=307.916..307.916 rows=4117718 loops=1)
                     Index Cond: (querynameid = 1)
         ->  Hash  (cost=162842.29..162842.29 rows=1371250 width=8) (actual time=534.065..534.065 rows=1372574 loops=1)
               Buckets: 4096  Batches: 64  Memory Usage: 850kB
               ->  Index Scan using drilldown_pkey on drilldown d  (cost=0.00..162842.29 rows=1371250 width=8) (actual time=0.015..364.657 rows=1372574 loops=1)
 Total runtime: 5213.205 ms
(11 rows)

我知道我可以为PostgreSQL调整一些调整参数,但是我想知道的是查询我正在做最合适的两个表的连接方式?

或者可能是某种内部联接? 我只是不确定。

任何指针都表示赞赏!

编辑

database#d drilldown
                                       Table "public.drilldown"
    Column     |            Type             |                       Modifiers                        
---------------+-----------------------------+--------------------------------------------------------
 transactionid | bigint                      | not null default nextval('drilldown_id_seq'::regclass)
 userid        | bigint                      | not null default 0
 pathid        | bigint                      | not null default 0
 reqms         | bigint                      | not null default 0
 quems         | bigint                      | not null default 0
 clicktime     | timestamp without time zone | default now()
Indexes:
    "drilldown_pkey" PRIMARY KEY, btree (transactionid)
    "drilldown_idx1" btree (clicktime)
Foreign-key constraints:
    "drilldown_userid_fkey" FOREIGN KEY (userid) REFERENCES users(id)
Referenced by:
    TABLE "querystats" CONSTRAINT "querystats_transactionid_fkey" FOREIGN KEY (transactionid) REFERENCES drilldown(transactionid)

database=# d querystats
                            Table "public.querystats"
    Column     |  Type  |                        Modifiers                        
---------------+--------+---------------------------------------------------------
 id            | bigint | not null default nextval('querystats_id_seq'::regclass)
 transactionid | bigint | not null default 0
 querynameid   | bigint | not null default 0
 queryms       | bigint | not null default 0
Indexes:
    "querystats_pkey" PRIMARY KEY, btree (id)
    "querystats_idx1" btree (transactionid)
    "querystats_idx2" btree (querynameid)
Foreign-key constraints:
    "querystats_querynameid_fkey" FOREIGN KEY (querynameid) REFERENCES queryname(id)
    "querystats_transactionid_fkey" FOREIGN KEY (transactionid) REFERENCES drilldown(transactionid)

所以这里是要求的两个表格和版本

PostgreSQL 9.1.7 on x86_64-unknown-linux-gnu, compiled by gcc (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3, 64-bit

那么这个查询所做的是从每个查询类型的queryms的所有行值中获取平均值(querynameid)

            name            |         current_setting          |        source        
----------------------------+----------------------------------+----------------------
 application_name           | psql                             | client
 client_encoding            | UTF8                             | client
 DateStyle                  | ISO, MDY                         | configuration file
 default_text_search_config | pg_catalog.english               | configuration file
 enable_seqscan             | off                              | session
 external_pid_file          | /var/run/postgresql/9.1-main.pid | configuration file
 lc_messages                | en_US.UTF-8                      | configuration file
 lc_monetary                | en_US.UTF-8                      | configuration file
 lc_numeric                 | en_US.UTF-8                      | configuration file
 lc_time                    | en_US.UTF-8                      | configuration file
 log_line_prefix            | %t                               | configuration file
 log_timezone               | localtime                        | environment variable
 max_connections            | 100                              | configuration file
 max_stack_depth            | 2MB                              | environment variable
 port                       | 5432                             | configuration file
 shared_buffers             | 24MB                             | configuration file
 ssl                        | on                               | configuration file
 TimeZone                   | localtime                        | environment variable
 unix_socket_directory      | /var/run/postgresql              | configuration file
(19 rows)

我看到enable_seqscan = off,我没有触及任何设置,这是一个完全默认安装。

UPDATE

我从下面的评论做了一些改变,这里是结果。

explain analyse SELECT (SELECT avg(queryms) AS total FROM querystats WHERE querynameid=3) as total FROM querystats qs JOIN drilldown d ON (qs.transactionid=d.transactionid) WHERE qs.querynameid=3 limit 1;
                                                                       QUERY PLAN                                                                        
---------------------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=196775.99..196776.37 rows=1 width=0) (actual time=2320.876..2320.876 rows=1 loops=1)
   InitPlan 1 (returns $0)
     ->  Aggregate  (cost=196775.94..196775.99 rows=1 width=8) (actual time=2320.815..2320.815 rows=1 loops=1)
           ->  Bitmap Heap Scan on querystats  (cost=24354.25..189291.69 rows=2993698 width=8) (actual time=226.516..1144.690 rows=2999798 loops=1)
                 Recheck Cond: (querynameid = 3)
                 ->  Bitmap Index Scan on querystats_idx  (cost=0.00..23605.83 rows=2993698 width=0) (actual time=225.119..225.119 rows=2999798 loops=1)
                       Index Cond: (querynameid = 3)
   ->  Nested Loop  (cost=0.00..1127817.12 rows=2993698 width=0) (actual time=2320.876..2320.876 rows=1 loops=1)
         ->  Seq Scan on drilldown d  (cost=0.00..76745.10 rows=1498798 width=8) (actual time=0.009..0.009 rows=1 loops=1)
         ->  Index Scan using querystats_idx on querystats qs  (cost=0.00..0.60 rows=2 width=8) (actual time=0.045..0.045 rows=1 loops=1)
               Index Cond: ((querynameid = 3) AND (transactionid = d.transactionid))
 Total runtime: 2320.940 ms
(12 rows)

它的行为就像您已设置enable_seqscan = off ,因为它使用索引扫描来填充散列表。 除非作为诊断步骤,否则绝对不要设置任何计划器选项,并且如果您要显示计划,请显示使用的任何选项。 这可以运行以显示很多有用的信息:

SELECT version();
SELECT name, current_setting(name), source
  FROM pg_settings
  WHERE source NOT IN ('default', 'override');

如果您告诉我们运行时环境,特别是计算机上的RAM数量,存储系统的外观和数据库的大小(或者甚至更好,数据库中频繁引用的数据的活动数据集) )。

作为粗略分解,5.2秒分解为:

  • 1.3秒查找符合您的选择标准的4,117,717个querystats行。
  • 2.3秒来随机匹配drilldown记录。
  • 1.6秒通过4,117,717行并计算平均值。
  • 因此,尽管您似乎已经削弱了其使用最快计划的能力,但它仅需1.26微秒(百万分之一秒)来定位每行,将其加入另一行,并将其计算为平均值。 从绝对的角度来看,这并不算太坏,但你几乎可以肯定会得到一个稍微快一点的计划。

    首先,如果您使用的是9.2.x,其中x小于3,请立即升级到9.2.3。 对于某些类型的计划进行了性能回归,这些计划在最近的版本中已修复,可能会影响此查询。 一般来说,尽量在次要版本上保持最新版本(版本号在第二个点之后更改)。

    您可以通过在该连接上设置计划因素并运行查询(或对其进行EXPLAIN )来在单个会话中测试不同的计划。 尝试这样的事情:

    SET seq_page_cost = 0.1;
    SET random_page_cost = 0.1;
    SET cpu_tuple_cost = 0.05;
    SET effective_cache_size = '3GB'; -- actually use shared_buffers plus OS cache
    

    确保所有的enable_设置都on


    当你不加入时, avg(qs.queryms)执行一次。

    当您执行连接时,您执行avg(qs.queryms)次数与连接生成的行次数相同。

    如果您始终对单个查询名称感兴趣,请尝试将avg(qs.queryms)放入子查询中:

    SELECT 
        (SELECT avg(queryms) FROM querystats WHERE querynameid=1) 
    FROM querystats qs 
    JOIN drilldown d ON (qs.transactionid=d.transactionid) 
    WHERE qs.querynameid=1;
    

    你在你的问题中声称:

    我看到enable_seqscan = off,我没有触及任何设置,这是一个完全默认安装。

    相比之下, pg_settings的输出告诉我们:

    enable_seqscan | 关闭| 会议

    这意味着你在会话中设置了enable_seqscan = off 。 这里没有东西加起来。

    SET enable_seqscan = on;
    

    要么

    RESET enable_seqscan;
    

    断言:

    SHOW enable_seqscan;
    

    另外,对于拥有数百万条记录的db,您对shared_buffers的设置太低。 24MB似乎是Ubuntu开箱即用的保守设置。 您需要编辑您的配置文件以供认真使用! 我引用手册:

    如果您拥有1GB或更多内存的专用数据库服务器,shared_buffers的合理起始值是系统内存的25%。

    所以编辑你的postgresql.conf文件来增加值并重新加载。
    然后再次尝试查询,并了解enable_seqscan如何关闭。

    链接地址: http://www.djcxy.com/p/86067.html

    上一篇: PostgreSQL Simple JOIN very slow

    下一篇: With Android, when i use the back key from a web view a white screen appears