Entender el rendimiento de las consultas
Consideraciones generales
- Análisis sintáctico y análisis de consultas
- Optimización de consultas
- Ejecución del pipeline de consultas
- Procesamiento final
Conjunto de datos
Identifica las consultas lentas
Registros de consultas
system.query_log.
Para cada consulta ejecutada, ClickHouse registra estadísticas como el tiempo de ejecución, el número de filas leídas y el uso de recursos, como la CPU, el uso de memoria o los aciertos de la caché del sistema de archivos.
Por lo tanto, el registro de consultas es un buen punto de partida para investigar consultas lentas. Puedes identificar fácilmente las consultas que tardan mucho en ejecutarse y ver la información de uso de recursos de cada una.
Veamos las cinco consultas de mayor duración en nuestro conjunto de datos de taxis de NYC.
query_duration_ms indica cuánto tardó en ejecutarse esa consulta en particular. Al observar los resultados de los registros de consultas, vemos que la primera consulta tarda 2967ms en ejecutarse, lo que podría mejorarse.
También puede que quieras saber qué consultas están poniendo bajo presión al sistema; para ello, examina la consulta que consume más memoria o CPU.
enable_filesystem_cache en 0 para mejorar la reproducibilidad.
Veamos un poco mejor qué hacen estas consultas.
- La consulta 1 calcula la distribución de distancias de los trayectos con una velocidad media superior a 30 millas por hora.
- La consulta 2 calcula el número y el coste medio de los trayectos por semana.
- La consulta 3 calcula la duración media de cada trayecto en el conjunto de datos.
Sentencia EXPLAIN
nyc_taxi.trips_small_inferred. A continuación, se aplica la cláusula WHERE para filtrar las filas según los valores calculados. Los datos filtrados se preparan para la agregación y se calculan los cuantiles. Por último, el resultado se ordena y se genera la salida.
Aquí podemos observar que no se utilizan claves primarias, lo cual tiene sentido ya que no definimos ninguna al crear la tabla. En consecuencia, ClickHouse realiza un escaneo completo de la tabla para la consulta.
Explain Pipeline
EXPLAIN Pipeline muestra la estrategia de ejecución concreta para la consulta. Allí se puede ver cómo ClickHouse ejecutó realmente el plan de consulta genérico que vimos anteriormente.
Metodología
user, tables o databases de system.query_logs para acotar la búsqueda.
Una vez que identifique las consultas que desea optimizar, puede empezar a trabajar en ellas. Un error común que cometen los desarrolladores en esta etapa es cambiar varias cosas a la vez, ejecutar experimentos ad hoc y, por lo general, acabar con resultados dispares y, lo que es más importante, sin entender bien qué hizo que la consulta fuera más rápida.
La optimización de consultas requiere un enfoque estructurado. No me refiero a benchmarks avanzados, sino a contar con un proceso sencillo para entender cómo afectan sus cambios al rendimiento de las consultas.
Empiece por identificar las consultas lentas en los registros de consultas y, a continuación, investigue posibles mejoras de forma aislada. Al probar la consulta, asegúrese de desactivar la caché del sistema de archivos.
ClickHouse aprovecha el almacenamiento en caché para acelerar el rendimiento de las consultas en distintas etapas. Esto es bueno para el rendimiento de las consultas, pero durante la resolución de problemas podría ocultar posibles cuellos de botella de E/S o un esquema de tabla deficiente. Por esta razón, sugiero desactivar la caché del sistema de archivos durante las pruebas. Asegúrese de tenerla habilitada en la configuración de producción.Una vez que haya identificado posibles optimizaciones, se recomienda implementarlas una por una para seguir mejor cómo afectan al rendimiento. A continuación se muestra un diagrama que describe el enfoque general. Por último, tenga cuidado con los valores atípicos; es bastante habitual que una consulta se ejecute lentamente, ya sea porque un usuario probó una consulta ad hoc costosa o porque el sistema estaba sometido a carga por alguna otra razón. Puede agrupar por el campo normalized_query_hash para identificar consultas costosas que se ejecutan con regularidad. Esas son probablemente las que querrá investigar.
Optimización básica
Nullable
mta_tax y payment_type. El resto de los campos no debería usar una columna Nullable.
Baja cardinalidad
ratecode_id, pickup_location_id, dropoff_location_id y vendor_id, son buenas candidatas para el tipo de campo LowCardinality.
Optimizar el tipo de dato
Aplicar las optimizaciones
Observamos algunas mejoras tanto en el tiempo de consulta como en el uso de memoria. Gracias a la optimización del esquema de datos, reducimos el volumen total de datos, lo que se traduce en un menor consumo de memoria y en menos tiempo de procesamiento.
Comprobemos el tamaño de las tablas para ver la diferencia.
La importancia de las claves primarias
Los gránulos en ClickHouse son las unidades de datos más pequeñas que se leen durante la ejecución de consultas. Contienen hasta un número fijo de filas, determinado por index_granularity, con un valor predeterminado de 8192 filas. Los gránulos se almacenan de forma contigua y se ordenan según la clave primaria.Elegir un buen conjunto de claves primarias es importante para el rendimiento y, de hecho, es habitual almacenar los mismos datos en distintas tablas y usar diferentes conjuntos de claves primarias para acelerar un conjunto específico de consultas. Otras opciones compatibles con ClickHouse, como Projection o una vista materializada, permiten usar un conjunto diferente de claves primarias sobre los mismos datos. La segunda parte de esta serie de blogs tratará este tema con más detalle.
Elegir claves primarias
- Usa campos que se utilicen para filtrar en la mayoría de las consultas
- Elige primero las columnas con menor cardinalidad
- Considera incluir un componente temporal en tu clave primaria, ya que filtrar por tiempo en un conjunto de datos con
timestampes bastante común.
passenger_count, pickup_datetime y dropoff_datetime.
La cardinalidad de passenger_count es baja (24 valores únicos) y se usa en nuestras consultas lentas. También añadimos campos de timestamp (pickup_datetime y dropoff_datetime), ya que suelen usarse para filtrar.
Crea una nueva tabla con las claves primarias y vuelve a reingestar los datos.
| Consulta 1 | |||
|---|---|---|---|
| Ejecución 1 | Ejecución 2 | Ejecución 3 | |
| Tiempo transcurrido | 1.699 sec | 1.353 sec | 0.765 sec |
| Filas procesadas | 329.04 millones | 329.04 millones | 329.04 millones |
| Memoria máxima | 440.24 MiB | 337.12 MiB | 444.19 MiB |
| Consulta 2 | |||
|---|---|---|---|
| Ejecución 1 | Ejecución 2 | Ejecución 3 | |
| Tiempo transcurrido | 1.419 sec | 1.171 sec | 0.248 sec |
| Filas procesadas | 329.04 millones | 329.04 millones | 41.46 millones |
| Memoria máxima | 546.75 MiB | 531.09 MiB | 173.50 MiB |
| Consulta 3 | |||
|---|---|---|---|
| Ejecución 1 | Ejecución 2 | Ejecución 3 | |
| Transcurrido | 1.414 sec | 1.188 sec | 0.431 sec |
| Filas procesadas | 329.04 millones | 329.04 millones | 276.99 millones |
| Memoria máxima | 451.53 MiB | 265.05 MiB | 197.38 MiB |