2016-10-18 32 views
3

我想用以前未知的值在Postgres中分區表。在我的場景中,該值將是device_id,它是一個字符串。Postgres中按字符串標識符進行動態表分區

這是目前的情況:

表「device_data」 - 存儲從設備發送的傳感器數據,通過DDL定義:

CREATE TABLE warehouse.device_data (
    id INTEGER PRIMARY KEY NOT NULL DEFAULT nextval('device_data_id_seq'::regclass), 
    device_id TEXT NOT NULL, 
    device_data BYTEA NOT NULL, 
-- contains additional fields which are omitted for brevity 
    received_at TIMESTAMP WITHOUT TIME ZONE DEFAULT now() 
); 

表目前擁有數以百萬計的記錄和查詢冒着巨大多少時間。大多數查詢包含WHERE device_id='something'子句。

解決方案我想到的是爲每個device_id創建表分區。

是否有可能在Postgres中爲每個device_id創建表分區?

我通過Postgres文檔和我發現的幾個例子,但他們都使用固定邊界來創建分區。我的解決方案將需要:

  1. 動態創建新的表分區時新device_id是第一 遇到
  2. 商店現有的分區,如果device_id是 已知和分區爲device_id已經存在

我希望這樣做可以使用表分區,因爲它可以跨多個device_id s查詢。

+2

爲什麼你想通過' device_id'? 'device_id'的簡單**索引**將完成這項工作。性能問題應該包括'EXPLAIN ANALYZE'和一些關於表格大小,索引,當前時間表現,期望時間等的信息。'Slow'是一個相對術語,我們需要一個真實值來比較。 –

回答

1

我喜歡動態分區的想法。我不知道它會如何影響性能,因爲我從來沒有用過它。

變化idint default 0和手動創建序列類型,以避免對單個插入多個nextval()電話:

create table device_data (
    id int primary key default 0, 
    device_id text not null, 
    device_data text not null, -- changed for tests 
    received_at timestamp without time zone default now() 
); 
create sequence device_data_seq owned by device_data.id; 

在觸發功能使用動態SQL:

create or replace function before_insert_on_device_data() 
returns trigger language plpgsql as $$ 
begin 
    execute format(
     $f$ 
      create table if not exists %I (
      check (device_id = %L) 
      ) inherits (device_data) 
     $f$, 
     concat('device_data_', new.device_id), 
     new.device_id); 
    execute format(
     $f$ 
      insert into %I 
      values (nextval('device_data_seq'), %L, %L, default) 
     $f$, 
     concat('device_data_', new.device_id), 
     new.device_id, 
     new.device_data); 
    return null; 
end $$; 

create trigger before_insert_on_device_data 
    before insert on device_data 
    for each row execute procedure before_insert_on_device_data(); 

測試:

insert into device_data (device_id, device_data) values 
    ('first', 'data 1'), 
    ('second', 'data 1'), 
    ('first', 'data 2'), 
    ('second', 'data 2'); 

select * from device_data_first; 

id | device_id | device_data |  received_at   
----+-----------+-------------+---------------------------- 
    1 | first  | data 1  | 2016-10-18 19:50:40.179955 
    3 | first  | data 2  | 2016-10-18 19:50:40.179955 
(2 rows) 

select * from device_data_second; 

id | device_id | device_data |  received_at   
----+-----------+-------------+---------------------------- 
    2 | second | data 1  | 2016-10-18 19:50:40.179955 
    4 | second | data 2  | 2016-10-18 19:50:40.179955 
(2 rows)