Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.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
Performance 为什么这个计数查询这么慢?_Performance_Postgresql - Fatal编程技术网

Performance 为什么这个计数查询这么慢?

Performance 为什么这个计数查询这么慢?,performance,postgresql,Performance,Postgresql,大家好,我在Heroku上运行postgresql 9.1.6,运行的是他们的Ika计划(7,5gb ram)。我有一张桌子叫汽车。我需要做以下工作: SELECT COUNT(*) FROM "cars" WHERE "cars"."reference_id" = 'toyota_hilux' 现在这需要非常长的时间(64秒!!!) 一点背景: 该表包含大约320万行,而我试图依赖的列具有以下设置: reference_id character varying(50); 和索引: CREA

大家好,我在Heroku上运行postgresql 9.1.6,运行的是他们的Ika计划(7,5gb ram)。我有一张桌子叫汽车。我需要做以下工作:

SELECT COUNT(*) FROM "cars" WHERE "cars"."reference_id" = 'toyota_hilux'
现在这需要非常长的时间(64秒!!!)

一点背景:

该表包含大约320万行,而我试图依赖的列具有以下设置:

reference_id character varying(50);
和索引:

CREATE INDEX index_cars_on_reference_id
  ON cars
  USING btree
  (reference_id COLLATE pg_catalog."default" );
我做错了什么?我认为这种表现不是我应该期望的——或者我应该?

这是不完全正确的。在存在匹配索引的情况下,如果表统计数据表明它将返回大约5%(取决于)的表,则规划器仅选择完整表扫描,因为这样扫描整个表会更快

正如您从自己的问题中看到的,您的查询并非如此。它使用位图索引扫描,然后是位图堆扫描。虽然我本以为会有一个简单的索引扫描。(?)

我注意到在解释输出中还有两件事:
第一次扫描发现832行,而第二次扫描将计数减少到739行。这表明索引中有许多死元组

解释分析
检查每个步骤后的执行时间,并可能将结果添加到您的问题中:

首先,使用EXPLAIN ANALYZE重新运行查询两到三次以填充缓存。最后一次与第一次相比结果如何

下一步:

重播

如果表上有很多写操作,我会将填充因子设置为小于100。比如:

ALTER TABLE cars SET (fillfactor=90);
如果行大小较大或有大量写入操作,则降低。然后:

VACUUM FULL ANALYZE cars;
这需要一段时间。重播

或者,如果您有能力这样做(并且其他重要查询没有相互矛盾的要求):

这将按照索引的物理顺序重写表,这将使此类查询更快


规范化模式 如果您需要快速完成此操作,请使用
serial
主键创建一个表
car\u type
,并从表
cars
中引用它。这将把必要的索引缩减到现在的一小部分

不用说,在尝试任何一种方法之前,您都要进行备份

CREATE temp TABLE car_type (
   car_type_id serial PRIMARY KEY
 , car_type text
 );

INSERT INTO car_type (car_type)
SELECT DISTINCT car_type_id FROM cars ORDER BY car_type_id;

ANALYZE car_type;

CREATE UNIQUE INDEX car_type_uni_idx ON car_type (car_type); -- unique types

ALTER TABLE cars RENAME COLUMN car_type_id TO car_type; -- rename old col
ALTER TABLE cars ADD COLUMN car_type_id int; -- add new int col

UPDATE cars c
SET car_type_id = ct.car_type_id
FROM car_type ct
WHERE ct.car_type = c.car_type;

ALTER TABLE cars DROP COLUMN car_type; -- drop old varchar col

CREATE INDEX cars_car_type_id_idx ON cars (car_type_id);    

ALTER TABLE cars 
ADD CONSTRAINT cars_car_type_id_fkey FOREIGN KEY (car_type_id )
REFERENCES car_type (car_type_id) ON UPDATE CASCADE; -- add fk

VACUUM FULL ANALYZE cars;
或者,如果你想全力以赴:

CLUSTER cars USING cars_car_type_id_idx;
您的查询现在如下所示:

SELECT count(*)
FROM   cars
WHERE  car_type_id = (SELECT car_type_id FROM car_type
                      WHERE car_type = 'toyota_hilux')
而且应该更快。主要是因为现在索引和表更小了,但也因为
integer
处理比
varchar
处理更快。不过,在
varchar
列上的集群表上,增益不会太大


一个受欢迎的副作用:如果您必须重命名一个类型,那么现在只需对一行进行一次小小的
更新
,根本不会弄乱大表。

afaik,您应该期待这样,任何where子句都会强制执行完整的表扫描,而不考虑条件/索引HMMM,但我如何才能利用已经存在的引用id上的索引?顺便说一句,为什么在解释中使用它呢?可能会有帮助。看起来您在分散的数据页上经历了许多磁盘查找(位图堆扫描)的高延迟。您可以尝试第二次立即重新执行查询,以查看数据在缓存中时的差异。“解释分析”的“缓冲区”选项在这里也很有用。@MikeChristensen:wiki页面仅用于计算表中没有任何(where)条件的所有行。使用条件计数是完全不同的事情。如果将cat_类型表连接到子查询中而不是子查询中,则最终查询是否有任何差异?@Clodoaldo:如果您只想计算一种类型(如示例中所示),子查询应该更快。但没什么大不了的。这是一个很好的答案!我试试看。你认为@ErwinBrandstetter认为cars表实际上有170列对这个计数的性能有什么意义吗?我不太了解postgres的时间间隔,但我的猜测是,我不会期望它,因为我在这个查询中没有显式地涉及这些列…@NielsKristian 170 columns可能是一个规范化问题。打开另一个关于表结构的问题。@NielsKristian:Clodoaldo说的,另外:是的,非常大的行意味着一个数据页上只能容纳很少的行。因此,必须访问更多的数据页才能计数,这是影响性能的最重要因素。
CREATE temp TABLE car_type (
   car_type_id serial PRIMARY KEY
 , car_type text
 );

INSERT INTO car_type (car_type)
SELECT DISTINCT car_type_id FROM cars ORDER BY car_type_id;

ANALYZE car_type;

CREATE UNIQUE INDEX car_type_uni_idx ON car_type (car_type); -- unique types

ALTER TABLE cars RENAME COLUMN car_type_id TO car_type; -- rename old col
ALTER TABLE cars ADD COLUMN car_type_id int; -- add new int col

UPDATE cars c
SET car_type_id = ct.car_type_id
FROM car_type ct
WHERE ct.car_type = c.car_type;

ALTER TABLE cars DROP COLUMN car_type; -- drop old varchar col

CREATE INDEX cars_car_type_id_idx ON cars (car_type_id);    

ALTER TABLE cars 
ADD CONSTRAINT cars_car_type_id_fkey FOREIGN KEY (car_type_id )
REFERENCES car_type (car_type_id) ON UPDATE CASCADE; -- add fk

VACUUM FULL ANALYZE cars;
CLUSTER cars USING cars_car_type_id_idx;
SELECT count(*)
FROM   cars
WHERE  car_type_id = (SELECT car_type_id FROM car_type
                      WHERE car_type = 'toyota_hilux')