我一直在使用postgreSQL数据库分区。我的数据库增长很多,并且很好地进行了分区。不幸的是,我现在似乎已经遇到了另一个障碍,正试图找出一些方法来加快数据库的速度。PostgreSQL多层分区
我的基本设置如下: 我有一个名为database_data
的主表,所有分区都从中继承。我选择每个月有一个分区,并将它们命名为:database_data_YYYY_MM
,这很好地工作。
通过分析我的数据使用情况,我注意到我主要在表上插入操作,只做了一些更新。但是更新也只发生在某种行上:我有一个名为channel_id
的列(一个FK到另一个表)。我更新的行总是有一个channel_id
,其中可能有50个ID,所以这将是区分从未更新的行与潜在的行的好方法。
我想这将进一步加快我的设置,如果我将使用分区有表只数据和每月潜在的更新数据之一插入的一个,因为我的更新会检查每件不排时间。
我当然可以使用我现在使用的“简单”分区,并为每个月添加一个名为database_data_YYYY_MM_update
的表,并为该表和database_data_YYYY_MM
表添加特殊约束,以便查询规划器区分这些表。
不管怎么说,我的确有时候会有操作对指定月份的所有数据进行操作,无论是否可更新。在这种情况下,我可以加入这两个表,但是对于这样的查询可能有一个更简单的方法。
所以,现在我真正的问题:
在PostgreSQL里是“两层”分区可能吗?我的意思是,不是每个月从主表继承两张表,而是每月只有一张表直接从主表继承,例如database_data_YYYY_MM
,然后再有两个表格从该表格继承,一个用于仅插入数据,例如database_data_YYYY_MM_insert
,一个用于可更新数据,例如database_data_YYYY_MM_update
。
这会加快查询计划吗?我猜想如果查询规划器可以在中间表被删除的情况下同时消除两个表,那么速度会更快。
这里的一个明显的优势是,我可以通过简单地使用表database_data_YYYY_MM
来操作一个月的所有数据,而我的更新可以直接使用子表。
我没有想到的任何缺点?
谢谢你的想法。
编辑1:
我不认为一个模式是绝对必要回答我的问题,但如果它有助于理解,我将提供一个示例模式:
CREATE TABLE database_data (
id bigint PRIMARY KEY,
channel_id bigint, -- This is a FK to another table
timestamp TIMESTAMP WITH TIME ZONE,
value DOUBLE PRECISION
)
我有database_data表上的一个触发器,用于根据需要生成分区:
CREATE OR REPLACE FUNCTION function_insert_database_data() RETURNS TRIGGER AS $BODY$
DECLARE
thistablename TEXT;
thisyear INTEGER;
thismonth INTEGER;
nextmonth INTEGER;
nextyear INTEGER;
BEGIN
-- determine year and month of timestamp
thismonth = extract(month from NEW.timestamp AT TIME ZONE 'UTC');
thisyear = extract(year from NEW.timestamp AT TIME ZONE 'UTC');
-- determine next month for timespan in check constraint
nextyear = thisyear;
nextmonth = thismonth + 1;
if (nextmonth >= 13) THEN
nextmonth = nextmonth - 12;
nextyear = nextyear +1;
END IF;
-- Assemble the tablename
thistablename = 'database_datanew_' || thisyear || '_' || thismonth;
-- We are looping until it's successfull to catch the case when another connection simultaneously creates the table
-- if that would be the case, we can retry inserting the data
LOOP
-- try to insert into table
BEGIN
EXECUTE 'INSERT INTO ' || quote_ident(thistablename) || ' SELECT ($1).*' USING NEW;
-- Return NEW inserts the data into the main table allowing insert statements to return the values like "INSERT INTO ... RETURNING *"
-- This requires us to use another trigger to delete the data again afterwards
RETURN NEW;
-- If the table does not exist, create it
EXCEPTION
WHEN UNDEFINED_TABLE THEN
BEGIN
-- Create table with check constraint on timestamp
EXECUTE 'CREATE TABLE ' || thistablename || ' (CHECK (timestamp >= TIMESTAMP WITH TIME ZONE '''|| thisyear || '-'|| thismonth ||'-01 00:00:00+00''
AND timestamp < TIMESTAMP WITH TIME ZONE '''|| nextyear || '-'|| nextmonth ||'-01 00:00:00+00''), PRIMARY KEY (id)
) INHERITS (database_data)';
-- Add any trigger and indices to the table you might need
-- Insert the new data into the new table
EXECUTE 'INSERT INTO ' || quote_ident(thistablename) || ' SELECT ($1).*' USING NEW;
RETURN NEW;
EXCEPTION WHEN DUPLICATE_TABLE THEN
-- another thread seems to have created the table already. Simply loop again.
END;
-- Don't insert anything on other errors
WHEN OTHERS THEN
RETURN NULL;
END;
END LOOP;
END;
$BODY$
LANGUAGE plpgsql;
CREATE TRIGGER trigger_insert_database_data
BEFORE INSERT ON database_data
FOR EACH ROW EXECUTE PROCEDURE function_insert_database_data();
至于示例数据:假设我们只有两个通道:1和2. 1仅插入数据,2可更新。
我的两个层的做法是这样的:
主表:
CREATE TABLE database_data (
id bigint PRIMARY KEY,
channel_id bigint, -- This is a FK to another table
timestamp TIMESTAMP WITH TIME ZONE,
value DOUBLE PRECISION
)
中间表:
CREATE TABLE database_data_2015_11 (
(CHECK (timestamp >= TIMESTAMP WITH TIME ZONE '2015-11-01 00:00:00+00' AND timestamp < TIMESTAMP WITH TIME ZONE '2015-12-01 00:00:00+00)),
PRIMARY KEY (id)
) INHERITS(database_data);
分区:
CREATE TABLE database_data_2015_11_insert (
(CHECK (channel_id = 1)),
PRIMARY KEY (id)
) INHERITS(database_data_2015_11);
CREATE TABLE database_data_2015_11_update (
(CHECK (channel_id = 2)),
PRIMARY KEY (id)
) INHERITS(database_data_2015_11);
我当然然后需要anot她在中间表上的触发器按需创建子表。
没有代码,模式和样本数据这得到题外话 –