为什么即使使用仅索引扫描

时间:2018-04-29 05:12:06

标签: sql query-performance postgresql-9.5 database-indexes covering-index

我有一个简单的计数查询,可以使用仅索引扫描,但在PostgresQL中仍然需要很长时间!

我有一个cars表,其中有两列type bigintactive boolean,我在这些列上也有一个多列索引

CREATE TABLE cars
(
id BIGSERIAL NOT NULL
    CONSTRAINT cars_pkey PRIMARY KEY ,
type BIGINT NOT NULL ,
name VARCHAR(500) NOT NULL ,
active            BOOLEAN DEFAULT TRUE NOT NULL,
created_at        TIMESTAMP(0) WITH TIME ZONE default NOW(),
updated_at        TIMESTAMP(0) WITH TIME ZONE default NOW(),
deleted_at        TIMESTAMP(0) WITH TIME ZONE
);
CREATE INDEX cars_type_active_index ON cars(type, active);

我插入了一些带有950k记录的测试数据,type = 1有600k记录

INSERT INTO cars (type, name) (SELECT 1, 'car-name' FROM generate_series(1,600000));
INSERT INTO cars (type, name) (SELECT 2, 'car-name' FROM generate_series(1,200000));
INSERT INTO cars (type, name) (SELECT 3, 'car-name' FROM generate_series(1,100000));
INSERT INTO cars (type, name) (SELECT 4, 'car-name' FROM generate_series(1,50000));

让我们运行VACUUM ANALYZE并强制PostgresQL使用仅索引扫描

VACUUM ANALYSE;
SET enable_seqscan = OFF;
SET enable_bitmapscan = OFF;

好的,我对typeactive

进行了简单查询
EXPLAIN (VERBOSE, BUFFERS, ANALYSE) 
SELECT count(*) 
FROM cars 
WHERE type = 1 AND active = true;

结果:

Aggregate  (cost=24805.70..24805.71 rows=1 width=0) (actual time=4460.915..4460.918 rows=1 loops=1)
Output: count(*)
Buffers: shared hit=2806
->  Index Only Scan using cars_type_active_index on public.cars (cost=0.42..23304.23 rows=600590 width=0) (actual time=0.051..2257.832 rows=600000 loops=1)
        Output: type, active
        Index Cond: ((cars.type = 1) AND (cars.active = true))
        Filter: cars.active
        Heap Fetches: 0
        Buffers: shared hit=2806
Planning time: 0.213 ms
Execution time: 4461.002 ms
(11 rows)

查看查询说明结果,

  • 它使用Index Only Scan,仅使用索引扫描,具体取决于visibilities map,PostgresQL有时需要获取表堆以检查元组的可见性,但我已经运行{{1}所以你可以看到VACUUM ANALYZE,所以阅读索引就足以回答这个问题。

  • 索引的大小非常小,它可以全部适合缓冲区缓存(Heap fetch = 0),PostgresQL不需要从磁盘中获取页面。

从那里开始,我无法理解为什么PostgresQL花了那么长时间(4.5s)来回答查询,1M记录不是大量记录,一切都已缓存在内存中,而索引上的数据是可见,它不需要获取堆。

在x86_64-pc-linux-gnu上的PostgreSQL 9.5.10,由gcc编译(Debian 4enter code here.9.2-10)4.9.2,64位

我在docker 17.09.1-ce,Macbook pro 2015上进行了测试。

我还是PostgresQL的新手,并尝试将我的知识与真实案例进行对比。 非常感谢,

1 个答案:

答案 0 :(得分:0)

似乎我找到了原因,这与PostgresQL问题无关,这是因为在docker中运行。当我直接在Mac中运行时,时间约为100毫秒,足够快。

我发现的另一件事是PostgresQL仍然使用seq scan而不是index only scan的原因(这就是为什么我必须在测试中禁用seq_scan和bitmapscan的原因):

  • 表的大小与索引的大小相比并没有那么大,如果我向表中添加更多的列,或者列的长度越长,表的大小越大,则可以使用更多的机会索引。 / li>
  • random_page_cost的默认值为4,我的磁盘非常快,因此我可以将其设置为1-2,这将帮助psql的解释器更准确地估算成本。