当我在Postgres 9.1中运行以下4个查询时,我得到了非常令人费解的结果.查询1,2和4的行为符合预期,快速返回结果,但查询3运行时间要长得多.
plant_data是一个包含约6700万条记录的表,并以所有预期方式使用btree索引编制索引.
-- Query 1
SELECT COUNT(*)
FROM plant_data
WHERE sensor_id IN (
SELECT name FROM nomenclature
WHERE resolution = '1DAY' LIMIT 1 OFFSET 0)
使用索引,在 http://explain.depesz.com/s/dx9
-- Query 2
SELECT COUNT(*)
FROM plant_data
WHERE sensor_id IN (
SELECT name FROM nomenclature
WHERE resolution = '1DAY' LIMIT 1 OFFSET 1)
也使用索引,运行于< 14毫秒(返回29)
http://explain.depesz.com/s/Zfl
-- Query 3
SELECT COUNT(*)
FROM plant_data
WHERE sensor_id IN (
SELECT name FROM nomenclature
WHERE resolution = '1DAY' LIMIT 2)
是seq.扫描,在261秒内运行(返回29)
http://explain.depesz.com/s/Xui
-- Query 4
select count(*)
FROM plant_data
WHERE sensor_id IN (
'BUR_PCLo_SAMPww_C_COD_1DAY', -- First 2 results in
'BUR_ANDIi1_FLOraw_I_VOL_1DAY') -- nomenclature table
使用索引,在 http://explain.depesz.com/s/iQc
问题:为什么查询3不使用索引并快速返回结果?
表中的数据倾向于遵循相当规则的模式.从广义上讲,有4组数据……首先是一个块,它往往是2800个条目的块,具有相同的sensor_id.第二个是96个值的块,具有相同的sensor_id.第三个是12个块,具有相同的sensor_id,第四个是块1个.我们倾向于得到~50个类型1然后是~50个类型2,接着是~50个类型3,接着是少数类型4,之前循环回到开头.上面查询中的数据都是类型4(选择因为结果集可以管理以手动运行查询).
总而言之,我们选择的数据往往分散在我们的6700万条记录中.
完整的表和索引结构:
CREATE TABLE plant_data
(
id serial NOT NULL,
date timestamp without time zone,
sensor_id text,
value double precision,
day_id integer,
week_id integer,
month_id integer,
year_id integer,
resolution integer
)
WITH (
OIDS=FALSE
);
ALTER TABLE plant_data
OWNER TO inners_data;
CREATE INDEX plant_data_date_index
ON plant_data
USING btree
(date);
CREATE INDEX plant_data_day_id
ON plant_data
USING btree
(day_id);
CREATE INDEX plant_data_month_id
ON plant_data
USING btree
(month_id);
CREATE INDEX plant_data_resolution_idx
ON plant_data
USING btree
(resolution);
CREATE INDEX plant_data_sensor_id_date_index
ON plant_data
USING btree
(date, sensor_id COLLATE pg_catalog."default");
CREATE INDEX plant_data_sensor_id_index
ON plant_data
USING btree
(sensor_id COLLATE pg_catalog."default");
CREATE INDEX plant_data_week_id
ON plant_data
USING btree
(week_id);
CREATE INDEX plant_data_year_id
ON plant_data
USING btree
(year_id);
编辑:更新说明以上解释分析,添加有关表中的索引,表结构和数据模式的信息
最佳答案 这个评论太长了.
以下是快速还是慢速运行?
-- Query 1
SELECT COUNT(*)
FROM plant_data
WHERE exists (SELECT 1 FROM nomenclature
WHERE resolution = '1DAY' and sensor_id = name LIMIT 2
);
首先,Postgres会对包含常量的列表很聪明.第四个快速回归并不奇怪.对于前两个示例,我怀疑(或者可能是这个示例证明)Postgres优化了一个子查询,该子查询返回一行与返回多行的子查询不同.也就是说,第一个查询被识别为返回一行并进行优化,就好像它是:
SELECT COUNT(*)
FROM plant_data
WHERE sensor_id = (
SELECT name FROM nomenclature
WHERE resolution = '1DAY' LIMIT 1 OFFSET 0)
但是,对于多行,Postgres采用了不同的方法,可能涉及每次重新执行内部查询.这个查询是快还是慢?
-- Query 2
with list as (
SELECT name FROM nomenclature
WHERE resolution = '1DAY' LIMIT 2
)
SELECT COUNT(*)
FROM plant_data
WHERE sensor_id IN (select name from list);
编辑:
您的解释确实解释了性能的差异.对于限制1,Postgres正在进行嵌套循环连接,当只有一行循环时恰好是真正快速的.它也可以通过两行实现快速循环.作为嵌套循环的一部分,它使用分辨率索引.
在限制2的情况下,Postgres选择进行“哈希 – 半连接”.这意味着需要处理较大表中的数据.更糟糕的是,数据不是来自索引,而是扫描整个表. Arrrg!
我不知道为什么Postgres优化器在这种情况下是如此不理想,避免了完全合理的索引.
希望Postgres大师能够带来更多洞察力.