Introducción
- Una reordenación completa
- Un subconjunto de la tabla original con un orden diferente
- Una agregación precalculada (similar a una vista materializada), pero con un orden alineado con la agregación.
¿Cómo funcionan las Proyecciones?
- Uso correcto de índices primarios
- Precomputación de agregados
Almacenamiento más inteligente con _part_offset
_part_offset en las
proyecciones, lo que ofrece una nueva forma de definir una proyección.
Ahora hay dos formas de definir una proyección:
- Almacenar columnas completas (el comportamiento original): La proyección contiene datos completos y puede leerse directamente, lo que ofrece un mejor rendimiento cuando los filtros coinciden con el orden de ordenación de la proyección.
-
Almacenar solo la clave de ordenación +
_part_offset: La proyección funciona como un índice. ClickHouse utiliza el índice primario de la proyección para localizar las filas coincidentes, pero lee los datos reales de la tabla base. Esto reduce la sobrecarga de almacenamiento a costa de un poco más de E/S en el momento de la consulta.
_part_offset.
¿Cuándo usar proyecciones?
- Las proyecciones no permiten usar distintos TTL para la tabla de origen y la tabla de destino (oculta); las vistas materializadas sí permiten distintos TTL.
- Las actualizaciones ligeras y las eliminaciones no son compatibles con tablas con proyecciones.
- Las vistas materializadas pueden encadenarse: la tabla de destino de una vista materializada puede ser la tabla de origen de otra vista materializada, y así sucesivamente. Esto no es posible con las proyecciones.
- Las definiciones de proyecciones no admiten joins, pero las vistas materializadas sí. Sin embargo, las consultas sobre tablas con proyecciones pueden usar joins libremente.
- Las definiciones de proyecciones no admiten filtros (cláusula
WHERE), pero las vistas materializadas sí. Sin embargo, las consultas sobre tablas con proyecciones pueden filtrar libremente.
- Se requiere una reordenación completa de los datos. Aunque la expresión de la
proyección puede, en teoría, usar un
GROUP BY,las vistas materializadas son más eficaces para mantener agregaciones. También es más probable que el optimizador de consultas aproveche proyecciones que usan una reordenación simple, es decir,SELECT * ORDER BY x. Puede seleccionar un subconjunto de columnas en esta expresión para reducir la huella de almacenamiento. - Los usuarios se sienten cómodos con el posible aumento de la huella de almacenamiento y la sobrecarga asociada de escribir los datos dos veces. Pruebe el impacto en la velocidad de inserción y evalúe la sobrecarga de almacenamiento.
Ejemplos
Filtrado por columnas que no están en la clave primaria
pickup_datetime.
Escribamos una consulta sencilla para encontrar todos los ID de viaje en los que los pasajeros
dejaron a su conductor una propina superior a $200:
Observa que, como estamos filtrando por tip_amount, que no está en el ORDER BY, ClickHouse
tuvo que hacer un escaneo completo de la tabla. Aceleremos esta consulta.
Para conservar la tabla original y los resultados, crearemos una tabla nueva y copiaremos los datos mediante un INSERT INTO SELECT:
ALTER TABLE junto con la sentencia
ADD PROJECTION:
MATERIALIZE PROJECTION
para que los datos que contiene se ordenen físicamente y se reescriban de acuerdo
con la consulta especificada anteriormente:
system.query_log:
Uso de proyecciones para acelerar las consultas sobre UK Price Paid
town ni price estaban en nuestra cláusula ORDER BY cuando
creamos la tabla:
INSERT INTO SELECT:
prj_oby_town_price, que genera una
tabla adicional (oculta) con un índice primario, ordenada por localidad y precio, para
optimizar la consulta que enumera los condados de una localidad específica con los precios
pagados más altos:
mutations_sync se
utiliza para forzar la ejecución síncrona.
Creamos y poblamos la proyección prj_gby_county – una tabla adicional (oculta)
que precomputa de forma incremental los valores agregados de avg(price) para los 130
condados del Reino Unido existentes:
Si se usa una cláusula
GROUP BY en una proyección como la proyección prj_gby_county
anterior, el motor de almacenamiento subyacente de la tabla (oculta)
pasa a ser AggregatingMergeTree, y todas las funciones de agregación se convierten en
AggregateFunction. Esto garantiza una correcta agregación incremental de los datos.uk_price_paid_with_projections
y sus dos proyecciones:
Si ahora ejecutamos de nuevo la consulta que muestra los condados de Londres con los tres precios
pagados más altos, veremos una mejora en el rendimiento de la consulta:
Del mismo modo, para la consulta que muestra los condados del Reino Unido con los tres precios
medios pagados más altos:
Ten en cuenta que ambas consultas se dirigen a la tabla original y que ambas dieron como resultado
un escaneo completo de la tabla (los 30,03 millones de filas se leyeron del disco) antes de que
creáramos las dos proyecciones.
Además, ten en cuenta que la consulta que muestra los condados de Londres para los tres precios
pagados más altos está procesando 2,17 millones de filas. Cuando usamos directamente una segunda tabla
optimizada para esta consulta, solo se leyeron 81,92 mil filas del disco.
La razón de la diferencia es que actualmente la optimización optimize_read_in_order
mencionada anteriormente no es compatible con las proyecciones.
Inspeccionamos la tabla system.query_log para ver que ClickHouse
utilizó automáticamente las dos proyecciones para las dos consultas anteriores (consulta la
columna projections a continuación):
Más ejemplos
CREATE AS e INSERT INTO SELECT.
Crear una proyección
toYear(date), district y town:
optimize_use_projections, que está habilitada de forma predeterminada.
Consulta 1. Precio medio por año
Consulta 2. Precio promedio por año en Londres
Consulta 3. Los barrios más caros
toYear(date) >= 2020)):
De nuevo, el resultado es el mismo, pero observa la mejora en el rendimiento de la consulta en la segunda consulta.
Combinar proyecciones en una sola consulta
_part_offset introducido en
la versión anterior, ClickHouse ahora puede usar varias proyecciones para acelerar
una única consulta con múltiples filtros.
Es importante destacar que ClickHouse sigue leyendo datos de una sola proyección (o de la tabla base),
pero puede usar los índices primarios de otras proyecciones para descartar partes innecesarias antes de leer.
Esto resulta especialmente útil para consultas que filtran por varias columnas, cada
una de las cuales puede coincidir con una proyección distinta.
Actualmente, este mecanismo solo descarta partes completas. La poda a nivel de gránulo aún no es compatible.Para demostrarlo, definimos la tabla (con proyecciones que usan columnas
_part_offset)
e insertamos cinco filas de ejemplo que coinciden con los diagramas anteriores.
Nota: La tabla usa ajustes personalizados con fines ilustrativos, como gránulos de una sola fila
y fusiones de partes deshabilitadas, que no se recomiendan para entornos de producción.
- Cinco partes independientes (una por cada fila insertada)
- Una entrada del índice primario por fila (en la tabla base y en cada proyección)
- Cada parte contiene exactamente una fila
region y user_id.
Como el índice primario de la tabla base se construye a partir de event_date e id,
no resulta útil en este caso, por lo que ClickHouse usa:
region_projpara descartar partes por regiónuser_id_projpara seguir descartando poruser_id
EXPLAIN projections = 1, que muestra cómo
ClickHouse selecciona y aplica las proyecciones.
EXPLAIN (mostrada arriba) revela el plan lógico de la consulta, de arriba hacia abajo:
Al final, solo 1 de las 5 partes se lee desde la tabla base.
Al combinar el análisis de índices de múltiples proyecciones, ClickHouse reduce significativamente la cantidad de datos analizados,
mejorando el rendimiento y manteniendo baja la sobrecarga de almacenamiento.