Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

Dado que ClickHouse es un sistema especializado, es importante tener en cuenta las peculiaridades de su arquitectura a la hora de utilizarlo. En este informe, Alexey hablará sobre ejemplos de errores típicos al usar ClickHouse, que pueden conducir a un trabajo ineficiente. Usando ejemplos prácticos, mostraremos cómo la elección de uno u otro esquema de procesamiento de datos puede cambiar el rendimiento en órdenes de magnitud.

¡Hola a todos! Mi nombre es Alexey, hago ClickHouse.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

En primer lugar, me apresuro a complacerte de inmediato, no te diré hoy qué es ClickHouse. Para ser honesto, estoy cansado de eso. Te digo cada vez lo que es. Y probablemente todo el mundo ya lo sabe.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

En cambio, le diré cuál es el posible rake, es decir, cómo se puede hacer un mal uso de ClickHouse. De hecho, no debe tener miedo, porque estamos desarrollando ClickHouse como un sistema simple, conveniente y listo para usar. Instalado todo, sin problema.

Pero aún así, hay que tener en cuenta que este sistema es especializado y fácilmente puedes tropezarte con un caso de uso inusual que sacará a este sistema de su zona de confort.

Entonces, ¿qué son los rastrillos? Básicamente hablaré de las cosas obvias. Todo es obvio para todos, todos entienden todo y pueden alegrarse de ser tan inteligentes, y aquellos que no entienden aprenderán algo nuevo.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

El primer ejemplo más simple, que lamentablemente ocurre a menudo, es una gran cantidad de insertos con lotes pequeños, es decir, una gran cantidad de insertos pequeños.

Si consideramos cómo ClickHouse realiza una inserción, puede enviar al menos un terabyte de datos en una solicitud. No es un problema.

Y veamos cuál será el rendimiento típico. Por ejemplo, tenemos una tabla con datos de Yandex.Metrics. Golpes. 105 algunas columnas. 700 bytes sin comprimir. E insertaremos de buena manera lotes de un millón de líneas.

Insertamos en la tabla MergeTree, se obtienen medio millón de filas por segundo. Excelente. En una tabla replicada, será un poco menos, alrededor de 400 filas por segundo.

Y si activa la inserción de quórum, obtiene un rendimiento un poco menor, pero aún decente, 250 veces por segundo. La inserción de quórum es una característica no documentada en ClickHouse*.

* a partir de 2020, ya documentado.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

¿Qué pasa si lo haces mal? Insertamos una fila en la tabla MergeTree y obtenemos 59 filas por segundo. Esto es 10 veces lento. En ReplicatedMergeTree: 000 filas por segundo. Y si el quórum se enciende, se obtienen 6 líneas por segundo. En mi opinión, esto es una especie de mierda total. ¿Cómo puedes frenar así? Incluso dice en mi camiseta que ClickHouse no debe reducir la velocidad. Pero sin embargo sucede a veces.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

De hecho, este es nuestro defecto. Podríamos haber hecho que funcionara bien, pero no lo hicimos. Y no lo hicimos, porque nuestro guión no lo necesitaba. Ya teníamos lotes. Acabamos de recibir lotes en la entrada y sin problemas. Conéctelo y todo funciona bien. Pero, por supuesto, todo tipo de escenarios son posibles. Por ejemplo, cuando tiene un montón de servidores en los que se generan datos. Y no insertan datos con tanta frecuencia, pero aún reciben inserciones frecuentes. Y necesitas evitar esto de alguna manera.

Desde un punto de vista técnico, la conclusión es que cuando realiza una inserción en ClickHouse, los datos no ingresan en ninguna tabla de memoria. Ni siquiera tenemos una estructura de registro MergeTree real, sino solo un MergeTree, porque no hay registro ni memTable. Inmediatamente escribimos los datos en el sistema de archivos, ya descompuestos en columnas. Y si tiene 100 columnas, será necesario escribir más de 200 archivos en un directorio separado. Todo esto es muy engorroso.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

Y surge la pregunta: "¿Cómo hacerlo bien?" Si tal situación, aún necesita escribir datos en ClickHouse de alguna manera.

Método 1. Esta es la forma más fácil. Utilice algún tipo de cola distribuida. Por ejemplo, Kafka. Simplemente saca los datos de Kafka, los agrupamos una vez por segundo. Y todo estará bien, grabas, todo funciona bien.

Las desventajas son que Kafka es otro sistema distribuido engorroso. También entiendo si ya tienes a Kafka en tu empresa. Es bueno, es conveniente. Pero si no está allí, debe pensar tres veces antes de arrastrar otro sistema distribuido a su proyecto. Y por eso vale la pena considerar alternativas.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

Método 2. Aquí hay una alternativa tan antigua y al mismo tiempo muy simple. ¿Tiene algún tipo de servidor que genere sus registros? Y simplemente escribe sus registros en un archivo. Y una vez por segundo, por ejemplo, cambiamos el nombre de este archivo, abrimos uno nuevo. Y un script separado, ya sea por cron o algún demonio, toma el archivo más antiguo y lo escribe en ClickHouse. Si escribe registros una vez por segundo, entonces todo estará bien.

Pero la desventaja de este método es que si el servidor en el que se generan los registros ha desaparecido en alguna parte, los datos también desaparecerán.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

Método 3. Hay otra forma interesante, que es sin archivos temporales. Por ejemplo, tiene algún tipo de spinner publicitario o algún otro demonio interesante que genera datos. Y puede acumular un montón de datos directamente en la RAM, en el búfer. Y cuando pasa una cantidad de tiempo suficiente, deja este búfer a un lado, crea uno nuevo e inserta lo que ya se ha acumulado en ClickHouse en un hilo separado.

Por otro lado, los datos también desaparecen con kill -9. Si su servidor se cae, perderá estos datos. Y otro problema es que si no puede escribir en la base de datos, sus datos se acumularán en la RAM. Y o se agota la memoria RAM, o simplemente se pierden datos.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

Método 4. Otra forma interesante. ¿Tiene algún proceso de servidor. Y puede enviar datos a ClickHouse a la vez, pero hacerlo en una sola conexión. Por ejemplo, envié una solicitud http con codificación de transferencia: fragmentada con inserción. Y genera fragmentos con poca frecuencia, puede enviar cada línea, aunque habrá una sobrecarga para enmarcar estos datos.

No obstante, en este caso, los datos se enviarán a ClickHouse de forma inmediata. Y el propio ClickHouse los almacenará en búfer.

Pero también hay problemas. Ahora perderá datos, incluso cuándo se cancela su proceso y si se cancela el proceso de ClickHouse, porque será una inserción incompleta. Y en ClickHouse, las inserciones son atómicas hasta un umbral específico en el tamaño de las filas. En principio, esta es una forma interesante. También se puede utilizar.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

Método 5. Aquí hay otra forma interesante. Este es algún tipo de servidor desarrollado por la comunidad para el procesamiento por lotes de datos. No lo he mirado yo mismo, así que no puedo garantizar nada. Sin embargo, no hay garantías para ClickHouse en sí. Esto también es de código abierto, pero por otro lado, podría acostumbrarse a algún estándar de calidad que tratamos de proporcionar. Pero para esto, no sé, ve a GitHub, mira el código. Tal vez escribieron algo bueno.

* a partir de 2020, también debe agregarse a la consideración casa del gatito.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

Método 6. Otra forma es usar tablas de búfer. La ventaja de este método es que es muy fácil de empezar a utilizar. Cree una tabla Buffer e insértela.

Pero la desventaja es que el problema no está completamente resuelto. Si a una tasa del tipo MergeTree debe agrupar datos en un lote por segundo, entonces a una tasa en una tabla de búfer, debe agrupar al menos hasta varios miles por segundo. Si hay más de 10 por segundo, seguirá siendo malo. Y si inserta en lotes, vio que allí se obtienen cien mil líneas por segundo. Y esto ya está en datos bastante pesados.

Y también las tablas de búfer no tienen un registro. Y si algo está mal con su servidor, entonces los datos se perderán.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

Y como beneficio adicional, recientemente tuvimos la oportunidad de recopilar datos de Kafka en ClickHouse. Hay un motor de mesa: Kafka. Simplemente estás creando. Y puedes colgar vistas materializadas en él. En este caso, sacará los datos de Kafka y los insertará en las tablas que necesites.

Y lo que es especialmente agradable de esta oportunidad es que no la logramos. Esta es una característica de la comunidad. Y cuando digo "característica de la comunidad", lo digo sin ningún desprecio. Leímos el código, hicimos una revisión, debería funcionar bien.

* a partir de 2020, existe un soporte similar para RabbitMQ.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

¿Qué más puede ser inconveniente o inesperado al insertar datos? Si realiza una consulta de inserción de valores y escribe algunas expresiones calculadas en valores. Por ejemplo, now() también es una expresión evaluada. Y en este caso, ClickHouse se ve obligado a ejecutar el intérprete de estas expresiones para cada línea, y el rendimiento se reducirá en órdenes de magnitud. Mejor evitarlo.

* por el momento, el problema está completamente resuelto, no hay más regresión de rendimiento al usar expresiones en VALUES.

Otro ejemplo en el que puede haber algunos problemas es cuando los datos de un lote pertenecen a un grupo de particiones. De forma predeterminada, ClickHouse realiza las particiones por mes. Y si inserta un lote de un millón de filas y hay datos de varios años, tendrá varias docenas de particiones allí. Y esto es equivalente al hecho de que habrá lotes varias decenas de veces más pequeños, porque en su interior siempre se dividen primero en particiones.

* recientemente en ClickHouse en modo experimental se agregó soporte para el formato compacto de fragmentos y fragmentos en RAM con registro de escritura anticipada, lo que resuelve casi por completo el problema.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

Ahora considere el segundo tipo de problema: tipificación de datos.

La tipificación de datos puede ser estricta y, a veces, una cadena. Cadena: esto es cuando acaba de tomar y declarar que tiene todos los campos de tipo cadena. apesta No tienes que hacer eso.

Averigüemos cómo hacerlo bien en los casos en que desee decir que tenemos algún campo, una cadena, y dejemos que ClickHouse lo descubra por sí solo, pero no tomaré un baño de vapor. Pero todavía vale la pena poner un poco de esfuerzo.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

Por ejemplo, tenemos una dirección IP. En un caso, lo guardamos como una cadena. Por ejemplo, 192.168.1.1. En caso contrario, será un número de tipo UInt32*. 32 bits es suficiente para una dirección IPv4.

Primero, por extraño que parezca, los datos se comprimirán casi igual. Habrá una diferencia, seguro, pero no tan grande. Por lo tanto, no hay problemas especiales con la E/S de disco.

Pero hay una gran diferencia en el tiempo de CPU y el tiempo de ejecución de consultas.

Contemos la cantidad de direcciones IP únicas si se almacenan como números. Resulta 137 millones de líneas por segundo. Si es lo mismo que líneas, entonces 37 millones de líneas por segundo. No sé por qué sucedió esta coincidencia. He hecho estas solicitudes yo mismo. Pero sin embargo alrededor de 4 veces más lento.

Y si calcula la diferencia en el espacio en disco, entonces también hay una diferencia. Y la diferencia es de aproximadamente una cuarta parte, porque hay muchas direcciones IP únicas. Y si hubiera líneas con una pequeña cantidad de valores diferentes, se habrían comprimido silenciosamente en el diccionario en aproximadamente el mismo volumen.

Y la diferencia horaria cuádruple no está tirada en el camino. Tal vez a ti, por supuesto, no te importe, pero cuando veo tanta diferencia, me siento triste.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

Consideremos diferentes casos.

1. Un caso en el que tiene pocos valores únicos diferentes. En este caso, usamos una práctica simple que probablemente conozca y pueda usar para cualquier SGBD. Todo esto tiene sentido no solo para ClickHouse. Simplemente escriba los identificadores numéricos en la base de datos. Y puede convertir a cadenas y volver al lado de su aplicación.

Por ejemplo, usted tiene una región. Y está tratando de guardarlo como una cadena. Y estará escrito allí: Moscú y Región de Moscú. Y cuando veo que "Moscú" está escrito allí, todavía no es nada, y cuando es MO, de alguna manera se vuelve completamente triste. Esa es la cantidad de bytes.

En su lugar, simplemente anotamos el número Ulnt32 y 250. Tenemos 250 en Yandex, pero el tuyo puede ser diferente. Por si acaso, diré que ClickHouse tiene una capacidad integrada para trabajar con una geobase. Simplemente escriba un directorio con regiones, incluido uno jerárquico, es decir, habrá Moscú, Región de Moscú y todo lo que necesite. Y puede convertir en el nivel de solicitud.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

La segunda opción es casi igual, pero con soporte dentro de ClickHouse. Es un tipo de datos Enum. Simplemente escriba todos los valores que necesita dentro del Enum. Por ejemplo, el tipo de dispositivo y escribe allí: escritorio, móvil, tableta, TV. Solo 4 opciones.

La desventaja es que necesita modificar periódicamente. Solo se ha agregado una opción. Hacemos mesa de altar. De hecho, modificar la tabla en ClickHouse es gratis. Especialmente gratis para Enum porque los datos en el disco no cambian. Sin embargo, alter adquiere un bloqueo * en la tabla y debe esperar hasta que se completen todas las selecciones. Y solo después de que se ejecute este alter, es decir, todavía hay algunos inconvenientes.

* en versiones recientes de ClickHouse, ALTER se hace completamente sin bloqueo.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

Otra opción bastante única para ClickHouse es la conexión de diccionarios externos. Puede escribir números en ClickHouse y mantener sus directorios en cualquier sistema conveniente para usted. Por ejemplo, puede usar: MySQL, Mongo, Postgres. Incluso puede crear su propio microservicio, que enviará estos datos a través de http. Y en el nivel de ClickHouse, escribe una función que convertirá estos datos de números a cadenas.

Esta es una forma especializada pero muy eficiente de realizar una combinación en una tabla externa. Y hay dos opciones. En una opción, estos datos se almacenarán completamente en caché, estarán completamente presentes en la RAM y se actualizarán en algunos intervalos. Y en otra opción, si estos datos no caben en la RAM, puede almacenarlos parcialmente en caché.

Aquí hay un ejemplo. Existe Yandex.Direct. Y hay una empresa de publicidad y pancartas. Probablemente hay decenas de millones de empresas de publicidad. Y encajar aproximadamente en la memoria RAM. Y hay miles de millones de pancartas que no encajan. Y estamos usando un diccionario almacenado en caché de MySQL.

El único problema es que el diccionario en caché funcionará bien si la tasa de aciertos es cercana al 100%. Si es más pequeño, al procesar solicitudes para cada paquete de datos, será necesario tomar las claves que faltan e ir a tomar datos de MySQL. Acerca de ClickHouse, todavía puedo garantizar que sí, no se ralentiza, no hablaré de otros sistemas.

Y como beneficio adicional, los diccionarios son una forma muy fácil de actualizar los datos en ClickHouse de forma retroactiva. Es decir, tenías un informe de empresas de publicidad, el usuario simplemente cambió la empresa de publicidad y en todos los datos antiguos, en todos los informes, estos datos también cambiaron. Si escribe filas directamente en la tabla, no podrá actualizarlas.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

Otra forma cuando no sabe dónde obtener los identificadores de sus cadenas. puedes hacer hash. Y la opción más sencilla es tomar un hash de 64 bits.

El único problema es que si el hash es de 64 bits, es casi seguro que tendrá colisiones. Porque si hay mil millones de líneas, entonces la probabilidad ya se está volviendo tangible.

Y no sería muy bueno hacer trizas los nombres de las empresas de publicidad de esa manera. Si se mezclan las campañas publicitarias de diferentes empresas, habrá algo incomprensible.

Y hay un truco simple. Es cierto que tampoco es muy adecuado para datos serios, pero si algo no es muy serio, simplemente agregue otro identificador de cliente a la clave del diccionario. Y luego tendrá colisiones, pero solo dentro de un cliente. Y usamos este método para el mapa de enlaces en Yandex.Metrica. Tenemos direcciones URL allí, almacenamos hashes. Y sabemos que hay conflictos, por supuesto. Pero cuando se muestra una página, entonces la probabilidad de que esté en una página para un usuario de que algunas URL se mantengan juntas y esto se notará, entonces esto puede despreciarse.

Como beneficio adicional, para muchas operaciones, solo los hashes son suficientes y las cadenas en sí no se pueden almacenar en ningún lado.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

Otro ejemplo si las cadenas son cortas, como dominios de sitios web. Se pueden almacenar tal cual. O, por ejemplo, el idioma del navegador ru es de 2 bytes. Por supuesto, lo siento por los bytes, pero no te preocupes, 2 bytes no son una pena. Por favor, mantenlo como está, no te preocupes.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

Otro caso es cuando, por el contrario, hay muchas cadenas y al mismo tiempo hay muchas únicas en ellas, e incluso el conjunto es potencialmente ilimitado. Un ejemplo típico son las frases de búsqueda o las URL. Frases de búsqueda, incluso debido a errores tipográficos. Veamos cuántas frases de búsqueda únicas por día. Y resulta que son casi la mitad de todos los eventos. Y en este caso, podría pensar que necesita normalizar los datos, contar los identificadores, colocarlos en una tabla separada. Pero no tienes que hacer eso. Solo mantén estas líneas como están.

Mejor: no invente nada, porque si lo almacena por separado, deberá hacer una unión. Y esta unión es, en el mejor de los casos, un acceso aleatorio a la memoria, si aún cabe en la memoria. Si no encaja, habrá problemas en general.

Y si los datos se almacenan en su lugar, simplemente se leen en el orden correcto desde el sistema de archivos y todo está bien.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

Si tiene direcciones URL o alguna otra cadena larga y compleja, debe pensar en el hecho de que puede calcular un poco de compresión por adelantado y escribirlo en una columna separada.

Para las URL, por ejemplo, puede almacenar el dominio por separado. Y si realmente necesita un dominio, simplemente use esta columna, y las URL mentirán y ni siquiera las tocará.

Veamos cuál es la diferencia. ClickHouse tiene una función especializada que calcula el dominio. Es muy rápido, lo hemos optimizado. Y, para ser honesto, ni siquiera cumple con el RFC, pero sin embargo considera todo lo que necesitamos.

Y en un caso, simplemente obtendremos las URL y calcularemos el dominio. Resulta 166 milisegundos. Y si toma un dominio listo para usar, resulta solo 67 milisegundos, es decir, casi tres veces más rápido. Y más rápido, no porque necesitemos hacer algunos cálculos, sino porque leemos menos datos.

Por alguna razón, una solicitud, que es más lenta, obtiene más velocidad en gigabytes por segundo. Porque lee más gigabytes. Estos son datos completamente redundantes. La solicitud parece ejecutarse más rápido, pero tarda más en completarse.

Y si observa la cantidad de datos en el disco, resulta que la URL tiene 126 megabytes y el dominio tiene solo 5 megabytes. Resulta 25 veces menos. Sin embargo, la consulta sigue siendo solo 4 veces más rápida. Pero eso es porque los datos están calientes. Y si estuviera frío, probablemente sería 25 veces más rápido debido a la E/S del disco.

Por cierto, si evalúa cuánto el dominio es menor que la URL, resulta ser aproximadamente 4 veces, pero por alguna razón, los datos en el disco ocupan 25 veces menos. ¿Por qué? Por compresión. Y la url está comprimida, y el dominio está comprimido. Pero a menudo la URL contiene un montón de basura.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

Y, por supuesto, vale la pena usar los tipos de datos correctos que están diseñados específicamente para los valores correctos o que se ajustan. Si está en IPv4, almacene UInt32*. Si es IPv6, entonces FixedString(16), porque una dirección IPv6 es de 128 bits, es decir, se almacena directamente en formato binario.

Pero, ¿qué pasa si a veces tienes direcciones IPv4 y otras veces IPv6? Sí, puedes quedarte con los dos. Una columna para IPv4, otra para IPv6. Por supuesto, hay una opción para mapear IPv4 a IPv6. Esto también funcionará, pero si a menudo necesita una dirección IPv4 en sus solicitudes, sería bueno ponerla en una columna separada.

* Ahora ClickHouse tiene tipos de datos IPv4 e IPv6 separados que almacenan datos tan eficientemente como números, pero los representan tan convenientemente como cadenas.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

También es importante tener en cuenta que vale la pena preprocesar los datos por adelantado. Por ejemplo, le llegan algunos registros sin procesar. Y, quizás, no debas ponerlos en ClickHouse de inmediato, aunque es muy tentador no hacer nada y todo funcionará. Pero aún vale la pena realizar aquellos cálculos que sean posibles.

Por ejemplo, la versión del navegador. En algún departamento vecino, que no quiero señalar con el dedo, la versión del navegador está almacenada allí así, es decir, como una cadena: 12.3. Y luego, para hacer un informe, toman esta cadena y la dividen por una matriz, y luego por el primer elemento de la matriz. Naturalmente, todo se ralentiza. Pregunté por qué hacen esto. Me dijeron que no les gusta la optimización prematura. Y no me gusta el pesimismo prematuro.

Entonces en este caso sería más correcto dividir en 4 columnas. No tengas miedo aquí, porque esto es ClickHouse. ClickHouse es una base de datos de columnas. Y cuantas más pequeñas columnas ordenadas, mejor. Habrá 5 BrowserVersion, haga 5 columnas. Esto esta bien.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

Ahora considere qué hacer si tiene muchas cadenas muy largas, arreglos muy largos. No es necesario almacenarlos en ClickHouse en absoluto. En cambio, puede almacenar solo algún identificador en ClickHouse. Y estas largas filas los empujan a algún otro sistema.

Por ejemplo, uno de nuestros servicios de análisis tiene algunos parámetros de eventos. Y si llegan muchos parámetros a los eventos, simplemente guardamos los primeros 512 que aparecen, porque 512 no es una pena.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

Y si no puede decidir sobre sus tipos de datos, también puede escribir datos en ClickHouse, pero en una tabla temporal del tipo Log, que es especial para datos temporales. Después de eso, puede analizar qué tipo de distribución de valores tiene allí, qué hay generalmente y crear los tipos correctos.

* Ahora ClickHouse tiene un tipo de datos Cardinalidad baja lo que le permite almacenar cadenas de manera eficiente con menos esfuerzo.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

Ahora considere otro caso interesante. A veces las cosas funcionan de una manera extraña para las personas. Voy y veo esto. E inmediatamente parece que esto lo hizo un administrador inteligente y muy experimentado que tiene una amplia experiencia en la configuración de MySQL versión 3.23.

Aquí vemos mil tablas, cada una de las cuales contiene el resto de dividir no está claro qué por mil.

En principio, respeto la experiencia de otras personas, incluida la comprensión de qué tipo de sufrimiento se puede obtener con esta experiencia.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

Y las razones son más o menos claras. Estos son viejos estereotipos que pueden haberse acumulado al trabajar con otros sistemas. Por ejemplo, las tablas MyISAM no tienen una clave principal agrupada. Y esta forma de compartir datos puede ser un intento desesperado por obtener la misma funcionalidad.

Otra razón es que es difícil realizar operaciones de modificación en tablas grandes. Todo estará bloqueado. Aunque en las versiones modernas de MySQL, este problema ya no es tan grave.

O, por ejemplo, microfragmentación, pero hablaremos de eso más adelante.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

En ClickHouse, no necesita hacer esto porque, en primer lugar, la clave principal está agrupada, los datos están ordenados por la clave principal.

Y a veces la gente me pregunta: "¿Cómo cambia el rendimiento de las consultas de rango en ClickHouse con el tamaño de la tabla?". Yo digo que no cambia en absoluto. Por ejemplo, tiene una tabla con mil millones de filas y está leyendo un rango de un millón de filas. Todo esta bien. Si la tabla tiene un billón de filas y está leyendo un millón de filas, entonces será casi lo mismo.

Y, en segundo lugar, no se requieren piezas como particiones manuales. Si entra y mira lo que hay en el sistema de archivos, verá que una tabla es algo bastante serio. Y ahí dentro hay algo así como tabiques. Es decir, ClickHouse hace todo por ti y no necesitas sufrir.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

Modificar en ClickHouse es gratuito si modifica la columna Agregar/Soltar.

Y no debe hacer tablas pequeñas, porque si tiene 10 filas o 10 filas en su tabla, entonces no importa en absoluto. ClickHouse es un sistema que optimiza el rendimiento, no la latencia, por lo que no tiene sentido procesar 000 líneas.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

Es correcto usar una mesa grande. Deshazte de los viejos estereotipos, todo estará bien.

Y como beneficio adicional, en la última versión, tenemos la oportunidad de crear una clave de partición arbitraria para realizar todo tipo de operaciones de mantenimiento en particiones individuales.

Por ejemplo, necesita muchas tablas pequeñas, por ejemplo, cuando es necesario procesar algunos datos intermedios, recibe fragmentos y necesita realizar una transformación en ellos antes de escribir en la tabla final. Para este caso, existe un maravilloso motor de tablas: StripeLog. Es como TinyLog, solo que mejor.

* Ahora ClickHouse tiene más entrada de función de tabla.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

Otro antipatrón es el microsharding. Por ejemplo, necesita fragmentar datos y tiene 5 servidores, y mañana habrá 6 servidores. Y piensas cómo reequilibrar estos datos. Y en cambio, no te estás dividiendo en 5 fragmentos, sino en 1 fragmentos. Y luego asigna cada uno de estos microfragmentos a un servidor separado. Y tendrá éxito en un servidor, por ejemplo, 000 ClickHouse, por ejemplo. Instancia separada en puertos separados o bases de datos separadas.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

Pero en ClickHouse esto no es muy bueno. Porque incluso una instancia de ClickHouse intenta usar todos los recursos disponibles del servidor para procesar una solicitud. Es decir, tienes algún tipo de servidor y ahí, por ejemplo, 56 núcleos de procesador. Está ejecutando una consulta que tarda un segundo y utilizará 56 núcleos. Y si colocó 200 ClickHouses en un servidor allí, resulta que se iniciarán 10 subprocesos. En general, todo será muy malo.

Otra razón es que la distribución del trabajo entre estas instancias será desigual. Algunos terminarán antes, algunos terminarán más tarde. Si todo esto sucediera en una sola instancia, ClickHouse habría descubierto cómo distribuir correctamente los datos entre los flujos.

Y otra razón es que tendrá comunicación entre procesadores sobre TCP. Los datos deberán serializarse, deserializarse, y esto es una gran cantidad de microfragmentos. Simplemente no funcionará.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

Otro antipatrón, aunque difícilmente puede llamarse antipatrón. Esta es una gran cantidad de agregación previa.

En general, la agregación previa es buena. Tenía mil millones de filas, las agregó y se convirtieron en 1 filas, y ahora la consulta se ejecuta instantáneamente. Todo esta bien. Así es como puedes hacerlo. Y para esto, incluso ClickHouse tiene un tipo de tabla AggregatingMergeTree especial que realiza una agregación incremental a medida que se insertan los datos.

Pero hay momentos en los que piensa que agregaremos datos como este y datos agregados como este. Y en algún departamento vecino, tampoco quiero decir cuál, usan tablas SummingMergeTree para resumir por clave principal, y se usan 20 columnas como clave principal. Por si acaso, cambié los nombres de algunas columnas por conspiración, pero eso es todo.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

Y tales problemas surgen. Primero, la cantidad de datos que tiene no se reduce demasiado. Por ejemplo, se reduce tres veces. Tres veces sería un buen precio para pagar el análisis ilimitado que conlleva tener datos no agregados. Si los datos se agregan, solo obtiene estadísticas miserables en lugar de análisis.

¿Y qué es especialmente bueno? Que estas personas del siguiente departamento vayan y pidan a veces que agreguen una columna más a la clave principal. Es decir, hemos agregado los datos así, y ahora queremos un poco más. Pero no hay una clave principal alterada en ClickHouse. Por lo tanto, debe escribir algunos scripts en C ++. Y no me gustan los scripts, incluso si están en C++.

Y si observa para qué se creó ClickHouse, entonces los datos no agregados son exactamente el escenario para el que nació. Si está utilizando ClickHouse para datos no agregados, entonces lo está haciendo todo bien. Si está agregando, entonces esto a veces es perdonable.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

Otro caso interesante son las solicitudes en un bucle infinito. A veces voy a algún servidor de producción y miro la lista de procesos de la muestra allí. Y cada vez que descubro que algo terrible está pasando.

Por ejemplo, aquí está esto. Inmediatamente queda claro que era posible hacer todo en una sola solicitud. Simplemente escriba la URL y la lista allí.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

¿Por qué muchas de estas solicitudes en un ciclo infinito son malas? Si no se utiliza el índice, tendrá muchas pasadas sobre los mismos datos. Pero si se usa un índice, por ejemplo, tiene una clave principal en ru y escribe url = algo allí. Y cree que una URL se leerá puntualmente de la tabla, todo estará bien. Pero realmente no. Porque ClickHouse hace todo por lotes.

Cuando necesita leer algún rango de datos, lee un poco más, porque el índice en ClickHouse es escaso. Este índice no le permite encontrar una fila individual en la tabla, solo algún tipo de rango. Y los datos se comprimen en bloques. Para leer una línea, debe tomar todo el bloque y descomprimirlo. Y si ejecuta un montón de consultas, tendrá muchas intersecciones de esas, y tendrá mucho trabajo hecho una y otra vez.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

Y como beneficio adicional, puede ver que en ClickHouse no debe tener miedo de transferir incluso megabytes e incluso cientos de megabytes a la sección IN. Recuerdo de nuestra práctica que si pasamos un montón de valores en la sección IN en MySQL, por ejemplo, pasamos 100 megabytes de algunos números allí, luego MySQL consume 10 gigabytes de memoria y no pasa nada más. eso, todo funciona mal.

Y lo segundo es que en ClickHouse, si sus consultas usan un índice, entonces no es más lento que un escaneo completo, es decir, si necesita leer casi toda la tabla, irá secuencialmente y leerá toda la tabla. En general, él lo resolverá.

Sin embargo, hay algunas dificultades. Por ejemplo, ese IN con una subconsulta no usa el índice. Pero este es nuestro problema y tenemos que solucionarlo. No hay nada fundamental aquí. Vamos a hacerlo*.

Y otra cosa interesante es que si tiene una solicitud muy larga y se está procesando una solicitud distribuida, esta solicitud muy larga se enviará a cada servidor sin compresión. Por ejemplo, 100 megas y 500 servidores. Y, en consecuencia, se transferirán 50 gigabytes a través de la red. Se transferirá y luego todo se ejecutará con éxito.

* ya usando; todo se solucionó como se prometió.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

Y es bastante común si las solicitudes provienen de la API. Por ejemplo, has realizado algún tipo de servicio. Y si alguien necesita tu servicio, entonces abriste la API y, literalmente, dos días después, ves que algo incomprensible está sucediendo. Todo está sobrecargado y están llegando algunas solicitudes terribles que nunca deberían haber sido.

Y solo hay una solución. Si ha abierto la API, tendrá que cortarla. Por ejemplo, para ingresar algunas cuotas. No hay otras opciones razonables. De lo contrario, inmediatamente escribirán un guión y habrá problemas.

Y ClickHouse tiene una característica especial: este es el cálculo de cuotas. Además, puede transferir su clave de cuota. Este es, por ejemplo, un ID de usuario interno. Y las cuotas se calcularán de forma independiente para cada uno de ellos.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

Ahora otra cosa interesante. Esta es la replicación manual.

Conozco muchos casos en los que, a pesar de que ClickHouse tiene soporte de replicación incorporado, las personas replican ClickHouse manualmente.

¿Cuál es el principio? Tiene una canalización de procesamiento de datos. Y funciona de forma independiente, por ejemplo, en diferentes centros de datos. Escribe los mismos datos de la misma manera en ClickHouse, por así decirlo. Es cierto que la práctica muestra que los datos seguirán divergiendo debido a algunas peculiaridades de su código. Espero que en el tuyo.

Y periódicamente todavía tienes que sincronizar manualmente. Por ejemplo, una vez al mes los administradores hacen rsync.

De hecho, es mucho más fácil usar la replicación integrada en ClickHouse. Pero puede haber algunas contraindicaciones, porque para esto necesitas usar ZooKeeper. No diré nada malo de ZooKeeper, en principio el sistema funciona, pero pasa que la gente no lo usa por javafobia, porque ClickHouse es un sistema tan bueno escrito en C++ que puedes usar y todo estará bien. Y ZooKeeper en java. Y de alguna manera ni siquiera quiere mirar, pero luego puede usar la replicación manual.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

ClickHouse es un sistema práctico. Tiene en cuenta tus necesidades. Si tiene una replicación manual, puede crear una tabla distribuida que analice sus réplicas manuales y realice una conmutación por error entre ellas. E incluso hay una opción especial que le permite evitar los fracasos, incluso si sus líneas son sistemáticamente divergentes.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

Además, puede haber problemas si usa motores de tablas primitivos. ClickHouse es un constructor de este tipo que tiene un montón de motores de tablas diferentes. Para todos los casos graves, como está escrito en la documentación, use tablas de la familia MergeTree. Y todo lo demás, esto es así, para casos individuales o para pruebas.

En una tabla MergeTree, no necesita tener fecha ni hora. Todavía puedes usar. Si no hay fecha y hora, escriba que el valor predeterminado es 2000. Funcionará y no requerirá recursos.

Y en la nueva versión del servidor, incluso puede especificar que tiene particiones personalizadas sin una clave de partición. Será lo mismo.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

Por otro lado, se pueden utilizar motores de tablas primitivos. Por ejemplo, complete los datos una vez y vea, gire y elimine. Puede utilizar Registro.

O el almacenamiento de pequeños volúmenes para procesamiento intermedio es StripeLog o TinyLog.

La memoria se puede usar si hay una pequeña cantidad de datos y simplemente girar algo en la RAM.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

A ClickHouse no le gustan mucho los datos renormalizados.

Aquí está un ejemplo típico. Esta es una gran cantidad de URL. Los pones en la mesa adyacente. Y luego decidimos hacer JOIN con ellos, pero esto no funcionará, por regla general, porque ClickHouse solo admite Hash JOIN. Si no hay suficiente RAM para una gran cantidad de datos con los que conectarse, JOIN no funcionará *.

Si los datos son de alta cardinalidad, no se preocupe, guárdelos en un formato no normalizado, las URL están directamente en la tabla principal.

* y ahora ClickHouse también tiene una combinación de combinación, y funciona en condiciones en las que los datos intermedios no caben en la RAM. Pero esto es ineficaz y la recomendación sigue siendo válida.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

Un par de ejemplos más, pero ya dudo si son antipatrones o no.

ClickHouse tiene un inconveniente conocido. No sabe cómo actualizar *. En cierto sentido, esto es incluso bueno. Si tiene algunos datos importantes, por ejemplo, contabilidad, nadie podrá enviarlos porque no hay actualizaciones.

* Hace tiempo que se agregó soporte para actualizar y eliminar en modo por lotes.

Pero hay algunas formas especiales que permiten que las actualizaciones aparezcan en segundo plano. Por ejemplo, tablas de tipo ReplaceMergeTree. Hacen actualizaciones durante las fusiones de fondo. Puede forzar esto con la tabla de optimización. Pero no lo haga con demasiada frecuencia, porque sobrescribirá completamente la partición.

JOIN distribuidos en ClickHouse: el planificador de consultas también lo maneja mal.

Mal, pero a veces bien.

Usando ClickHouse solo para leer datos con select*.

No recomendaría usar ClickHouse para cálculos voluminosos. Pero esto no es del todo cierto, porque ya nos estamos alejando de esta recomendación. Y recientemente agregamos la capacidad de aplicar modelos de aprendizaje automático en ClickHouse - Catboost. Y me preocupa, porque pienso: “Qué horror. ¡Esto es cuántos ciclos por byte resultan! Es una pena para mí iniciar ciclos de reloj en bytes.

Uso efectivo de ClickHouse. Alexei Milovidov (Yandex)

Pero no tengas miedo, instala ClickHouse, todo estará bien. En todo caso, tenemos una comunidad. Por cierto, la comunidad eres tú. Y si tienes algún problema, al menos puedes ir a nuestro chat, y espero que te ayuden.

preguntas

¡Gracias por el informe! ¿Dónde quejarse del bloqueo de ClickHouse?

Puedes reclamarme personalmente ahora mismo.

Recientemente comencé a usar ClickHouse. Inmediatamente dejó caer la interfaz cli.

Tienes suerte

Un poco más tarde, dejé caer el servidor con una pequeña selección.

Tienes talento.

Abrí un error de GitHub, pero fue ignorado.

Ya veremos

Aleksey me engañó para que asistiera al informe, prometiéndome decirme cómo estás extrayendo los datos dentro.

Muy simple.

Esto es lo que me di cuenta ayer. Más detalles.

No hay trucos terribles. Es solo compresión bloque por bloque. El valor predeterminado es LZ4, puede habilitar ZSTD*. Bloques desde 64 kilobytes hasta 1 megabyte.

* también hay soporte para códecs de compresión especializados que se pueden usar en cadena con otros algoritmos.

¿Los bloques son solo datos sin procesar?

No exactamente crudo. Hay matrices. Si tiene una columna numérica, los números en una fila se apilan en una matriz.

Yo veo

Alexey, un ejemplo que fue con uniqExact sobre IP, es decir, el hecho de que uniqExact tarda más en contar por cadenas que por números, y así sucesivamente. ¿Y si aplicamos una finta con las orejas y echamos en el momento de la revisión? Es decir, parece que has dicho que no difiere mucho en el disco. Si leemos líneas del disco, lanzamos, ¿tendremos agregados más rápido o no? ¿O todavía estamos ganando marginalmente aquí? Me parece que lo probaste, pero por alguna razón no lo indicaste en el benchmark.

Creo que será más lento que sin elenco. En este caso, la dirección IP debe analizarse a partir de la cadena. Por supuesto, en ClickHouse, el análisis de direcciones IP también está optimizado. Nos esforzamos mucho, pero en el mismo lugar tienes los números escritos en forma de diezmilésima. Muy incómodo. Por otro lado, la función uniqExact funcionará más lentamente en cadenas, no solo porque se trata de cadenas, sino también porque se elige una especialización diferente del algoritmo. Las cadenas simplemente se manejan de manera diferente.

¿Y si tomamos un tipo de datos más primitivo? Por ejemplo, anotaron la identificación de usuario que tenemos, la escribieron como una línea y luego la lanzaron, ¿será más divertido o no?

Yo dudo. Creo que será aún más triste porque, después de todo, analizar números es un problema grave. Me parece que este colega incluso tenía un informe sobre lo difícil que es analizar números en forma de diezmilésima, pero tal vez no.

Alexey, ¡muchas gracias por el informe! ¡Y muchas gracias por ClickHouse! Tengo una pregunta sobre los planes. ¿Hay alguna característica en los planes para actualizar los diccionarios de forma incompleta?

es decir, reinicio parcial?

Sí Sí. Como la capacidad de establecer un campo MySQL allí, es decir, actualizar después para que solo se carguen estos datos si el diccionario es muy grande.

Característica muy interesante. Y, me parece, alguien lo sugirió en nuestro chat. Tal vez incluso fuiste tú.

No me parece.

Genial, ahora resulta que dos peticiones. Y puedes empezar a hacerlo lentamente. Pero quiero advertirte de inmediato que esta característica es bastante simple de implementar. Es decir, en teoría, solo necesita escribir el número de versión en la tabla y luego escribir: la versión es menor que tal y cual. Y esto significa que, muy probablemente, lo ofreceremos a los entusiastas. ¿Eres un entusiasta?

Sí, pero desafortunadamente no en C++.

¿Tus colegas pueden escribir en C++?

Encontraré a alguien.

Excelente*.

* la función se agregó dos meses después del informe: fue desarrollada por el autor de la pregunta y enviada por su solicitud de extracción.

Gracias!

¡Hola! ¡Gracias por el informe! Usted mencionó que ClickHouse consume muy bien todos los recursos disponibles. Y el orador junto a Luxoft habló sobre su decisión para el Russian Post. Dijo que les gustaba mucho ClickHouse, pero que no lo usaban en lugar de su principal competidor precisamente porque se comía todo el procesador. Y no pudieron encajarlo en su arquitectura, en su ZooKeeper con muelles. ¿Es posible restringir de alguna manera ClickHouse para que no consuma todo lo que está disponible para él?

Sí, es posible y muy fácil. Si desea consumir menos núcleos, simplemente escriba set max_threads = 1. Y eso es todo, ejecutará la solicitud en un núcleo. Además, puede especificar diferentes configuraciones para diferentes usuarios. Asique no hay problema. Y dígales a sus colegas de Luxoft que no es bueno que no hayan encontrado esta configuración en la documentación.

Alexey, hola! Me gustaría hacer esta pregunta. Esta no es la primera vez que escucho que muchas personas están comenzando a usar ClickHouse como repositorio de registros. En el informe, dijo que no hiciera esto, es decir, no necesita almacenar largas filas. ¿Qué piensa usted al respecto?

En primer lugar, los registros no suelen ser líneas largas. Hay, por supuesto, excepciones. Por ejemplo, algún servicio escrito en Java arroja una excepción, se registra. Y así en un bucle sin fin, y quedándose sin espacio en el disco duro. La solución es muy simple. Si las líneas son muy largas, entonces córtelas. ¿Qué significa largo? Decenas de kilobytes es malo *.

* en versiones recientes de ClickHouse, la "granularidad de índice adaptable" está habilitada, lo que elimina el problema de almacenar cadenas largas en su mayor parte.

¿Es normal un kilobyte?

Normalmente.

¡Hola! ¡Gracias por el informe! Ya pregunté sobre esto en el chat, pero no recuerdo si recibí una respuesta. ¿Hay algún plan para ampliar la sección CON en forma de CTE?

Aún no. La sección CON es algo frívola. Es como una pequeña característica para nosotros.

Entiendo. ¡Gracias!

¡Gracias por el informe! ¡Muy interesante! pregunta mundial. ¿Está previsto hacer, quizás en forma de algún tipo de stubs, modificación de borrado de datos?

Necesariamente. Esta es nuestra primera tarea en nuestra cola. Ahora estamos pensando activamente en cómo hacer todo bien. Y deberías empezar a pulsar el teclado*.

* apretó los botones del teclado y todo estaba listo.

¿Afectará de alguna manera el rendimiento del sistema o no? ¿Será la inserción tan rápida como ahora?

Quizás las eliminaciones en sí mismas, las actualizaciones en sí mismas sean muy pesadas, pero esto no afectará el rendimiento de las selecciones y el rendimiento de las inserciones de ninguna manera.

Y una pequeña pregunta más. En la presentación, hablaste sobre la clave principal. En consecuencia, tenemos partición, que es mensual por defecto, ¿no? Y cuando establecemos un rango de fechas que se ajusta a un mes, solo leemos esta partición, ¿verdad?

Sí.

Una pregunta. Si no podemos seleccionar ninguna clave primaria, entonces ¿es correcto hacerlo exactamente por el campo “Fecha” para que en el fondo haya una reestructuración menor de estos datos para que encajen de manera más ordenada? Si no tiene consultas de rango y ni siquiera puede seleccionar ninguna clave principal, ¿vale la pena poner una fecha en la clave principal?

Sí.

Tal vez tenga sentido poner en la clave principal un campo por el cual los datos se comprimirán mejor si se ordenan por este campo. Por ejemplo, ID de usuario. El usuario, por ejemplo, va al mismo sitio. En este caso, ponga el ID de usuario y la hora. Y entonces sus datos estarán mejor comprimidos. En cuanto a la fecha, si realmente no tiene y nunca tiene consultas de rango en fechas, entonces no puede poner la fecha en la clave principal.

¡Vale, muchas gracias!

Fuente: habr.com

Añadir un comentario