sql >> Base de Datos >  >> RDS >> PostgreSQL

Coincidencia avanzada de particiones para unir particiones

Anteriormente había escrito un blog sobre la unión por partición en PostgreSQL. En ese blog, hablé sobre una técnica avanzada de coincidencia de particiones que permitirá que se use la combinación de particiones en más casos. En este blog discutiremos esta técnica en detalle.

En resumen, la técnica básica de coincidencia de particiones permite realizar una combinación entre dos tablas particionadas utilizando la técnica de combinación de partición si las dos tablas particionadas tienen límites de partición que coinciden exactamente, p. tablas particionadas prt1 y prt2 descritas a continuación

psql> \d+ prt1
... [output clipped]
Partition key: RANGE (a)
Partitions: prt1_p1 FOR VALUES FROM (0) TO (5000),
prt1_p2 FOR VALUES FROM (5000) TO (15000),
prt1_p3 FOR VALUES FROM (15000) TO (30000)

y

psql>\d+ prt2
... [ output clipped ]
Partition key: RANGE (b)
Partitions: prt2_p1 FOR VALUES FROM (0) TO (5000),
prt2_p2 FOR VALUES FROM (5000) TO (15000),
prt2_p3 FOR VALUES FROM (15000) TO (30000)

Una unión entre prt1 y prt2 en su clave de partición (a) se divide en uniones entre sus particiones coincidentes, es decir, prt1_p1 se une a prt2_p1, prt1_p2 se une a prt2_p2 y prt1_p3 se une a prt2_p3. Los resultados de estas tres uniones forman el resultado de la unión entre prt1 y prt2. Esto tiene muchas ventajas como se discutió en mi blog anterior. Sin embargo, la coincidencia de partición básica no puede unir dos tablas particionadas con diferentes límites de partición. En el ejemplo anterior, si prt1 tiene una partición adicional prt1_p4 PARA VALORES DESDE (30000) HASTA (50000), la coincidencia de partición básica no ayudaría a convertir una combinación entre prt1 y prt2 en una combinación de partición, ya que no tienen una partición exactamente coincidente límites.

Muchas aplicaciones usan particiones para separar los datos usados ​​activamente y los datos obsoletos, una técnica que expuse en mi otro blog. Los datos obsoletos finalmente se eliminan eliminando particiones. Se crean nuevas particiones para acomodar datos nuevos. Una unión entre dos de estas tablas particionadas utilizará principalmente la unión por partición, ya que la mayoría de las veces tendrán particiones coincidentes. Pero cuando se agrega una partición activa a una de estas tablas o se elimina una obsoleta, los límites de la partición no coincidirán hasta que la otra tabla también se someta a una operación similar. Durante ese intervalo, una unión entre estas dos tablas no utilizará la unión por partición y puede tardar un tiempo inusualmente más largo en ejecutarse. No queremos que una unión llegue a la base de datos durante este breve período de tiempo para que funcione mal, ya que no puede usar la unión por partición. El algoritmo avanzado de coincidencia de particiones ayuda en este y otros casos más complicados en los que los límites de las particiones no coinciden exactamente.

Algoritmo avanzado de coincidencia de particiones

La técnica avanzada de coincidencia de particiones encuentra particiones coincidentes de dos tablas particionadas incluso cuando sus límites de partición no coinciden exactamente. Encuentra particiones coincidentes comparando los límites de ambas tablas en su orden ordenado similar al algoritmo de combinación de combinación. Dos particiones cualesquiera, una de cada una de las tablas particionadas, cuyos límites coincidan exactamente o se superpongan, se consideran socios de unión, ya que pueden contener filas de unión. Continuando con el ejemplo anterior, digamos que se agrega una nueva partición activa prt2_p4 a prt4. Las tablas particionadas ahora se ven como:

psql>\d+ prt1
... [output clipped]
Partition key: RANGE (a)
Partitions: prt1_p1 FOR VALUES FROM (0) TO (5000),
prt1_p2 FOR VALUES FROM (5000) TO (15000),
prt1_p3 FOR VALUES FROM (15000) TO (30000)

y

psql>\d+ prt2
... [ output clipped ]
Partition key: RANGE (b)
Partitions: prt2_p1 FOR VALUES FROM (0) TO (5000),
prt2_p2 FOR VALUES FROM (5000) TO (15000),
prt2_p3 FOR VALUES FROM (15000) TO (30000),
prt2_p4 FOR VALUES FROM (30000) TO (50000)

Es fácil ver que los límites de partición de prt1_p1 y prt2_p1, prt1_p2 y prt2_p2, y prt1_p3 y prt2_p3 coinciden respectivamente. Pero a diferencia de la coincidencia de partición básica, la coincidencia de partición avanzada sabrá que prt2_p4 no tiene ninguna partición coincidente en prt1. Si la unión entre prt1 y prt2 es una unión INTERNA o cuando prt2 es una relación INTERNA en la unión, el resultado de la unión no tendrá ninguna fila de prt2_p4. Habilitado con información detallada sobre las particiones coincidentes y las particiones que no coinciden, en lugar de simplemente si los límites de la partición coinciden o no, el optimizador de consultas puede decidir si usar o no la unión por partición. En este caso, elegirá ejecutar la combinación como una combinación entre las particiones coincidentes, dejando de lado prt2_p4. Pero eso no se parece mucho a una coincidencia de partición "avanzada". Veamos un caso un poco más complicado usando listas de tablas particionadas esta vez:

psql>\d+ plt1
Partition key: LIST (c)
Partitions: plt1_p1 FOR VALUES IN ('0001', '0003'),
plt1_p2 FOR VALUES IN ('0004', '0006'),
plt1_p3 FOR VALUES IN ('0008', '0009')

y

psql>\d+ plt2
Partition key: LIST (c)
Partitions: plt2_p1 FOR VALUES IN ('0002', '0003'),
plt2_p2 FOR VALUES IN ('0004', '0006'),
plt2_p3 FOR VALUES IN ('0007', '0009')

Observe que hay exactamente tres particiones en ambas relaciones, pero las listas de valores de partición difieren. La lista correspondiente a la partición plt1_p2 coincide exactamente con la de plt2_p2. Aparte de eso, no hay dos particiones, una de cada lado, que tengan listas que coincidan exactamente. El algoritmo avanzado de coincidencia de particiones deduce que plt1_p1 y plt2_p1 tienen listas superpuestas y sus listas no se superponen con ninguna otra partición de la otra relación. Del mismo modo para plt1_p3 y plt2_p3. Luego, el optimizador de consultas ve que la unión entre plt1 y plt2 se puede ejecutar como unión por partición al unir las particiones coincidentes, es decir, plt1_p1 y plt2_p1, plt1_p2 y plt2_p2, y plt1_p3 y plt2_p3 respectivamente. El algoritmo puede encontrar particiones coincidentes en conjuntos de listas enlazados a particiones aún más complejos, así como en tablas particionadas por rangos. Pero no los cubriremos en aras de la brevedad. Los lectores interesados ​​y más atrevidos pueden echar un vistazo al compromiso. También tiene muchos casos de prueba, que muestran varios escenarios en los que se utiliza un algoritmo avanzado de coincidencia de particiones.

Limitaciones

Uniones exteriores con particiones coincidentes que faltan en el lado interior

Las uniones externas plantean un problema particular en el mundo de PostgreSQL. Considere prt2 LEFT JOIN prt1, en el ejemplo anterior, donde prt2 es una relación EXTERNA. prt2_p4 no tiene un socio de unión en prt1 y, sin embargo, las filas en esa partición deben ser parte del resultado de la unión ya que pertenecen a la relación externa. En PostgreSQL, cuando el lado INTERNO de una combinación está vacío, se representa mediante una relación "ficticia" que no emite filas pero aún conoce el esquema de esa relación. Por lo general, una relación "ficticia" surge de una relación no ficticia que no va a emitir ninguna fila debido a alguna optimización de consulta, como la exclusión de restricciones. El optimizador de consultas de PostgreSQL marca una relación no ficticia como ficticia y el ejecutor procede normalmente al ejecutar dicha combinación. Pero cuando no hay una partición interna que coincida con una partición externa, no hay una "entidad existente" que pueda marcarse como "ficticia". Por ejemplo, en este caso no hay prt1_p4 que pueda representar una partición interior ficticia que se una a la prt2_p4 exterior. En este momento, PostgreSQL no tiene una forma de "crear" tales relaciones "ficticias" durante la planificación. Por lo tanto, el optimizador de consultas no utiliza la unión por partición en este caso.

Idealmente, tal unión con el interior vacío solo requiere un esquema de la relación interna y no una relación completa. Este esquema se puede derivar de la propia tabla particionada. Todo lo que necesita es la capacidad de producir la fila de unión usando las columnas de una fila en el lado exterior unidas por valores NULL para las columnas del lado interior. Una vez que tengamos esa capacidad en PostgreSQL, el optimizador de consultas podrá usar la combinación de partición incluso en estos casos.

Permítanme enfatizar que las uniones externas en las que no faltan particiones en la unión interna sí usan una unión por partición.

Múltiples particiones coincidentes

Cuando las tablas se particionan de tal manera que varias particiones de un lado coinciden con una o más particiones del otro lado, no se puede usar la unión por partición ya que no hay forma de inducir una relación "Agregar" durante el tiempo de planificación que represente dos o más particiones juntas. Con suerte, también eliminaremos esa limitación en algún momento y permitiremos que se use la combinación de partición en esos casos también.

Tablas con particiones hash

Los límites de partición de dos tablas con particiones hash que usan el mismo módulo siempre coinciden. Cuando el módulo es diferente, una fila de una partición dada de una tabla puede tener sus socios de unión en muchas de las particiones de la otra, por lo tanto, una partición dada de un lado coincide con varias particiones de la otra tabla, lo que hace que la unión por partición sea ineficaz.

Cuando el algoritmo avanzado de coincidencia de particiones no puede encontrar particiones coincidentes o la unión por partición no se puede usar debido a las limitaciones anteriores, PostgreSQL recurre a unir las tablas particionadas como tablas regulares.

Tiempo de coincidencia de partición avanzada

Simon mencionó un punto interesante al comentar sobre la característica. Las particiones de una tabla particionada no cambian con frecuencia, por lo que el resultado de la coincidencia avanzada de particiones debería permanecer igual durante más tiempo. No es necesario calcularlo cada vez que se ejecuta una consulta que involucre estas tablas. En su lugar, podríamos guardar el conjunto de particiones coincidentes en algún catálogo y actualizarlo cada vez que cambien las particiones. Eso es un poco de trabajo, pero vale la pena el tiempo invertido en hacer coincidir la partición para cada consulta.

Aún con todas estas limitaciones, lo que tenemos hoy es una solución muy útil que sirve para la mayoría de los casos prácticos. ¡No hace falta decir que esta función funciona a la perfección con FDW push down, mejorando las capacidades de fragmentación que PostgreSQL ya tiene!