2011-11-25 12 views
5

Estoy configurando una nueva base de datos de PostgreSQL 9 que contendrá millones (o tal vez miles de millones) de filas. Así que decidí dividir los datos usando la herencia de PostgreSQL.Elección de la regla de partición correcta

he creado una tabla maestra como esta (simplificado por ejemplo):

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

Y 10 de partición tablas:

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

Sé que las filas se accederá siempre desde la aplicación usando un único condición de usuario_id. Así que me gustaría extender los datos "bastante" por igual sobre las 10 tablas usando una regla basada en user_id.

Para ajustar consultas sobre la tabla maestra, mi primera idea era utilizar una restricción de comprobación módulo:

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); 
... 

El problema es, cuando me consultan la tabla maestra "mitabla" con la condición de user_id, PostgreSQL analizador de comprobar todas las tablas y no se benefician de la restricción de comprobación:

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)" 

Mientras que si utilizo una restricción cHECK clásico como este (y el reparto que coincide con la regla):

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); 
... 

va a escanear sólo las tablas que coinciden con la condición (mitabla y mytable_1 en este ejemplo):

"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)" 

Pero el uso de tales restricción de comprobación es difícil de mantener debido a que el número de usuarios que se rellenará en el las tablas cambiarán a lo largo de los años. miles primero, quizás millones o más en el futuro cercano ...

¿Qué regla podría usar para dividir equitativamente mis datos en las 10 tablas que podrían beneficiarse de una restricción de verificación para que un SELECT en la tabla maestra escanee únicamente la mesa correcta ...?

Gracias, Nico

Respuesta

5

La limitación es con el planificador en lugar del propio partioning. Está cubierto en el manual con cierto detalle:

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

hay dos cosas que usted menciona sin embargo que deben tenerse en cuenta.

En primer lugar, dice que todos los accesos se realizarán a través de la clave principal. Esto significa que no obtendrá beneficios de rendimiento de la partición (al menos no en el uso normal). El índice en cada partición será más pequeño, pero PG debe elegir qué partición verificar primero. Donde ganará si necesita reindexar o similar, puede volver a indexar cada partición por separado.

En segundo lugar, dice que puede tener de miles a miles de millones de filas. Esto me lleva a dos conclusiones:

  1. Quizás deje la decisión para más adelante. Espere hasta que necesite particionar.
  2. Es poco probable que desee exactamente 10 particiones con dos mil filas y dos mil millones.

Si va a la partición, hágalo por rango, digamos 100,000 filas o 1 millón por partición. Agregue un trabajo cron para verificar el ID máximo utilizado y cree una nueva partición si es necesario (una vez al día, tal vez).

Personalmente, sin embargo, lo dejaría hasta que lo necesitara. Tal vez tenga una sola partición como un todo, si cree que es más probable que los necesite más adelante.

1

El WHERE debe estar en la misma expresión que el CHECK, i. e., el planificador de consultas no se dará cuenta de que user_id = 12345 permite la conclusión de que user_id % 10 = 5. Trate

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

Dicho esto, me gustaría segunda Richard Huxton's answer en que es posible que desee posponer la partición hasta que haya más información sobre el tamaño del conjunto de datos, TH eidea es evitar la optimización prematura. Postgres puede ser muy rápido en tablas bastante grandes, te llevará bastante lejos sin particiones.

Cuestiones relacionadas