【问题标题】:Slow count while inner joining tables in PostgreSQL在 PostgreSQL 中内部连接表时计数缓慢
【发布时间】:2016-06-24 01:06:52
【问题描述】:

我正在使用 postgres 9.4。我跑过VACUUMANALYZE。但是对inner join 的查询仍然很慢。

举个简单的例子,我有 3 个表:numbersalebase_numbernumberstorethroughnumber_idnumbersalenumberstorethrough 中只是 FK(numbersale.number_id 指向 base_numbernumberstorethrough.number_id 指向 numbersale,是的,这是可怕的命名):

                                                           Table "public.numbersale"
        Column        |           Type           |                           Modifiers                           | Storage  | Stats target | Description 
----------------------+--------------------------+---------------------------------------------------------------+----------+--------------+-------------
 id                   | integer                  | not null default nextval('numbersale_id_seq'::regclass)       | plain    |              | 
 number_id            | integer                  | not null                                                      | plain    |              | 


                                                        Table "public.base_number"
   Column    |           Type           |                        Modifiers                         | Storage  | Stats target | Description 
-------------+--------------------------+----------------------------------------------------------+----------+--------------+-------------
 id          | integer                  | not null default nextval('base_number_id_seq'::regclass) | plain    |              | 


                                                        Table "public.numberstorethrough"
    Column    |           Type           |                               Modifiers                               | Storage | Stats target | Description 
--------------+--------------------------+-----------------------------------------------------------------------+---------+--------------+-------------
 id           | integer                  | not null default nextval('numberstorethrough_id_seq'::regclass)       | plain   |              | 
 number_id    | integer                  | not null                                                              | plain   |              | 

其中包含 250k 到 595k 个条目:

$ SELECT COUNT(*) FROM numbersale;
 count  
--------
 258552
(1 row)

Time: 17,845 ms

$ SELECT COUNT(*) FROM base_number;
 count  
--------
 332484
(1 row)

Time: 16,273 ms

$ SELECT COUNT(*) FROM numberstorethrough;
 count  
--------
 595812
(1 row)

Time: 56,710 ms

并且表有对应的索引:

$ select * from pg_indexes where tablename = 'numbersale';
 schemaname |    tablename     |                 indexname                  | tablespace |                                                              indexdef                                                              
------------+------------------+--------------------------------------------+------------+------------------------------------------------------------------------------------------------------------------------------------
...
public      | numbersale       | numbersale_number_id_key             |            | CREATE UNIQUE INDEX numbersale_number_id_key ON numbersale USING btree (number_id)


$ select * from pg_indexes where tablename = 'numberstorethrough';
 schemaname |        tablename         |               indexname               | tablespace |                                                               indexdef                                                               
------------+--------------------------+---------------------------------------+------------+--------------------------------------------------------------------------------------------------------------------------------------
 public     | numberstorethrough       | numberstorethrough_number_id    |            | CREATE INDEX numberstorethrough_number_id ON numberstorethrough USING btree (number_id)

我的问题是以下查询:

SELECT COUNT(*) FROM "numbersale"
INNER JOIN "base_number"
   ON ( "numbersale"."number_id" = "base_number"."id" )
INNER JOIN "numberstorethrough"
  ON ( "numbersale"."id" = "numberstorethrough"."number_id" );
 count  
 --------
 595812
(1 row)

Time: 541,523 ms

解释那个查询:

Aggregate  (cost=62564.67..62564.68 rows=1 width=0)
  ->  Hash Join  (cost=34443.31..61075.14 rows=595812 width=0)
        Hash Cond: (numberstorethrough.number_id = numbersale.id)
        ->  Seq Scan on numberstorethrough  (cost=0.00..10539.12 rows=595812 width=4)
        ->  Hash  (cost=30201.41..30201.41 rows=258552 width=4)
              ->  Hash Join  (cost=14411.42..30201.41 rows=258552 width=4)
                    Hash Cond: (base_number.id = numbersale.number_id)
                    ->  Seq Scan on base_number  (cost=0.00..7102.84 rows=332484 width=4)
                    ->  Hash  (cost=10169.52..10169.52 rows=258552 width=8)
                          ->  Seq Scan on numbersale  (cost=0.00..10169.52 rows=258552 width=8)

这种带有两个内连接的基本查询需要半秒以上(有时需要长达 700 毫秒)是否正常?行数甚至不是数百万,它只是 300-600k。

我已经简化了我的查询,实际上它更大并且需要超过 1 秒,但连接问题是我的主要瓶颈。

【问题讨论】:

  • 您的第一个连接条件不正确,因为未定义"number"
  • 抱歉,我尝试改名,但出现了一些拼写错误。 number 应该是 base_number。对于给您带来的不便,我深表歉意!
  • SELECT COUNT... 的表现通常比您预期的要差得多。您真的需要行数,还是只是想确定是否有任何行与查询条件匹配?如果你使用SELECT *... 而不是SELECT COUNT(*)...,性能会怎样?您的查询正在对所有三个表进行顺序扫描 - 在我看来,令人惊奇的不是它需要 500 到 700 毫秒,而是它需要 500 到 700 毫秒。 YMMV。
  • @BobJarvis 只尝试了SELECT *explain analyze。性能上没有任何收获。我真的不需要所有这些行的精确计数,这是我的分页行为——也许,我应该找到一些解决方法。我想知道,为什么索引在这种情况下不起作用。

标签: sql postgresql count database-performance


【解决方案1】:

一种可能性是连接产生了一个非常大的中间结果,但随后被第二个连接过滤掉了。这仍然不能解释为什么没有使用索引,但也许这可能有更好的性能:

SELECT COUNT(*)
FROM "base_number" bn
WHERE EXISTS (SELECT 1 FROM "numbersale" ns WHERE ns."number_id" = bn."id") AND
      EXISTS (SELECT 1 FROM "numberstorethrough" nst WHERE bn."id" = nst."number_id");

您已经为此(以及您的原始)查询拥有正确的索引:numbersale(number_id)base_number(id)numberstorethrough(number_id)

【讨论】:

  • 但是我已经有了索引。查看我从pg_indexes-table 的输出。
  • @prokaktus 。 . .我大大修改了答案。
  • 感谢您的回答!但我在numberstorethrough 中的number_id 字段指向numbersale,而不是base_number。不幸的是,这是一个有点可怕的命名。而我的numberstorethrough是m2m-table,里面可以包含多个number_id。因为selectexists 提供的功能不同。我将编辑答案以使其更加明确。
【解决方案2】:

我对查询计划的最佳解释是不使用索引,因为查询中没有任何内容限制应获取的行。正在读取每个表中的每一行。那么在这种情况下使用索引有什么好处呢?数据库必须花时间读取索引,然后仍然必须读取“真实”数据。没有收获。如果有一个 WHERE 子句限制了查询返回的内容,或者如果连接条件限制了检索的行数,我希望索引可能会发挥作用。索引并不神奇——它们的使用不会自动“更好”,它们不在查询计划中也不会自动“更糟”。与许多事情一样,它取决于:-) - 在这种情况下,从查询优化器的角度来看,使用一个或多个索引似乎是一个净损失。还要记住,COUNT 并不神奇——它是一个简单地计算结果集中行数的函数。它并不神奇地“知道”结果集中预计有多少行 - 数据库必须生成结果集,然后COUNT 只是计算有多少行。

【讨论】:

  • 没关系,有时数据库不使用索引,但在我的情况下,使用连接读取比单独读取大多数较大的表慢 10 倍。我不指望魔术,但这样的性能差异对我来说看起来非常令人生畏。但无论如何,感谢您的时间和回答!
猜你喜欢
  • 2012-12-09
  • 1970-01-01
  • 1970-01-01
  • 2019-12-18
  • 1970-01-01
  • 2014-07-07
  • 2012-02-22
  • 1970-01-01
  • 2021-02-15
相关资源
最近更新 更多