2011-11-25 8 views
5

Sto configurando un nuovo database PostgreSQL 9 che conterrà milioni (o forse miliardi) di righe. Così ho deciso di partizionare i dati usando l'ereditarietà di PostgreSQL.Scelta della regola di partizionamento corretta

ho creato una tabella master come questo (semplificato per esempio):

CREATE TABLE mytable 
(
    user_id integer, 
    year integer, 
    CONSTRAINT pk_mytable PRIMARY KEY (user_id, year) 
); 

E 10 divisorie tabelle:

CREATE TABLE mytable_0() INHERITS (mytable); 
CREATE TABLE mytable_1() INHERITS (mytable); 
... 
CREATE TABLE mytable_9() INHERITS (mytable); 

So che le righe saranno sempre accessibili dall'applicazione utilizzando un unico condizione user_id. Quindi mi piacerebbe diffondere i dati "abbastanza" equamente sulle 10 tabelle usando una regola basata su user_id.

Per query sintonizzare sopra la tabella principale, la mia prima idea era quella di utilizzare un vincolo di controllo modulo:

ALTER TABLE mytable_0 ADD CONSTRAINT mytable_user_id_check CHECK (user_id % 10 = 0); 
ALTER TABLE mytable_1 ADD CONSTRAINT mytable_user_id_check CHECK (user_id % 10 = 1); 
... 

Il problema è che quando interrogo la tabella master "MyTable" con la condizione sulla user_id, PostgreSQL analizzatore di controllare tutte le tabelle e non beneficiano del vincolo di controllo:

EXPLAIN SELECT * FROM mytable WHERE user_id = 12345; 

"Result (cost=0.00..152.69 rows=64 width=36)" 
" -> Append (cost=0.00..152.69 rows=64 width=36)" 
"  -> Seq Scan on mytable (cost=0.00..25.38 rows=6 width=36)" 
"    Filter: (user_id = 12345)" 
"  -> Seq Scan on mytable_0 mytable (cost=0.00..1.29 rows=1 width=36)" 
"    Filter: (user_id = 12345)" 
"  -> Seq Scan on mytable_1 mytable (cost=0.00..1.52 rows=1 width=36)" 
"    Filter: (user_id = 12345)" 
... 
"  -> Seq Scan on mytable_9 mytable (cost=0.00..1.52 rows=1 width=36)" 
"    Filter: (user_id = 12345)" 

Mentre se uso un classico CHECK CONSTRAINT come questo (e la ripartizione che corrisponde a quella regola):

012.351.641.061.
ALTER TABLE mytable_0 ADD CONSTRAINT mytable_user_id_check CHECK (user_id BETWEEN 1 AND 10000); 
ALTER TABLE mytable_1 ADD CONSTRAINT mytable_user_id_check CHECK (user_id BETWEEN 10001 AND 20000); 
... 

si esegue la scansione solo le tabelle che soddisfano la condizione (MyTable e mytable_1 in questo esempio):

"Result (cost=0.00..152.69 rows=64 width=36)" 
" -> Append (cost=0.00..152.69 rows=64 width=36)" 
"  -> Seq Scan on mytable (cost=0.00..25.38 rows=6 width=36)" 
"    Filter: (user_id = 12345)" 
"  -> Seq Scan on mytable_1 mytable (cost=0.00..1.52 rows=1 width=36)" 
"    Filter: (user_id = 12345)" 

Ma usando come vincolo di controllo è difficile da mantenere perché la gamma di utenti che saranno popolato nel le tabelle cambieranno nel corso degli anni. migliaia prima, forse milioni o più nel prossimo futuro ...

Quale regola potrei usare per partizionare equamente i miei dati su 10 tabelle che potrebbero trarre vantaggio da un vincolo di controllo in modo che una SELECT sulla tabella master effettui la scansione solo il tavolo giusto ...?

Grazie, Nico

risposta

5

la limitazione è con il pianificatore piuttosto che il partizionamento stessa. E 'coperto nel manuale in dettaglio:

http://www.postgresql.org/docs/9.1/static/ddl-partitioning.html

ci sono due cose di cui parli, tuttavia, che devono essere considerati.

In primo luogo, si dice che tutto l'accesso avverrà tramite la chiave primaria. Ciò significa che non otterrai alcun beneficio dal partizionamento (almeno non nel normale utilizzo). L'indice su ogni partizione sarà più piccolo, ma PG deve scegliere quale partizione controllare per prima. Dove guadagnerai è se hai bisogno di reindicizzare o simili - puoi reindicizzare ogni partizione separatamente.

In secondo luogo, si dice che si potrebbe avere qualcosa da migliaia a miliardi di righe. Questo mi porta a due conclusioni:

  1. Forse lasciare la decisione a più tardi. Aspetta finché non hai bisogno di partizionare.
  2. È improbabile che vogliate esattamente 10 partizioni con duemila righe e due miliardi.

Se si sta partizionando, farlo per intervallo - diciamo 100.000 righe o 1 milione per partizione. Aggiungi un cron-job per verificare l'ID massimo utilizzato e crea una nuova partizione se necessario (forse una volta al giorno).

Personalmente, però, l'avrei lasciato fino a quando non ne avessi avuto bisogno. Magari avere una singola partizione come una presa in giro se pensi che sia più probabile che non ne avrai bisogno in seguito.

1

Il WHERE deve essere sulla stessa espressione di CHECK, i. e., il pianificatore di query non si renderà conto che user_id = 12345 consente di concludere che user_id % 10 = 5. Prova

EXPLAIN SELECT * FROM mytable WHERE user_id = 12345 AND user_id % 10 = 5; 

Detto questo, vorrei secondo Richard Huxton's answer in che si potrebbe desiderare di rinviare il partizionamento fino ad avere ulteriori informazioni sulle dimensioni del set di dati, th eidea è quello di evitare di ottimizzazione prematura. Postgres può essere molto veloce su tavoli piuttosto grandi, ti porterà piuttosto lontano senza partizionare.