慢慢查询索引未被使用的情况

时间:2014-06-30 18:58:35

标签: postgresql indexing

概述

我一直致力于Netdot尝试加速一些查询。由于不需要连接或广泛搜索,其中一些可以从SQL更改中受益,但有些已经证明难以追踪。

在这个特殊情况下,我有两张桌子。 fwtableentry有132,233,684行。 fwtable有2,178,088行。

硬件/软件版本

这是一个运行debian_version 7.5(wheezy)的虚拟机。磁盘位于SAN上,raid为0 + 1。这台机器分配了4GB的RAM。 I / O和内存似乎不是问题,但如果需要,我可以分配更多资源。

Linux netdot 3.2.0-4-amd64 #1 SMP Debian 3.2.57-3+deb7u2 x86_64 GNU/Linux

运行Postgres 9.1.13-0wheezy1(debian包版本)

Sysctl选项

kernel.shmmax = 1500000000
vm.overcommit_memory = 2

Postgres选项

我从默认开始。我现在修改了它们,引用了我之前根据postgres调优文档调整过的另一台服务器。这些变化似乎不会有助于缓慢的查询,但可能有助于其他事情。

shared_buffers = 1GB            # min 128kB
work_mem = 64MB             # min 64kB
maintenance_work_mem = 256MB        # min 1MB
wal_buffers = 16MB          # min 32kB, -1 sets based on shared_buffers
checkpoint_segments = 32        # in logfile segments, min 1, 16MB each
checkpoint_timeout = 10min      # range 30s-1h
checkpoint_completion_target = 0.9  # checkpoint target duration, 0.0 - 1.0
random_page_cost = 2.5          # same scale as above
effective_cache_size = 2GB

解释显示问题

netdot=# explain analyze SELECT   ft.tstamp
              FROM     fwtableentry fte, fwtable ft
              WHERE    fte.physaddr=9115
                AND    fte.fwtable=ft.id
              GROUP BY ft.tstamp
              ORDER BY ft.tstamp DESC
              LIMIT 10
;
                                                                    QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=53610.80..53610.82 rows=10 width=8) (actual time=27436.502..27436.631 rows=10 loops=1)
   ->  Sort  (cost=53610.80..53617.92 rows=2849 width=8) (actual time=27436.220..27436.258 rows=10 loops=1)
         Sort Key: ft.tstamp
         Sort Method: top-N heapsort  Memory: 25kB
         ->  HashAggregate  (cost=53520.74..53549.23 rows=2849 width=8) (actual time=27417.749..27425.805 rows=2876 loops=1)
                ->  Nested Loop  (cost=125.79..53500.91 rows=7933 width=8) (actual time=98.801..27367.988 rows=3562 loops=1)
                      ->  Bitmap Heap Scan on fwtableentry fte  (cost=125.79..18909.68 rows=7933 width=8) (actual time=97.718..26942.693 rows=3562 loops=1)
                            Recheck Cond: (physaddr = 9115)
                           ->  Bitmap Index Scan on "FWTableEntry3"  (cost=0.00..123.81 rows=7933 width=0) (actual time=86.433..86.433 rows=3562 loops=1)
                                 Index Cond: (physaddr = 9115)
                      ->  Index Scan using pk_fwtable on fwtable ft  (cost=0.00..4.35 rows=1 width=16) (actual time=0.069..0.077 rows=1 loops=3562)
                            Index Cond: (id = fte.fwtable)
 Total runtime: 27449.802 ms

以下是两个表

fwtable

netdot=# \d fwtable
                                           Table "public.fwtable"
 Column |            Type             |                              Modifiers
 --------+-----------------------------+---------------------------------------------------------------------
  device | bigint                      | not null
  id     | bigint                      | not null default nextval('fwtable_id_seq'::regclass)
  tstamp | timestamp without time zone | not null default '1970-01-02 00:00:01'::timestamp without time zone
Indexes:
    "pk_fwtable" PRIMARY KEY, btree (id)
    "fwtable1" UNIQUE CONSTRAINT, btree (device, tstamp)
    "FWTable2" btree (device)
    "FWTable3" btree (tstamp)
Foreign-key constraints:
     "fk_device" FOREIGN KEY (device) REFERENCES device(id) DEFERRABLE
Referenced by:
     TABLE "fwtableentry" CONSTRAINT "fk_fwtable" FOREIGN KEY (fwtable) REFERENCES fwtable(id) DEFERRABLE

fwtableentry

netdot=# \d fwtableentry
                          Table "public.fwtableentry"
  Column   |  Type  |                         Modifiers
-----------+--------+-----------------------------------------------------------
 fwtable   | bigint | not null
 id        | bigint | not null default nextval('fwtableentry_id_seq'::regclass)
 interface | bigint | not null
 physaddr  | bigint | not null
Indexes:
    "pk_fwtableentry" PRIMARY KEY, btree (id)
    "FWTableEntry1" btree (fwtable)
    "FWTableEntry2" btree (interface)
    "FWTableEntry3" btree (physaddr)
Foreign-key constraints:
    "fk_fwtable" FOREIGN KEY (fwtable) REFERENCES fwtable(id) DEFERRABLE
    "fk_interface" FOREIGN KEY (interface) REFERENCES interface(id) DEFERRABLE
    "fk_physaddr" FOREIGN KEY (physaddr) REFERENCES physaddr(id) DEFERRABLE

以下是两个表

的示例

第一个fwtableentry:

 fwtable |    id     | interface | physaddr
---------+-----------+-----------+----------
  675157 |  39733332 |     29577 |     9115
  674352 |  39686929 |     29577 |     9115
     344 |     19298 |     29577 |     9115
    1198 |     68328 |     29577 |     9115
    1542 |     88107 |     29577 |     9115
  675960 |  39779466 |     29577 |     9115
  675750 |  39766468 |     39946 |     9115
    2994 |    168721 |     29577 |     9115
    3895 |    218228 |     29577 |     9115
    4795 |    267949 |     29577 |     9115
    5695 |    324905 |     29577 |     9115
  674944 |  39720652 |     39946 |     9115
    6595 |    375149 |     29577 |     9115
    7501 |    425045 |     29577 |     9115
    8400 |    475265 |     29577 |     9115
    9298 |    524985 |     29577 |     9115
   10200 |    575136 |     29577 |     9115
   11104 |    626065 |     29577 |     9115
   12011 |    677963 |     29577 |     9115
  676580 |  39814792 |     39946 |     9115
   12914 |    731390 |     29577 |     9115
  677568 |  39871297 |     29577 |     9115
   13821 |    784435 |     29577 |     9115
  676760 |  39825496 |     29577 |     9115

fwtable(减去设备列):

   id    |       tstamp
---------+---------------------
 2178063 | 2014-06-10 17:00:13
 2177442 | 2014-06-10 16:00:06
 2176816 | 2014-06-10 15:00:07
 2176190 | 2014-06-10 14:00:09
 2175566 | 2014-06-10 13:00:07
 2174941 | 2014-06-10 12:00:07
 2174316 | 2014-06-10 11:00:07
 2173689 | 2014-06-10 10:00:06
 2173065 | 2014-06-10 09:00:06
 2172444 | 2014-06-10 08:00:06
(10 rows)

据我所知,问题

所以,问题是你需要知道要发送到fwtable的id,但是你不能知道哪些匹配10个最新的时间戳,所以你需要将它们全部发送,然后让fwtable上的索引确定哪个扔掉的人。

netdot=# select count(*) from fwtableentry where physaddr = 9115;
 count
-------
  3562
(1 row)

这也是一旦缓存,查询速度很快的原因。连接的数据集并不大,所以一旦知道该怎么做就可以缓存所需的一切。

您可能会问为什么不选择最新的10个时间戳并与之匹配,但问题是这些时间戳可能与该physaddr没有任何结果,因此您需要检查连接的结果。

重写查询

select ft.tstamp from fwtable ft where ft.id in (select fwtable from fwtableentry where physaddr = 9115) order by ft.tstamp desc limit 10;

仍然提供相同的查询计划,但让我更容易想象出问题。实际上,即使您放弃排序,以这种方式排序查询也会强制使用计划。

你会认为fwtable(id,tstamp DESC)上的索引可能会有所帮助,但它似乎没有被使用。我可以看到任何索引都会被混淆,因为它从各处获取了大量结果。

我认为告诉数据库id和tstamp之间的关系是1:1会有所帮助,所以我为这两者添加了一个唯一的索引。它没有。

放弃限制并不会影响计划。它只是那种杀死表演的那种。

缺少具有三个所需列的物化视图(由于表格大小我认为这是不切实际的。)我不确定如何解决这个问题,但我可能只是不够智能SQL意识到真正的问题。

最后的笔记

您可以在第一个查询中删除GROUP BY,这是不需要的。我从这个例子中不需要的几个表中删除了几个表。它们不会像这个问题一样影响查询速度,并且一般不需要这样做,所以我可能会重写代码以永久地将它们排除在外。

我还应该提到,对模式的重大改变并不是我能做的事情。如果它是问题的唯一解决办法,我或许可以提出它作为最后的措施,但Netdot不是我的计划,所以我不知道我可以改变多少以修复可能有用的东西我比其他人更多。

2 个答案:

答案 0 :(得分:0)

如果您也可以按时间戳约束查询,那么您可能会发现(tstamp,physaddr)上的索引有用*。这是一个关于过去30天内"前10名的查询问题"是可以接受的就目前而言,我并不认为规划师可以做任何更聪明的事情,没有理由认为physaddr值会出现在任何地方。

*或者(physaddr,tstamp) - 它将取决于值的分布。

答案 1 :(得分:0)

尝试重新加入exists(...)而不是IN(...)加上删除(不需要的)GROUP BY;这可能会避免聚合

SELECT ft.tstamp
FROM  fwtable ft
WHERE EXISTS(
    SELECT *
    FROM  fwtableentry fte
    WHERE fte.physaddr=9115
    AND   fte.fwtable=ft.id
    )
-- GROUP BY ft.tstamp -- you don't need this
ORDER BY ft.tstamp DESC
LIMIT 10 -- LIMIT could kill your performance...
;