sql – 为什么其中一个查询运行得这么慢?

当我在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大师能够带来更多洞察力.

点赞