EM0*_*EM0 4 sql postgresql performance database-design partitioning
我在 PostgreSQL 9.2 中有一个大表,我按照手册中的描述对它进行了分区。嗯……差不多!我真正的分区键不在分区表本身中,而是在连接表中,如下所示(简化):
-- millions to tens of millions of rows
CREATE TABLE data
(
slice_id integer NOT NULL,
point_id integer NOT NULL,
-- ... data columns ...,
CONSTRAINT pk_data PRIMARY KEY (slice_id, point_id),
CONSTRAINT fk_data_slice FOREIGN KEY (slice_id) REFERENCES slice (id)
CONSTRAINT fk_data_point FOREIGN KEY (point_id) REFERENCES point (id)
)
-- hundreds to thousands of rows
CREATE TABLE slice
(
id serial NOT NULL,
partition_date timestamp without time zone NOT NULL,
other_date timestamp without time zone NOT NULL,
int_key integer NOT NULL
CONSTRAINT pk_slice PRIMARY KEY (id)
)
-- about 40,000 rows
CREATE TABLE point
(
-- ... similar to "slice" ...
)
Run Code Online (Sandbox Code Playgroud)
要分区表(data)包含的每个组合的行point和slice,其中每一个具有复合键。我只想在一个关键列上对它进行分区,partition_date,它是slice. 当然,我的子表上的检查约束不能直接包含它,所以我包含了slice.id与 that 对应的所有值的范围partition_date,如下所示:
ALTER TABLE data_part_123 ADD CONSTRAINT ck_data_part_123
CHECK (slice_id >= 1234 AND slice_id <= 1278);
Run Code Online (Sandbox Code Playgroud)
这一切都适用于插入数据。但是,查询不使用上述 CHECK 约束。例如。
SELECT *
FROM data d
JOIN slice s ON d.slice_id = s.id
WHERE s.partition_date = '2013-07-23'
Run Code Online (Sandbox Code Playgroud)
我可以在查询计划中看到这仍然扫描所有子表。我尝试以多种方式重写查询,包括 CTE 和子选择,但这并没有帮助。
有什么办法可以让规划者“理解”我的分区方案?我真的不想在data表中重复数百万次的分区键。
查询计划如下所示:
Aggregate (cost=539243.88..539243.89 rows=1 width=0)
-> Hash Join (cost=8.88..510714.02 rows=11411945 width=0)
Hash Cond: (d.slice_id = s.id)
-> Append (cost=0.00..322667.41 rows=19711542 width=4)
-> Seq Scan on data d (cost=0.00..0.00 rows=1 width=4)
-> Seq Scan on data_part_123 d (cost=0.00..135860.10 rows=8299610 width=4)
-> Seq Scan on data_part_456 d (cost=0.00..186807.31 rows=11411931 width=4)
-> Hash (cost=7.09..7.09 rows=143 width=4)
-> Seq Scan on slice s (cost=0.00..7.09 rows=143 width=4)
Filter: (partition_date = '2013-07-23 00:00:00'::timestamp without time zone)
Run Code Online (Sandbox Code Playgroud)
实现它的唯一方法是使查询动态化:
create function select_from_data (p_date date)
returns setof data as $function$
declare
min_slice_id integer,
max_slice_id integer;
begin
select min(slice_id), max(slice_id)
into min_slice_id, max_slice_id
from slice
where partition_date = p_date;
return query execute
$dynamic$
select *
from data
where slice_id between $1 and $2
$dynamic$
using min_slice_id, max_slice_id;
end;
$function$ language plpgsql;
Run Code Online (Sandbox Code Playgroud)
这将使用给定日期的适当切片范围构建查询,并将在运行时规划它,此时规划器将拥有检查确切分区所需的信息。
为了使函数更通用而不失去规划器在运行时获取信息的能力,请使用or parameter is null过滤器中的构造。
create function select_from_data (
p_date date,
value_1 integer default null,
value_2 integer default null
)
returns setof data as $function$
declare
min_slice_id integer,
max_slice_id integer;
begin
select min(slice_id), max(slice_id)
into min_slice_id, max_slice_id
from slice
where partition_date = p_date;
return query execute
$dynamic$
select *
from data
where
slice_id between $1 and $2
and (some_col = $3 or $3 is null)
and (another_col = $4 or $4 is null)
$dynamic$
using min_slice_id, max_slice_id, value_1, value_2;
end;
$function$ language plpgsql;
Run Code Online (Sandbox Code Playgroud)
现在,如果传递了一些参数,因为null它不会干扰查询。
| 归档时间: |
|
| 查看次数: |
2709 次 |
| 最近记录: |