Periódicamente, se agregan nuevos valores grapho se actualiza un valor existente. Quiero actualizar la vista graph_avgcada dos horas solo para los valores que se han actualizado. Sin embargo, en PostgreSQL 9.3, toda la tabla se actualiza. Esto lleva bastante tiempo. La próxima versión 9.4 permite la CONCURRENTactualización, pero aún actualiza la vista completa. Con cientos de millones de filas, esto lleva unos minutos.
¿Cuál es una buena manera de realizar un seguimiento de los valores actualizados y nuevos y solo actualizar parcialmente la vista?
Siempre puede implementar su propia tabla que sirve como "vista materializada". Eso es lo que tenía que hacer antes de que MATERIALIZED VIEWse implementara en Postgres 9.3 de cualquier manera.
Por ejemplo, puedes crear un plano VIEW:
CREATEVIEW graph_avg_view ASSELECT xaxis, AVG(value)AS avg_val
FROM graph
GROUPBY xaxis;
Y materialice el resultado como un todo una vez o cada vez que necesite comenzar de nuevo:
(O use la SELECTdeclaración directamente, sin crear una VIEW.)
Luego, dependiendo de los detalles no revelados de su caso de uso, podría DELETE/ UPDATE/ INSERTcambiar manualmente.
Una declaración DML básica con CTE modificadores de datos para su tabla como es :
Suponiendo que nadie trate de otra persona a escribir al graph_avgmismo tiempo (la lectura no es ningún problema):
WITH del AS(DELETEFROM graph_avg t
WHERENOTEXISTS(SELECT1FROM graph_avg_view v WHERE v.xaxis = v.xaxis);), upd AS(UPDATE graph_avg t
FROM graph_avg_view v
WHERE t.xaxis = v.xaxis
AND t.avg_val <> v.avg_val
)INSERTINTO graph_avg t
SELECT*FROM graph_avg_view v
LEFTJOIN graph_avg t USING(xaxis)WHERE t.xaxis ISNULL;
Pero esto probablemente debería optimizarse.
Receta básica:
Agregue una timestampcolumna con valor predeterminado now()a su tabla base. Digamos que es ts.
Si tiene actualizaciones, agregue un activador para establecer la marca de tiempo actual con cada actualización que cambie xaxiso value.
Cree una tabla pequeña para recordar la marca de tiempo de su última instantánea. Llamémoslo mv:
CREATETABLE mv (
tbl text PRIMARYKEY, ts timestamp NOTNULLDEFAULT'-infinity');-- possibly more details
Cree este índice parcial de varias columnas:
CREATEINDEX graph_mv_latest ON graph (xaxis, value)WHERE ts >='-infinity';
Use la marca de tiempo de la última instantánea como predicado en sus consultas para actualizar la instantánea con un uso de índice perfecto.
Al final de la transacción, suelte el índice y vuelva a crearlo con la marca de tiempo de la transacción reemplazando la marca de tiempo en el predicado de índice (inicialmente '-infinity'), que también guarda en su tabla. Todo en una sola transacción.
Tenga en cuenta que el índice parcial es excelente para cubrir INSERTy UPDATEoperaciones, pero no DELETE. Para cubrir eso, debe considerar toda la tabla. Todo depende de los requisitos exactos.
Gracias por la claridad en las opiniones materializadas y por sugerir una respuesta alternativa.
user4150760
13
Actualización concurrente (Postgres 9.4)
Si bien no es una actualización incremental como la solicitó, Postgres 9.4 proporciona una nueva función de actualización concurrente .
Para citar el documento ...
Antes de PostgreSQL 9.4, actualizar una vista materializada significaba bloquear toda la tabla y, por lo tanto, evitar cualquier cosa que la consultara, y si una actualización tardaba mucho en adquirir el bloqueo exclusivo (mientras espera que las consultas lo usen para finalizar), a su vez está retrasando consultas posteriores. Esto ahora se puede mitigar con la palabra clave CONCURRENTEMENTE:
Sin embargo, deberá existir un índice único en la vista materializada. En lugar de bloquear la vista materializada, crea una versión actualizada temporal de la misma, compara las dos versiones, luego aplica INSERT y DELETEs contra la vista materializada para aplicar la diferencia. Esto significa que las consultas aún pueden usar la vista materializada mientras se actualiza. A diferencia de su forma no concurrente, las tuplas no están congeladas, y necesita VACÍO debido a los DELETEs mencionados anteriormente que dejarán atrás las tuplas muertas.
Esta actualización concurrente todavía realiza una nueva consulta completa (no incremental). Por lo tanto, CONCURRENTEMENTE no ahorra en el tiempo de cálculo general, solo minimiza la cantidad de tiempo que su vista materializada no está disponible para su uso durante su actualización.
Por un momento estuve emocionado hasta que leí detenidamente. it instead creates a temporary updated version of it...compares the two versions- Esto significa que la versión actualizada temporal sigue siendo un cálculo completo, luego aplica la diferencia a la vista existente. Entonces, esencialmente, todavía estoy volviendo a hacer TODOS los cálculos, pero solo en la tabla temporal.
user4150760
55
Ah, es cierto, CONCURRENTLYno ahorra en el tiempo de cálculo general, solo minimiza la cantidad de tiempo que su vista materializada no está disponible para su uso durante su actualización.
Actualización concurrente (Postgres 9.4)
Si bien no es una actualización incremental como la solicitó, Postgres 9.4 proporciona una nueva función de actualización concurrente .
Para citar el documento ...
Esta actualización concurrente todavía realiza una nueva consulta completa (no incremental). Por lo tanto, CONCURRENTEMENTE no ahorra en el tiempo de cálculo general, solo minimiza la cantidad de tiempo que su vista materializada no está disponible para su uso durante su actualización.
fuente
it instead creates a temporary updated version of it...compares the two versions
- Esto significa que la versión actualizada temporal sigue siendo un cálculo completo, luego aplica la diferencia a la vista existente. Entonces, esencialmente, todavía estoy volviendo a hacer TODOS los cálculos, pero solo en la tabla temporal.CONCURRENTLY
no ahorra en el tiempo de cálculo general, solo minimiza la cantidad de tiempo que su vista materializada no está disponible para su uso durante su actualización.