En abril, los ingenieros de Avito se reunieron en una sesión en línea con el principal desarrollador de ClickHouse, Alexey Milovidov, y Kirill Shvakov, desarrollador de Golang de la empresa Integros. Discutieron cómo utilizamos el sistema de gestión de bases de datos y qué dificultades encontramos.
Con base en la reunión, hemos recopilado un artículo con las respuestas de los expertos a nuestras preguntas y las de la audiencia sobre copias de seguridad, re-sharding de datos, diccionarios externos, el controlador de Golang y la actualización de versiones de ClickHouse. Puede ser útil para los desarrolladores que ya están trabajando activamente con la base de datos de 'Yandex' y que están interesados en su presente y futuro. Por defecto, las respuestas son de Alexey Milovidov, a menos que se indique lo contrario.
Cuidado, hay mucho texto bajo el corte. Esperamos que el contenido con las preguntas te ayude a orientarte.

Contenido
Si no quieres leer el texto, puedes ver la grabación de las reuniones. . Las marcas de tiempo están en el primer comentario debajo del video.
ClickHouse se actualiza constantemente, pero nuestros datos no. ¿Qué hacer al respecto?
ClickHouse se actualiza constantemente, pero nuestros datos, que fueron procesados con optimize final, no se actualizan y permanecen en la copia de seguridad.
Supongamos que tuvimos algún problema y los datos se perdieron. Decidimos restaurarnos y resultó que las antiguas particiones que se guardan en los servidores de backups son muy diferentes a la versión actual de ClickHouse. ¿Qué hacer en esta situación, y es posible?
La situación en la que restauraste datos desde un backup en un formato antiguo y en la nueva versión no se conectan no es posible. Nos aseguramos de que el formato de los datos en ClickHouse sea siempre compatible hacia atrás. Esto es mucho más importante que la compatibilidad hacia atrás en funcionalidad, si el comportamiento de alguna función poco utilizada ha cambiado. Los datos almacenados en disco siempre deben ser leíbles por la nueva versión de ClickHouse. Esa es la regla.
¿Cuáles son las mejores prácticas en este momento para la copia de seguridad de datos de ClickHouse?
¿Cómo hacer copias de seguridad teniendo en cuenta que tenemos operaciones optimize final, una base de datos enorme en terabytes, y datos que se actualizan, supongamos, en los últimos tres días, y después no hay más procedimientos?
Podemos crear nuestra propia solución y escribir en Bash: recopila estas copias de seguridad de esta manera y esta otra. Quizás no haya necesidad de inventar nada y el 'bicicleta' ya ha sido creado hace tiempo.
Para empezar, respecto a las mejores prácticas. Mis colegas siempre aconsejan, en respuesta a preguntas sobre copias de seguridad, recordar el servicio "Yandex.Cloud", donde esta tarea ya está resuelta. Así que úsalo si tienes la oportunidad.
No existe una solución completa, integrada al cien por cien en ClickHouse, para copias de seguridad. Hay algunas plantillas que se pueden utilizar. Para obtener una solución completa, tendrás que trabajar un poco manualmente o crear envolturas en forma de scripts.
Comenzaré con las soluciones más simples y terminaré con las más avanzadas dependiendo del volumen de datos y el tamaño del clúster. Cuanto más grande sea el clúster, más complicado se vuelve la solución.
Si la tabla de datos ocupa solo unos pocos gigabytes, puedes hacer una copia de seguridad de la siguiente manera:
- Guardar la definición de las tablas, es decir, los metadatos — show create table.
- Hacer un volcado utilizando el cliente de ClickHouse — select * from table en un archivo. Por defecto, obtendrás un archivo en formato TabSeparated. Si deseas algo más eficiente, puedes usar el formato Native.
Si el volumen de datos es mayor, la copia de seguridad tomará más tiempo y ocupará mucho espacio. Esto se llama copia de seguridad lógica, no está vinculada al formato de datos de ClickHouse. Si existe, en el peor de los casos, podrás tomar la copia de seguridad y cargarla en MySQL para su recuperación.
Para casos más avanzados, ClickHouse tiene la función incorporada para crear instantáneas de particiones en el sistema de archivos local. Esta función está disponible en forma de consulta alter table freeze partition. O simplemente alter table freeze — esto es una instantánea de toda la tabla.
La instantánea se creará de manera consistente para una tabla en una shard, es decir, no es posible crear una instantánea consistente de todo el clúster de esta manera. Pero para la mayoría de las tareas, no hay necesidad de ello, y es suficiente ejecutar la consulta en cada shard y obtener una instantánea consistente. Se crea en forma de hardlinks y, por lo tanto, no ocupa espacio adicional. Luego, copias esta instantánea al servidor de respaldo o al almacenamiento que utilizas para las copias de seguridad.
Restaurar esta copia de seguridad es bastante fácil. Primero, creas las tablas según las definiciones existentes de las tablas. Luego, copias las instantáneas de particiones guardadas en Directory-Detached para esas tablas y ejecutas la consulta attach partition. Esta solución es totalmente adecuada para volúmenes de datos muy serios.
A veces se requiere algo aún más impresionante: en aquellos casos en los que tienes decenas o incluso cientos de terabytes en cada servidor y cientos de servidores. Aquí hay una solución que observé de colegas de «Yandex.Metrica». No lo recomendaría para todos; léelo y decide tú mismo si es adecuado o no.
Primero, necesitas crear varios servidores con grandes estantes de discos. Luego, en estos servidores, levantar varios servidores de ClickHouse y configurarlos para que funcionen como una réplica adicional para los mismos shards. Y después usar un sistema de archivos en estos servidores o alguna herramienta que permita crear instantáneas. Hay dos opciones. La primera opción son las instantáneas de LVM, la segunda opción es ZFS en Linux.
Después de esto, es necesario crear una instantánea diariamente, que ocupará un espacio determinado. Naturalmente, si los datos cambian, con el tiempo el volumen de espacio aumentará. Esta instantánea se puede acceder en cualquier momento y restaurar los datos, es una solución bastante extraña. Además, es necesario limitar estas réplicas en la configuración para que no intenten convertirse en líderes.
¿Se podrá organizar un retraso controlado en las réplicas en los clústeres?
Este año planeas hacer flujos en ClickHouse. ¿Se podrá organizar un retraso controlado de las réplicas en ellos? Nos gustaría utilizarlo para protegernos de escenarios negativos con alternativos y otros cambios.
¿Se pueden hacer algunos rollbacks para alternativas? Por ejemplo, en el flujo existente, ¿se puede indicar que hasta este momento apliques cambios, y a partir de este momento dejes de aplicar cambios?
Si llega un comando y rompe nuestro clúster, entonces tenemos una réplica condicional con un retraso de una hora, donde podemos decir que vamos a usarla en este momento, pero no aplicaremos los últimos diez minutos de cambios en ella.
Primero sobre el retraso controlado de las réplicas. Hubo una solicitud de los usuarios, y creamos un issue en GitHub pidiendo: «Si a alguien le interesa, dale like, dale un corazón». Nadie dio like, así que el issue se cerró. Sin embargo, ya es posible obtener esta funcionalidad configurando ClickHouse, aunque solo a partir de la versión 20.3.
ClickHouse realiza continuamente un proceso de fusión de datos en segundo plano. Cuando se produce la fusión, un conjunto de partes de datos se reemplaza por una parte más grande. Sin embargo, las partes de datos que existían anteriormente permanecen en el disco durante un tiempo.
En primer lugar, se mantienen hasta que hay solicitudes de selección que las utilizan, para asegurar un funcionamiento sin bloqueos. Las solicitudes de selección pueden leer tranquilamente de las partes antiguas.
En segundo lugar, hay un umbral de tiempo: las partes antiguas de datos permanecen en el disco durante ocho minutos. Este tiempo se puede ajustar y extender incluso a un día. Esto ocupará espacio en disco: dependiendo del flujo de datos, puede resultar que en el último día los datos no solo se dupliquen, sino que pueden llegar a ser cinco veces más. Sin embargo, si hay un problema serio, podrás detener el servidor ClickHouse y resolver todo.
Ahora surge la pregunta de cómo esto protege contra los alters. Aquí conviene mirar más de cerca, porque en versiones antiguas de ClickHouse, el alter funcionaba cambiando directamente las partes. Hay una parte de datos con ciertos archivos, y hacemos, por ejemplo, alter drop column. Entonces, esta columna se elimina físicamente de todas las partes.
Pero a partir de la versión 20.3, el mecanismo de alters fue completamente modificado, y ahora las partes de datos son siempre inmutables. No cambian en absoluto: los alters ahora funcionan de manera similar a las fusiones. En lugar de cambiar la parte en su lugar, creamos una nueva. En la nueva parte, los archivos que no han cambiado se convierten en hardlinks, y si eliminamos una columna, simplemente no estará presente en la nueva parte. La parte antigua se eliminará por defecto después de ocho minutos, y se pueden ajustar las configuraciones mencionadas anteriormente.
Lo mismo ocurre con los alters de tipo mutaciones. Cuando haces alter delete o alter update, no modifica la parte, sino que crea una nueva. Y luego elimina la antigua.
¿Qué hacer si la estructura de la tabla ha cambiado?
¿Cómo restaurar una copia de seguridad que se realizó con un esquema antiguo? Y la segunda pregunta sobre el caso de los snapshots y herramientas del sistema de archivos. ¿Sirve aquí Btrfs en lugar de ZFS en Linux LVM?
Si haces attach partition Si las particiones tienen una estructura diferente, ClickHouse le dirá que no se puede hacer. La solución es la siguiente. Primero, cree una tabla temporal tipo MergeTree con la estructura antigua, adjunte los datos con attach, y haga una solicitud alter. Luego, puede copiar o mover estos datos y hacer attach nuevamente, o utilizar una consulta. alter table move partition.
Ahora, la segunda pregunta es si se puede usar Btrfs. Primero, si tiene LVM, entonces los snapshots de LVM son suficientes, y el sistema de archivos puede ser ext4, no importa. Con Btrfs, todo depende de su experiencia en su uso. Es un sistema de archivos maduro, pero todavía hay algunas dudas sobre cómo funcionará en la práctica en un escenario específico. No lo recomendaría si no tiene Btrfs en producción.
¿Cuáles son las mejores prácticas actuales en el re-sharding de datos?
La cuestión del re-sharding es compleja y multifacética. Se puede responder de varias maneras. Se puede abordar desde un lado y decir que ClickHouse no tiene una opción incorporada para re-sharding. Pero temo que esta respuesta no satisfará a nadie. Por lo tanto, se puede abordar desde otro lado y decir que ClickHouse tiene muchas formas de re-shardear datos.
Si se acaba el espacio en el clúster o no puede manejar la carga, agrega nuevos servidores. Pero estos servidores están vacíos por defecto, no tienen datos y no se están cargando. Debe redistribuir los datos para que estén equilibrados en el nuevo clúster ampliado.
La primera forma de hacerlo es copiar algunas particiones a los nuevos servidores mediante la consulta. alter table fetch partition. Por ejemplo, si tenía particiones por meses, puede tomar el primer mes de 2017 y copiarlo a un nuevo servidor, luego copiar el tercer mes a otro nuevo servidor. Y así lo hará hasta que esté más o menos equilibrado.
La transferencia solo se puede realizar para aquellas particiones que no cambian al momento de escribir. Para las particiones recientes, deberá desactivar la escritura porque su transferencia no es atómica. De lo contrario, tendrá duplicados o faltantes en los datos. Sin embargo, este método es práctico y funciona de manera bastante efectiva. Se transfieren particiones comprimidas ya listas a través de la red, es decir, los datos no se descomprimen ni se vuelven a codificar.
Este método tiene una desventaja, que depende de la configuración de sharding, si estabas preparado para este esquema de sharding y cuál era tu clave de sharding. En tu ejemplo para el caso de métricas, la clave de sharding es un hash de la ruta. Cuando haces un select en una tabla distribuida, va directamente a todos los shards del clúster y recupera datos de allí.
Esto significa que, en realidad, no importa qué datos se encuentran en qué shard. Lo principal es que los datos por una misma ruta se encuentran en un mismo shard, y cuál sea exactamente no es decisivo. En este caso, la transferencia de particiones ya listas es muy adecuada, porque en las consultas select, ya sea antes o después del re-sharding, la configuración de los valores no importa, recibirás todos los datos.
Pero hay casos más complejos. Si a nivel de lógica de aplicación te basas en un esquema especial de sharding, donde este cliente está en un cierto shard y la consulta se puede enviar directamente a allí, en lugar de a la tabla distribuida. O si estás utilizando una versión bastante reciente de ClickHouse y has habilitado la configuración optimize skip unused shards. En este caso, durante la consulta select, la expresión en la sección where se analizará y se calculará a qué shards es necesario ir según el esquema de sharding. Esto funciona con la condición de que los datos estén distribuidos de acuerdo con este esquema de sharding. Si los has reorganizado manualmente, la correspondencia puede cambiar.
Así que este es el método número uno. Espero tu respuesta, si este método es adecuado o si seguimos adelante.
Vladimir Kolobaev, administrador de sistemas líder en Avito: Alexey, el método que mencionaste no se aplica muy bien cuando es necesario distribuir la carga, incluso la de lectura. Podemos tomar una partición mensual y mover el mes anterior a otro nodo, pero cuando llegue la solicitud por esos datos, solo estaremos cargando ese nodo. Y nos gustaría que toda la carga del clúster se repartiera, porque, de lo contrario, durante un tiempo toda la carga de lectura se procesará en solo dos shards.
Alexey Milovidov: La respuesta aquí es extraña: sí, es mala, pero puede funcionar. Te explicaré cómo. Vale la pena observar el escenario de carga que acompaña a tus datos. Si se trata de datos de monitoreo, se puede afirmar casi con certeza que la gran mayoría de las solicitudes se dirigen hacia los datos más recientes.
Has instalado nuevos servidores, trasladado particiones antiguas, pero también cambiaste cómo se registran los datos recientes. Y esos datos recientes estarán distribuidos por todo el clúster. Así, después de cinco minutos, las solicitudes de los últimos cinco minutos cargarán uniformemente el clúster, y al cabo de un día, las solicitudes del último día también lo harán. Sin embargo, las solicitudes del mes anterior, lamentablemente, solo se dirigirán a parte de los servidores del clúster.
Pero a menudo no tendrás solicitudes específicamente de febrero de 2019. Es más probable que, si hay solicitudes del año 2019, sean para todo ese año, durante un gran intervalo de tiempo y no para un pequeño rango. Y tales solicitudes también pueden cargar uniformemente el clúster. Pero en general, tu observación es completamente válida: es una solución ad hoc que no distribuye los datos de manera completamente uniforme.
Tengo unos cuantos puntos más para responder a la pregunta. Uno de ellos es sobre cómo diseñar inicialmente un esquema de particionado para que sea menos doloroso al re-particionar. Esto no siempre es posible.
Por ejemplo, tienes datos de monitoreo. Los datos de monitoreo crecen por tres razones. La primera es la acumulación de datos históricos. La segunda es el aumento del tráfico. Y la tercera es el incremento en la cantidad de cosas que están bajo monitoreo. Aparecen nuevos microservicios y métricas que necesitan ser almacenadas.
Es posible que el mayor crecimiento esté relacionado precisamente con la tercera razón: el aumento del uso del monitoreo. En este caso, vale la pena observar la naturaleza de la carga, cuáles son las principales solicitudes de select. Es probable que las principales solicitudes de select se dirijan a un subconjunto específico de métricas.
Por ejemplo, el uso de CPU en algunos servidores por algunos servicios. Resulta que hay un subconjunto de claves según el cual se obtienen estos datos. Y la consulta para obtener estos datos, probablemente, es bastante simple y se ejecuta en decenas de milisegundos. Se utiliza para servicios de monitoreo, para dashboards. Espero que lo entienda correctamente.
Vladimir Kolobaev: El problema es que a menudo apelamos a datos históricos, ya que comparamos en tiempo real la situación actual con la histórica. Y para nosotros es importante tener acceso rápido a un gran volumen de datos, y ClickHouse maneja esto muy bien.
Tiene toda la razón, la mayoría de las consultas de lectura las experimentamos en el último día, como cualquier sistema de monitoreo. Pero, aun así, la carga sobre los datos históricos también es bastante grande. Principalmente proviene del sistema de alertas, que cada treinta segundos consulta a ClickHouse: "Dame los datos de las últimas seis semanas. Y ahora, construya una media móvil con ellos y comparémosla con el valor actual frente a lo histórico."
Me gustaría decir que tenemos una pequeña tabla adicional para tales consultas muy recientes, en la que almacenamos solo datos de dos días, y las consultas principales van allí. A la gran tabla particionada solo enviamos grandes consultas históricas.
Alexey Milovidov: Desafortunadamente, para su escenario no resulta muy aplicable, pero describiré dos esquemas de partición difíciles y malos que no deben usarse, pero que son utilizados por un servicio de mis amigos.
Hay un clúster principal con eventos de "Yandex.Metrica". Los eventos son vistas de páginas, clics y transiciones. La mayoría de las consultas se dirigen a un sitio web específico. Ustedes abren el servicio "Yandex.Metrica", tienen un sitio — avito.ru, entran en el informe y se genera una consulta para su sitio.
Pero también hay otras consultas, analíticas y globales, que realizan analistas internos. Para que conste, los analistas internos solo hacen consultas sobre servicios de "Yandex". Sin embargo, incluso los servicios de "Yandex" ocupan una parte sustancial de todos los datos. Estas son consultas no sobre contadores específicos, sino sobre una filtración más amplia.
¿Cómo organizar los datos de manera que funcione de forma eficiente tanto con un solo contador como para las solicitudes globales? La complejidad radica en que la cantidad de solicitudes en ClickHouse para el clúster 'Métricas' es de varios miles por segundo. Además, las solicitudes no triviales, por ejemplo, varios miles por segundo, un único servidor de ClickHouse no puede manejarlas.
El tamaño del clúster es de seiscientos y pico servidores. Si simplemente se coloca una tabla Distributed sobre este clúster y se envían miles de solicitudes, sería aún peor que enviarlas a un solo servidor. Por otro lado, desechamos inmediatamente la opción de que los datos estén distribuidos uniformemente y que vayamos a consultar a todos los servidores.
Hay una opción diametralmente opuesta. Imagina que vamos a particionar los datos por sitios web, y la consulta para un solo sitio vaya a una única partición. Ahora el clúster podrá manejar diez mil solicitudes por segundo, pero en una partición, alguna solicitud funcionará demasiado lento. Ya no podrá escalar en capacidad. Especialmente si es el sitio avito.ru. No sería un secreto decir que Avito es uno de los sitios más visitados en la Runet. Procesarlo en una sola partición sería una locura.
Por eso, el esquema de particionamiento está diseñado de manera más astuta. Todo el clúster se divide en un cierto número de subclústeres, que llamamos capas. Dentro de cada subclúster hay de diez a varias decenas de particiones. Y en total hay treinta y nueve de esos subclústeres.
¿Cómo se escala todo esto? La cantidad de subclústeres no cambia: como hace unos años había treinta y nueve, así sigue. Pero dentro de cada uno de ellos, gradualmente aumentamos la cantidad de particiones a medida que se acumulan los datos. Y el esquema de particionamiento en general es el siguiente: la división en estos subclústeres se realiza por sitios web, y para entender qué sitio está en qué subclúster, se utiliza una base de datos separada en MySQL. Un sitio en un subclúster. Y dentro de él, el particionamiento se realiza por identificadores de visitantes.
Al registrar, los dividimos según el residuo de la división del identificador del visitante. Pero al agregar una nueva partición, el esquema de particionamiento cambia, continuamos dividiendo, pero según el residuo de la división por otro número. Esto significa que un visitante ya se encuentra en varios servidores, y no se puede contar con eso. Esto se hace exclusivamente para que los datos se compriman mejor. Y al realizar las consultas, acudimos a la tabla Distributed, que examina el clúster y se comunica con decenas de servidores. Así es como funciona este esquema raro.
Pero mi relato sería incompleto si no mencionara que hemos abandonado este esquema. En el nuevo esquema hemos cambiado todo y copiado todos los datos usando clickhouse-copier.
En el nuevo esquema, todos los sitios se dividen en dos categorías: grandes y pequeños. No sé cómo se ha establecido el umbral, pero el resultado es que los sitios grandes se registran en un solo clúster, donde hay 120 particiones con tres réplicas cada una, es decir, 360 servidores. Y el esquema de particionamiento es tal que cualquier consulta va directamente a todas las particiones. Si ahora abres cualquier página de informes de avito.ru en ‘Yandex.Metrica’, la consulta se dirigirá a 120 servidores. Hay pocos sitios grandes en Runet. Y las consultas no son mil por segundo, sino que incluso son menos de cien. Todo esto es manejado sin problemas por la tabla Distributed, que cada uno de ellos procesa con 120 servidores.
Y el segundo clúster es para los sitios pequeños. Aquí el esquema de particionamiento se basa en el identificador del sitio, y cada consulta va a exactamente una partición.
ClickHouse tiene una utilidad llamada clickhouse-copier. ¿Puedes hablarnos de ella?
De inmediato digo que esta solución es más pesada y algo menos eficiente. La ventaja es que dispersa los datos completamente según el esquema que indiques. Pero la desventaja de la utilidad es que no realiza la re-partición. Copia los datos de un esquema de clúster a otro esquema de clúster.
Esto significa que para su funcionamiento debes tener dos clústeres. Pueden estar ubicados en los mismos servidores, pero, sin embargo, los datos no se moverán de manera incremental, sino que serán copiados.
Por ejemplo, si había cuatro servidores, ahora hay ocho. Creas una nueva tabla distribuida en todos los servidores, nuevas tablas locales y ejecutas clickhouse-copier, especificando en él el esquema de funcionamiento, que debe leer de allí, aceptar el nuevo esquema de particionamiento y mover los datos allí. Y necesitarás un espacio en los servidores antiguos que sea una vez y media mayor que el que tienes actualmente, porque los datos antiguos deben permanecer allí, y además recibir medio de esos mismos datos antiguos. Si pensaste de antemano que los datos necesitaban ser re-particionados y hay espacio, entonces este método es adecuado.
¿Cómo está estructurado internamente clickhouse-copier? Divide todo el trabajo en un conjunto de tareas para procesar una partición de una tabla en un shard. Todas estas tareas pueden ejecutarse en paralelo, y clickhouse-copier puede ser ejecutado en diferentes máquinas en varias instancias, pero lo que hace para una partición es, nada más ni nada menos, que un insert select. Los datos son leídos, descomprimidos, reorganizados, luego comprimidos nuevamente, escritos en algún lugar y reordenados. Esta es una solución más pesada.
Tuviste una característica piloto llamada re-sharding. ¿Qué ha pasado con eso?
En 2017 tenías un piloto llamado resharding. Incluso hay una opción en ClickHouse. Entiendo que no tuvo éxito. ¿Puedes contarme por qué ocurrió esto? Parece bastante relevante.
Todo el problema es que, cuando es necesario re-particionar los datos en el lugar, se requiere una sincronización bastante compleja para hacerlo de forma atómica. Cuando empezamos a analizar cómo está organizada esta sincronización, quedó claro que había problemas fundamentales. Y estos problemas fundamentales no solo son teóricos, sino que también se manifestaron de inmediato en la práctica en forma de algo que se puede explicar muy simplemente: nada funciona.
¿Se pueden fusionar todas las partes de datos en uno solo antes de moverlo a discos lentos?
Pregunta sobre TTL con la opción de mover a disco lento en el contexto de las fusiones. ¿Hay alguna forma, además de mediante cron, de fusionar todas las partes en una antes de trasladarlas a discos lentos?
La respuesta a la pregunta de si se puede fusionar automáticamente todas las partes en una antes de su traslado es no. No creo que sea necesario. No hace falta fusionar todas las partes en una, simplemente se puede contar con que serán trasladadas a los discos lentos automáticamente.
Tenemos dos criterios para la migración. El primero es en función de la ocupación. Si en el nivel de almacenamiento actual hay menos de un cierto porcentaje de espacio libre, seleccionamos un fragmento y lo trasladamos a un almacenamiento más lento. Más bien, no más lento, sino el siguiente, según como esté configurado.
El segundo criterio es por tamaño. Este trata sobre la migración de grandes fragmentos. Puedes ajustar el umbral de espacio libre en el disco rápido, y los datos se trasladarán automáticamente.
¿Cómo migrar a nuevas versiones de ClickHouse si no hay posibilidad de verificar la compatibilidad por adelantado?
Este tema se discute regularmente teniendo en cuenta las diferentes versiones, y aún así. ¿Qué tan seguro es actualizar de la versión 19.11 a la 19.16 y, por ejemplo, de 19.16 a 20.3? ¿Cómo es mejor migrar a nuevas versiones sin poder verificar la compatibilidad en un entorno de pruebas?
Aquí hay algunas reglas 'doradas'. La primera es . Es extenso, pero hay puntos específicos sobre cambios incompatibles. No consideres estos puntos como una señal de alarma. Generalmente son incompatibilidades menores, relacionadas con alguna funcionalidad marginal que probablemente no estés utilizando.
La segunda es que si no hay posibilidad de verificar la compatibilidad en un entorno de pruebas, y deseas actualizar directamente en producción, la recomendación es: no lo hagas. Primero crea un entorno de pruebas y verifica. Si no hay entorno de prueba, probablemente no seas una empresa muy grande, lo que significa que puedes copiar parte de los datos en tu laptop y verificar que todo funciona correctamente. Incluso se puede levantar varias réplicas localmente en tu máquina. O puedes levantar una nueva versión en algún lugar cercano e importar parte de los datos, es decir, crear un entorno de prueba improvisado.
Otra regla es no actualizar durante una semana después del lanzamiento de una versión debido a la detección de errores en producción y las subsiguientes correcciones rápidas. Vamos a desglosar la numeración de versiones de ClickHouse para no perdernos.
Hay una versión 20.3.4. El número 20 indica el año de lanzamiento: 2020. Desde el punto de vista de lo que hay dentro, esto no tiene ningún significado, así que no nos detendremos en ello. A continuación, tenemos 20.3. Aumentamos el segundo dígito, en este caso el 3, cada vez que lanzamos una versión con alguna nueva funcionalidad. Si queremos agregar alguna capacidad a ClickHouse, estamos obligados a incrementar este número. Es decir, en la versión 20.4, ClickHouse funcionará aún mejor. El tercer dígito, 20.3.4. Aquí el 4 representa la cantidad de lanzamientos de parches, en los cuales no hemos añadido nuevas capacidades, pero hemos corregido algunos errores. Y el 4 significa que hemos hecho esto cuatro veces.
No deberías pensar que esto es algo terrible. Normalmente, el usuario puede instalar la versión más reciente y funcionará sin ningún problema durante un año. Pero imagina que en alguna función para el procesamiento de mapas de bits, que fue añadida por nuestros compañeros chinos, el servidor se cae si se pasan argumentos incorrectos. Debemos solucionarlo. Lanzaremos una nueva versión de parche y ClickHouse será más estable.
Si tienes ClickHouse funcionando en producción y se lanza una nueva versión de ClickHouse con características adicionales, por ejemplo, 20.4.1, no te apresures a instalarla en producción el primer día. ¿Para qué sirve? Si aún no usas ClickHouse, puedes instalarlo y probablemente funcionará bien. Pero si ClickHouse ya está funcionando de manera estable, presta atención a los parches y actualizaciones: qué problemas estamos corrigiendo.
Kirill Shvakov: Quiero añadir un poco sobre los entornos de prueba. Todos tienen mucho miedo de los entornos de prueba y, por alguna razón, creen que si tienes un clúster ClickHouse muy grande, el entorno de prueba debe ser igual de grande o al menos diez veces más pequeño. Esto no es cierto en absoluto.
Puedo hablar por experiencia propia. Tengo un proyecto y allí está ClickHouse. Nuestro entorno de prueba para él es una pequeña máquina virtual en Hetzner por veinte euros, donde todo está desplegado. Para hacer esto, tenemos una automatización completa en Ansible, por lo que, en principio, no hay diferencia en desplegar en servidores físicos o simplemente en máquinas virtuales.
¿Qué se puede hacer? Sería útil incluir en la documentación de ClickHouse un ejemplo de cómo desplegar un pequeño clúster en Docker, en LXC, o quizás crear un playbook de Ansible, ya que diferentes personas tienen diferentes implementaciones. Esto simplificaría mucho las cosas. Cuando puedes desplegar un clúster en cinco minutos, es mucho más fácil entender cómo funcionan las cosas. Así es mucho más cómodo, porque ir a producción con una versión que no has probado es un camino sin salida. A veces funciona, a veces no. Por eso, depender del éxito no es una buena idea.
Maxim Kotiakov, ingeniero backend senior en Avito: Déjame añadir algo sobre los entornos de prueba que enfrentan las grandes empresas. Tenemos un clúster de aceptación completo de ClickHouse, que es una copia exacta de los esquemas de datos y configuraciones que tenemos en producción. Este clúster está desplegado en contenedores bastante antiguos con un mínimo de recursos. Escribimos un cierto porcentaje de los datos de producción allí, afortunadamente tenemos la capacidad de replicar el flujo en Kafka. Todo está sincronizado y escalado — tanto en capacidad como en flujo, y, en teoría, bajo condiciones similares debería comportarse como producción. Todo lo potencialmente explosivo se prueba primero en este entorno durante varios días hasta que esté listo. Pero, por supuesto, esta solución es costosa, pesada y con gastos no despreciables en manutención.
Alexey Milovidov: Voy a hablar sobre el entorno de prueba de nuestros amigos de 'Yandex.Metrica'. Un clúster tenía más de 600 servidores, otro alrededor de 360, y también hay un tercero y varios clústeres. El entorno de prueba para uno de ellos se compone simplemente de dos shards con dos réplicas en cada uno. ¿Por qué dos shards? Para que no haya solo uno. Y también para tener réplicas. Solo una cantidad mínima que se puede permitir.
Este entorno de prueba permite comprobar la funcionalidad de las consultas y si no ha habido fallos graves. Pero a menudo surgen problemas de otro tipo, cuando todo funciona, pero hay algunos cambios pequeños en la carga.
Voy a dar un ejemplo. Decidimos instalar una nueva versión de ClickHouse. Esta se publicó en el entorno de prueba, se realizaron pruebas automatizadas en 'Yandex.Metrica', que comparan los datos de la versión antigua con los de la nueva, ejecutando toda la cadena de procesamiento. Y, por supuesto, las pruebas de CI son positivas. De lo contrario, ni siquiera habríamos propuesto esta versión.
Todo va bien. Comenzamos a poner en producción. Me llega un mensaje de que en los gráficos la carga ha aumentado varias veces. Revertimos la versión. Miro el gráfico y veo: la carga realmente aumentó varias veces durante el despliegue y volvió a disminuir cuando se completó. Luego comenzamos a revertir la versión. Y la carga también aumentó de la misma manera y volvió a caer. Así que la conclusión es que la carga aumentó debido al despliegue, nada sorprendente.
Después fue difícil convencer a los colegas de que instalaran la nueva versión. Les digo: ‘Todo está bien, desplieguen. Mantengan los dedos cruzados, todo funcionará. Ahora la carga ha aumentado en los gráficos, pero todo está bien. Sigan adelante’. En general, lo hicimos y todos - la versión fue desplegada en producción. Pero casi en cada despliegue surgen problemas similares.
El kill query debería matar consultas, pero no lo hace. ¿Por qué?
Un usuario, un analista, vino a mí y creó una solicitud que colapsó mi clúster de ClickHouse. Alguna nodo o el clúster completo - dependiendo de a qué réplica o fragmento llegó la solicitud. Veo que todos los recursos de CPU en ese servidor están al máximo, todo en rojo. Sin embargo, ClickHouse responde a las solicitudes. Y escribo: ‘Muéstrame, por favor, la lista de procesos, ¿qué solicitud causó esta locura?’
Encuentro esa solicitud y le doy kill. Y veo que no pasa nada. Mi servidor está al máximo, ClickHouse sigue dándome algunos comandos, mostrando que el servidor está vivo y todo está genial. Pero tengo una degradación en todas las solicitudes de los usuarios, comenzó la degradación en la escritura en ClickHouse y mi kill query no se ejecuta. ¿Por qué? Pensé que kill query debería eliminar las solicitudes, pero eso no ocurre.
Ahora va a haber una respuesta bastante extraña. La cuestión es que kill query no elimina las solicitudes.
Kill query establece una pequeña bandera llamada ‘quiero que esta solicitud sea eliminada’. Y la solicitud, al procesar cada bloque, verifica esa bandera. Si está activada, la solicitud deja de funcionar. Así que nadie mata la solicitud, ella misma tiene que verificar y detenerse. Y esto debería funcionar en todos los casos cuando la solicitud se encuentra en estado de procesamiento de bloques de datos. Procesará el siguiente bloque de datos, comprobará la bandera y se detendrá.
Esto no funciona en casos donde la consulta está bloqueada en alguna operación. Sin embargo, es probable que este no sea su caso, porque según sus palabras, está utilizando un montón de recursos del servidor. Puede que esto no funcione en el caso de una ordenación externa y en algunos otros detalles. Pero en general, esto no debería suceder, es un error. Y lo único que puedo aconsejar es actualizar ClickHouse.
¿Cómo calcular el tiempo de respuesta bajo carga de lectura?
Hay una tabla que almacena agregados por item: diversos contadores. La cantidad de filas es de aproximadamente cien millones. ¿Se puede esperar un tiempo de respuesta predecible si se envían 1K RPS por 1K items?
A juzgar por el contexto, se trata de carga de lectura, ya que no hay problemas al escribir: se pueden insertar mil, cien mil o incluso varios millones de filas.
Las consultas de lectura pueden ser muy diversas. En select 1, ClickHouse puede ejecutar alrededor de decenas de miles de consultas por segundo, por lo que incluso las consultas por una sola clave requerirán algunos recursos. Y esas consultas puntuales serán más complicadas que en algunas bases de datos key-value, porque para cada lectura es necesario leer un bloque de datos por índice. El índice no direcciona cada registro, sino cada rango. Es decir, habrá que leer todo el rango: por defecto son 8192 filas. Y se tendrá que descomprimir el bloque de datos comprimidos de 64 Kb a 1 Mb. Normalmente, esas consultas puntuales tardan desde unos pocos milisegundos. Pero esta es la opción más sencilla.
Intentemos hacer una simple aritmética. Si multiplicas unos pocos milisegundos por mil, obtienes unos pocos segundos. Como si no se pudieran sostener mil solicitudes por segundo, pero en realidad se puede, porque tenemos varios núcleos de procesador. Así que en principio, ClickHouse puede soportar 1000 RPS a veces, pero en consultas cortas, específicamente puntuales.
Si necesitas escalar un clúster de ClickHouse por la cantidad de consultas simples, recomiendo lo más sencillo: aumentar el número de réplicas y enviar las consultas a una réplica aleatoria. Si una réplica puede manejar quinientas consultas por segundo, lo que es completamente realista, entonces tres réplicas podrán manejar mil quinientas.
A veces, por supuesto, se puede configurar ClickHouse para un número máximo de lecturas puntuales. ¿Qué se necesita para ello? Primero, reducir la granularidad del índice. Al hacerlo, no debe reducirse a uno, sino considerando que la cantidad de registros en el índice será de varios millones o decenas de millones en el servidor. Si en la tabla hay cien millones de filas, se puede establecer una granularidad de 64.
Se puede reducir el tamaño del bloque comprimido. Para ello, hay configuraciones min compress block size, max compress block size. Pueden reducirse, reconfigurar los datos y entonces las consultas puntuales serán más rápidas. Pero aún así, ClickHouse no es una base de datos tipo key-value. Un gran número de consultas pequeñas es un antipatrón de carga.
Kirill Shvakov: Te daré un consejo en caso de que allí haya contadores comunes. Esta es una situación bastante estándar, cuando en ClickHouse se almacena algún contador. Tengo un usuario, es de tal país, hay algún tercer campo, y se necesita incrementar algo. Toma MySQL, crea una clave única; en MySQL es una clave duplicada y en PostgreSQL es un conflicto, y lo agregas con un signo más. Esto funcionará mucho mejor.
Cuando tienes pocos datos, no tiene sentido usar ClickHouse en particular. Hay bases de datos comunes que manejan esto bien.
¿Qué afinar en ClickHouse para que haya más datos en caché?
Imaginemos una situación: en los servidores hay 256 GB de RAM, en la rutina diaria ClickHouse utiliza aproximadamente de 60 a 80 GB, en el pico hasta 130. ¿Qué se puede activar y ajustar para que haya más datos en caché y, por lo tanto, menos accesos al disco?
Por lo general, la caché de páginas del sistema operativo maneja bien esta tarea. Si abres simplemente top, miras allí cached o free—también indica cuánto está en caché—puedes notar que toda la memoria libre se utiliza para caché. Y esos datos, al leerse, no se leerán desde el disco, sino desde la RAM. Además, puedo decir que la caché se utiliza de manera efectiva, porque se almacenan precisamente los datos comprimidos.
Sin embargo, si deseas acelerar aún más algunas consultas simples, existe la posibilidad de habilitar dentro de ClickHouse la caché en datos descomprimidos. Esto se llama uncompressed cache. En el archivo de configuración config.xml, ajusta el tamaño del caché descomprimido al valor que necesites; recomiendo no más de la mitad de la RAM libre, ya que el resto se utilizará para el caché de página.
Además, hay dos configuraciones a nivel de consulta. La primera configuración es usar caché descomprimido — activa su uso. Se recomienda activarlo para todas las consultas, excepto para aquellas pesadas que pueden leer todos los datos y limpiar este caché. Y la segunda configuración es algo similar a la cantidad máxima de filas que se utilizarán para el caché. Limita automáticamente las consultas grandes para que pasen por alto el caché.
¿Cómo se puede configurar storage_configuration para almacenamiento en memoria?
En la nueva documentación de ClickHouse, leí una sección relacionada . En la descripción hay un ejemplo con SSD rápido.
Es interesante ver cómo se puede configurar lo mismo con memoria caliente. Y otra pregunta: ¿cómo funciona el select con esta organización de datos? ¿Leerá todo el conjunto o solo lo que está en disco, y se comprimen esos datos en memoria? Y ¿cómo funciona la sección prewhere en esta organización de datos?
Esta configuración afecta el almacenamiento de los fragmentos de datos, y su formato no cambia.
Veamos esto con más detalle.
Se puede configurar el almacenamiento de datos en memoria. Todo lo que se configura para el disco es su ruta. Creas una partición tmpfs que está montada en algún camino en el sistema de archivos. Indicas esta ruta como el camino para almacenar datos para la partición más caliente, donde comienzan a llegar y a registrarse fragmentos de datos, todo bien.
Pero no lo recomiendo debido a la baja fiabilidad, aunque si tienes al menos tres réplicas en diferentes centros de datos, podría hacerse. Si algo, los datos serán recuperados. Imagina que el servidor se apaga y se vuelve a encender. La partición se ha montado de nuevo, pero está vacía. El servidor ClickHouse al iniciar ve que esos fragmentos faltan, aunque según los metadatos de ZooKeeper deberían estar allí. Mira en qué réplicas están, las solicita y las descarga. Así, los datos serán recuperados.
En este sentido, el almacenamiento de datos en la memoria RAM no difiere fundamentalmente de su almacenamiento en disco, porque al escribir datos en el disco, también llegan primero a la caché de página y se graban físicamente de forma diferida. Esto depende de la opción de montaje del sistema de archivos. Pero, por si acaso, diré que ClickHouse no realiza fsync durante el insert.
Sin embargo, los datos en la memoria RAM se almacenan en el mismo formato que en el disco. La consulta select elige de la misma manera los fragmentos que se necesitan leer, selecciona los rangos de datos necesarios en fragmentos y los lee. Y prewhere funciona exactamente igual, independientemente de si los datos estaban en la RAM o en el disco.
¿Hasta cuántos valores únicos es eficaz Low Cardinality?
Low Cardinality está ingeniosamente diseñado. Crea diccionarios de datos, pero son locales. Primero, hay diccionarios específicos para cada fragmento; segundo, incluso dentro de un mismo fragmento, pueden ser diferentes para cada rango. Cuando el número de valores únicos alcanza un umbral —creo que un millón— el diccionario simplemente se pospone y se crea uno nuevo.
La respuesta en general: para cada rango local —digamos, para cada día— donde hay hasta un millón de valores únicos, Low Cardinality es eficiente. Después, lo que hay es un fallback, donde se utilizarán varios diccionarios diferentes en lugar de uno solo. Funciona más o menos igual que una columna normal de tipo string, tal vez un poco menos eficiente, pero no habrá una degradación seria del rendimiento.
¿Cuáles son las mejores prácticas para búsqueda de texto completo en una tabla con cinco mil millones de filas?
Hay varias respuestas posibles. La primera es decir que ClickHouse no es un sistema para búsqueda de texto completo. Para eso, existen sistemas especializados como y . Sin embargo, cada vez encuentro más personas que dicen que están migrando de Elasticsearch a ClickHouse.
¿Por qué ocurre esto? Ellos explican que Elasticsearch deja de manejar la carga con ciertos volúmenes, especialmente en lo que respecta a la construcción de índices. Los índices se vuelven demasiado voluminosos, y si simplemente trasladamos los datos a ClickHouse, el resultado es que se almacenan de manera mucho más eficiente en términos de espacio. Además, las consultas de búsqueda a menudo no eran tales que se necesitara encontrar alguna frase en todo el volumen de datos considerando la morfología, sino completamente distintas. Por ejemplo, encontrar en los últimos minutos en los logs algún sub-secuencia de bytes.
En este caso, en ClickHouse, creas un índice cuyo primer campo será la fecha con la hora. Y el mayor recorte de datos será precisamente por el rango de fechas. Dentro del rango de fechas seleccionado, por lo general, se puede realizar una búsqueda de texto completo incluso con el método de fuerza bruta utilizando like. El operador like en ClickHouse es el operador like más eficiente que puedes encontrar. Si encuentras uno mejor, házmelo saber.
Pero aún así, like implica un escaneo completo. Y el escaneo completo puede ser lento no solo por el CPU, sino también por el disco. Si de repente tienes un terabyte de datos al día y buscas una palabra en un día, tendrás que escanear un terabyte. Y seguramente estará en discos duros normales, y al final estarán tan ocupados que no podrás acceder a ese servidor por SSH.
En este caso, estoy listo para ofrecer otro pequeño truco. Es del tipo experimental: puede funcionar o puede no funcionar. En ClickHouse hay índices de texto completo en forma de filtros de Bloom trigramas. Nuestros colegas de la empresa Arenadata ya han probado estos índices, y a menudo funcionan exactamente como se esperaba.
Para utilizarlos correctamente, debes entender bien cómo funcionan: qué es un filtro de Bloom trigramas y cómo elegir su tamaño. Puedo decirte que ayudarán para consultas sobre frases raras y subcadenas que aparecen con poca frecuencia en los datos. En este caso, se seleccionarán subconjuntos por los índices, y se leerán menos datos.
Recientemente, ClickHouse introdujo funciones aún más avanzadas para la búsqueda de texto completo. Esto incluye, en primer lugar, la búsqueda de múltiples subcadenas de una sola vez, incluidas variantes que tienen en cuenta mayúsculas y minúsculas, sin tener en cuenta mayúsculas y minúsculas, con soporte para UTF-8 o solo para ASCII. Elige la opción más efectiva que necesites.
También se ha añadido la búsqueda de varias expresiones regulares en un solo paso. No necesitas escribir X like una subcadena o X like otra subcadena. Simplemente lo escribes todo y se ejecuta de la manera más eficiente posible.
Tercero, ahora hay búsqueda aproximada de expresiones regulares y búsqueda aproximada de subcadenas. Si alguien escribió una palabra con un error tipográfico, se buscará con la máxima coincidencia.
¿Cómo organizar mejor el acceso a ClickHouse para un gran número de usuarios?
Cuéntame cómo organizar mejor el acceso para una gran cantidad de consumidores y analistas. ¿Cómo formar una cola, priorizar solicitudes de max concurrent queries y con qué herramientas?
Si el clúster es lo suficientemente grande, una buena solución será levantar otros dos servidores que se convertirán en el punto de entrada para los analistas. Es decir, no permitir el acceso de los analistas a shards específicos del clúster, sino simplemente crear dos servidores vacíos, sin datos, y configurar los derechos de acceso en ellos. Además, las configuraciones de usuarios en solicitudes distribuidas se transfieren a servidores remotos. En otras palabras, configuras todo en estos dos servidores, y las configuraciones tienen efecto en todo el clúster.
Estos servidores son en principio sin datos, pero la cantidad de memoria RAM en ellos es muy importante para la ejecución de consultas. El disco también puede utilizarse para datos temporales si se activa la agregación externa o la ordenación externa.
Es importante revisar las configuraciones relacionadas con todos los posibles límites. Si ahora accedo al clúster 'Yandex.Metrics' como analista y hago una consulta select count from hits, inmediatamente recibiré una excepción indicando que no puedo ejecutar la consulta. La cantidad máxima de filas que se me permite escanear es de cien mil millones, y en total hay cincuenta billones en el clúster en una tabla. Esta es la primera limitación.
Supongamos que elimino la limitación en la cantidad de filas y ejecuto la consulta nuevamente. Entonces veré la siguiente excepción: la configuración está activada force index by date. No puedo ejecutar la consulta si no he especificado el rango de fechas. No hay que contar con que los analistas lo indiquen manualmente. Un caso típico es escribir el rango de fechas where event date between week. Y luego simplemente no poner el paréntesis en el lugar correcto, y en lugar de and se convirtió en or — or URL match. Si no hay restricciones, comenzará a escanear la columna URL y gastará una tonelada de recursos.
Además, en ClickHouse hay dos configuraciones de prioridades. Desafortunadamente, son muy primitivas. Una se llama simplemente priority. Si priority ≠ 0 y se están ejecutando consultas con algún nivel de prioridad, pero se ejecuta una consulta con un nivel de prioridad que tiene un valor menor, lo que significa mayor prioridad, entonces la consulta con un valor de prioridad mayor, que significa menor prioridad, simplemente se pausa y no funcionará en absoluto durante ese tiempo.
Esta es una configuración bastante grosera y no es adecuada para aquellos casos en los que hay una carga constante en el clúster. Pero si tiene consultas cortas e impulsivas que son importantes y el clúster está mayormente inactivo, esta configuración será adecuada.
La siguiente configuración de prioridades se llama prioridad de hilo del SO. Simplemente establece para todos los hilos que ejecutan consultas un valor nice para el programador de Linux. Funciona más o menos, pero sigue funcionando. Si se establece el valor nice mínimo, que es el más alto en valor, lo que significa la menor prioridad — y para las consultas de alta prioridad se establece -19, entonces la CPU consumirá las consultas de baja prioridad aproximadamente cuatro veces menos que las de alta prioridad.
También es necesario configurar el tiempo máximo de ejecución de consultas — digamos, cinco minutos. La velocidad mínima de ejecución de consultas — eso es lo más importante. Esta configuración ha existido por un tiempo y es necesaria no solo para afirmar que ClickHouse no se ralentiza, sino para forzarlo.
Imagínese que está configurando: si alguna consulta procesa menos de un millón de filas por segundo — eso no se puede hacer. Eso avergüenza nuestro buen nombre, nuestra buena base de datos. Vamos a prohibirlo. En realidad, hay dos configuraciones. Una se llama velocidad mínima de ejecución — en filas por segundo, y la otra se llama tiempo de espera antes de verificar la velocidad mínima de ejecución — por defecto son quince segundos. Es decir, se puede permitir hasta quince segundos, y luego, si es lento, simplemente lanzar una excepción — interrumpir la consulta.
También es necesario configurar cuotas. En ClickHouse hay una funcionalidad de cuotas incorporada, que contabiliza el consumo de recursos. Pero, desafortunadamente, no recursos físicos como CPU, discos, sino lógicos — la cantidad de consultas procesadas, filas y bytes leídos. Y se pueden configurar, por ejemplo, un máximo de cien consultas en cinco minutos y mil consultas en una hora.
¿Por qué es importante? Porque parte de las consultas de análisis se realizarán manualmente desde el cliente de ClickHouse. Y todo estará bien. Pero si tiene analistas avanzados en su empresa, escribirán un script, y en el script puede haber un error. Y este error provocará que la consulta se ejecute en un bucle infinito. De esto es de lo que hay que protegerse.
¿Se pueden enviar los resultados de una consulta a diez clientes?
Tenemos varios usuarios que les gusta hacer consultas muy grandes al mismo tiempo. La consulta es grande, se ejecuta rápidamente en principio, pero debido a que hay muchas de estas consultas simultáneamente, se vuelve muy doloroso. ¿Se puede ejecutar la misma consulta que llegó diez veces seguidas una sola vez y devolver su resultado a diez clientes?
El problema es que no tenemos resultados de caché o caché de datos intermedios. Hay caché de página del sistema operativo, que permite no leer los datos del disco nuevamente, pero, desafortunadamente, los datos aún tendrán que descomprimirse, deserializarse y procesarse nuevamente.
Me gustaría evitar esto de alguna manera, ya sea almacenando en caché datos intermedios o organizando consultas similares en alguna cola y añadiendo caché de resultados. Actualmente tenemos en desarrollo una solicitud de extracción que agrega caché para consultas, pero solo para subconsultas en las secciones in y join, es decir, la solución no es completa.
Sin embargo, también nos encontramos en esta situación. Un ejemplo canónico son las consultas con paginación. Hay un informe, contiene varias páginas y se hace una consulta limit 10. Luego lo mismo, pero limit 10,10. Después la siguiente página. Y se pregunta, ¿por qué estamos calculando todo esto cada vez? Pero ahora no hay soluciones, y no se puede evitar.
Hay una solución alternativa que se coloca como un sidecar junto a ClickHouse — .
Kirill Shvakov: En ClickHouse Proxy hay un limitador de tasa integrado y un caché de resultados incorporado. Se han hecho muchas configuraciones, porque se resolvía un problema similar. El Proxy permite limitar las consultas, organizándolas en una cola, y configurando cuánto tiempo vive el caché de consultas. Si las consultas fueron realmente iguales, el Proxy las devolverá muchas veces, mientras que solo hará una llamada a ClickHouse.
Nginx también tiene caché en la versión gratuita, y esto también funcionará. Nginx incluso tiene configuraciones que, si las solicitudes llegan al mismo tiempo, ralentizará otras hasta que una se complete. Pero precisamente en ClickHouse Proxy, la configuración está hecha mucho mejor. Se ha diseñado específicamente para ClickHouse, para estas solicitudes, por lo que es más adecuado. Y se instala fácilmente.
¿Qué hacer con operaciones asíncronas y vistas materializadas?
Hay un problema en el que las operaciones con el motor de reemplazo son asíncronas: primero se escriben los datos, luego se realiza su consolidación. Si debajo de la tabla hay una tabla materializada con algunos agregados, entonces los duplicados se escribirán en ella. Y si no hay una lógica compleja, los datos estarán duplicados. ¿Qué se puede hacer al respecto?
Hay una solución obvia: implementar un desencadenador para un cierto tipo de vista materializada durante la operación asíncrona de consolidación. ¿Existen algunas "balas de plata", planes para implementar funcionalidades similares?
Vale la pena profundizar en cómo funciona la deduplicación. Lo que voy a contar ahora no se relaciona directamente con la pregunta, pero es bueno tenerlo en mente.
Al insertar en una tabla replicada, hay deduplicación de bloques completos insertados. Si insertaste el mismo bloque nuevamente, que contiene la misma cantidad de las mismas filas en el mismo orden, entonces los datos se deduplican. Recibirás 'Ok' como respuesta a la inserción, pero en realidad se grabará un solo bloque de datos, y no se duplicará.
Esto es necesario para la certidumbre. Si al insertar recibiste 'Ok', significa que tus datos se han insertado. Si recibiste un error de ClickHouse, significa que no se han insertado, y es necesario repetir la inserción. Pero si durante la inserción se interrumpió la conexión, no sabes si los datos fueron insertados o no. La única opción es repetir la inserción nuevamente. Si los datos realmente fueron insertados, y los insertaste nuevamente, hay deduplicación de bloques. Esto es necesario para evitar duplicados.
Y también es importante cómo funciona para las vistas materializadas. Si los datos fueron deduplicados al insertarse en la tabla principal, entonces tampoco irán a la vista materializada.
Ahora sobre la cuestión. Tienes una situación más complicada porque estás registrando duplicados de líneas individuales. Es decir, no es un paquete completo el que se duplica, sino líneas específicas, y se colapsan en el fondo. De hecho, los datos se colapsarán en la tabla principal, y en la vista materializada irán las no colapsadas, y durante las combinaciones no ocurrirá nada con las vistas materializadas. Porque la vista materializada no es más que un trigger de inserción. En otras operaciones no ocurre nada adicional con ella.
Y aquí no puedo complacerte de ninguna manera. Solo hay que buscar una solución específica para este caso. Por ejemplo, ¿es posible hacer un reemplazo en la vista materializada, y tal vez el método de deduplicación también funcione? Pero, desafortunadamente, no siempre. Si es agregadora, entonces no se podrá.
Kirill Shvakov: Nosotros también teníamos un montón de soluciones improvisadas en su momento. Había un problema de que había impresiones publicitarias, y había algunos datos que podíamos mostrar en tiempo real: simplemente eran impresiones. Rara vez se duplican, pero, si eso sucede, luego las colapsamos de todos modos. Y había cosas que no se podían duplicar: clics y toda esa historia. Pero también queríamos mostrarlas prácticamente de inmediato.
¿Cómo se hicieron las vistas materializadas? Había vistas en las que se escribe directamente: se están registrando los datos en crudo, y se escribe en las vistas. Allí en algún momento los datos no son muy correctos, se duplican y así sucesivamente. Y hay una segunda parte de la tabla, donde se ven exactamente igual que las vistas materializadas, es decir, estructuralmente son idénticas. Cada cierto tiempo recalculamos los datos, contamos los datos sin duplicados y los escribimos en esas tablas.
Accedimos a través de la API: hacerlo a mano en ClickHouse no funcionará. Y la API verifica: cuando tengo la fecha de la última adición en la tabla, donde garantizadamente ya hay datos correctos, calculados, y hace una consulta a una tabla y a otra tabla. De una extrae hasta un cierto momento, y de la otra complementa lo que aún no ha sido contabilizado. Y esto funciona, pero no con los medios de un solo ClickHouse.
Si tiene algún API - para analistas, para usuarios - entonces, en principio, es una opción. Siempre puedes contar, siempre recalcular. Esto se puede hacer una vez al día o en cualquier otro momento. Usted elige el rango que no necesita y no es crítico.
En ClickHouse hay muchos registros. ¿Cómo puedo ver todo lo que está sucediendo con el servidor en tiempo real?
En ClickHouse hay una gran cantidad de diferentes registros, y esta cantidad está aumentando. En las nuevas versiones, algunos de ellos incluso están habilitados por defecto, mientras que en las versiones antiguas deben activarse al actualizar. Sin embargo, cada vez son más. Me gustaría ver, en última instancia, qué está sucediendo actualmente con mi servidor, tal vez en algún panel de control resumen.
¿No tiene en su equipo de ClickHouse, o en los equipos de sus amigos, que soporte alguna funcionalidad de paneles de control predefinidos que muestren estos registros en forma de un producto listo? En última instancia, ver registros en ClickHouse está bien. Pero sería genial si ya estuvieran preparados en forma de un panel de control. Disfrutaría mucho de eso.
Existen paneles de control, aunque no están estandarizados. En nuestra empresa, alrededor de 60 equipos utilizan ClickHouse, y lo más extraño es que muchos de ellos tienen paneles de control que hicieron por sí mismos, y son un poco diferentes. Algunos equipos utilizan una instalación interna de 'Yandex.Cloud'. Allí hay algunos informes listos, aunque no todos los necesarios. Otros tienen los suyos.
Mis colegas de 'Metricas' tienen su propio panel de control en Grafana, y yo tengo el mío sobre su clúster. Allí miro cosas como el cache hit para el caché de los puntos. Y aún más complicado es que utilizamos diferentes herramientas. Yo creé mi panel de control en una herramienta muy antigua llamada Graphite-web. Es completamente fea. Y todavía la sigo utilizando, aunque Grafana probablemente sería más conveniente y bonita.
La característica básica de los dashboards es la misma. Se trata de métricas del sistema del clúster: CPU, memoria, disco, red. Otras son el número de solicitudes simultáneas, el número de fusiones simultáneas, el número de solicitudes por segundo, el número máximo de partes para las particiones de tablas MergeTree, el retraso de replicación, el tamaño de la cola de replicación, el número de filas insertadas por segundo, el número de bloques insertados por segundo. Esto es todo lo que se obtiene no de los registros, sino de las métricas.
Vladimir Kolobaev: Alexey, me gustaría hacer un pequeño ajuste. Hay Grafana. Grafana tiene una fuente de datos, que es ClickHouse. Es decir, puedo hacer consultas directamente en ClickHouse desde Grafana. En ClickHouse hay una tabla de registros, la misma para todos. Quiero referirme a esta tabla de registros en Grafana y ver las solicitudes que realiza mi servidor. Sería genial tener un dashboard así.
Lo hice por mí mismo. Pero tengo una pregunta: si todo está estandarizado y Grafana es utilizada por todos, ¿por qué no hay un dashboard oficial así en Yandex?
Kirill Shvakov: De hecho, la fuente de datos que va a ClickHouse es actualmente soportada por Altinity. Solo quiero dar una dirección, hacia dónde indagar y a quién empujar. Se puede preguntarles, porque Yandex, después de todo, desarrolla ClickHouse, y no la historia a su alrededor. Altinity es la principal empresa que actualmente promueve ClickHouse. No lo abandonarán, lo apoyarán. Porque, en principio, para subir un dashboard al sitio de Grafana, solo necesitas registrarte y cargarlo; no hay problemas especiales.
Alexey Milovidov: En el último año, ClickHouse ha agregado muchas capacidades para el perfilado de consultas. Hay métricas para cada consulta sobre el uso de recursos. Y muy recientemente se ha añadido un perfilador de consultas de nivel aún más bajo, para ver dónde cada consulta utiliza cada milisegundo. Pero para aprovechar esta funcionalidad, tengo que abrir el cliente de consola y escribir la consulta que siempre olvido. La guardé en algún lugar y olvido constantemente dónde exactamente.
Me gustaría que existiera una herramienta en la que simplemente estuviera escrito: aquí están sus consultas pesadas, agrupadas por clases de consultas. Hago clic en alguna de ellas y me dicen que es pesada por esta razón. Actualmente, no hay ninguna solución así. Y es bastante extraño que, cuando la gente me pregunta: “¿Hay algún panel de control listo para Grafana?”, yo diga: “Visita el sitio de Grafana, ahí está la comunidad ‘Paneles’, y hay un panel de Dima, hay un panel de Kostya. No sé qué es eso, yo mismo no lo he utilizado”.
¿Cómo influir en los merges para que el servidor no caiga en OOM?
Tengo una tabla, en la tabla hay solo una partición, que es ReplacingMergeTree. He estado escribiendo datos en ella durante cuatro años. Necesitaba hacer un alter y eliminar algunos datos.
Lo hice, y durante el procesamiento de esta consulta, toda la memoria en todos los servidores del clúster se consumió y todos los servidores del clúster se fueron juntos a OOM. Luego, todos se levantaron juntos, comenzaron a realizar el merge de esta misma operación, de este bloque de datos, y nuevamente cayeron en OOM. Luego, se levantaron de nuevo y cayeron otra vez. Y esta situación no se detenía.
Luego resultó que, de hecho, era un error que los chicos solucionaron. Eso es genial, muchas gracias. Pero quedó un mal sabor. Y ahora, cuando pienso en hacer algún merge en la tabla, surge la pregunta: ¿por qué no puedo influir de alguna manera en estos merges? Por ejemplo, limitarlos por la cantidad de memoria RAM necesaria, o por su cantidad en general, que se procesará específicamente en esta tabla.
Tengo una tabla llamada ‘Métricas’, por favor, procésala en dos hilos. No es necesario crear diez o cinco merges en paralelo, hazlo en dos. Creo que en dos me alcanzará la memoria, pero para procesar diez puede que no alcance. ¿Por qué persiste el miedo? Porque la tabla está creciendo, y algún día enfrentaré la situación de que, en general, no por un error, sino porque los datos cambiarán en tal cantidad que simplemente no tendré suficiente memoria en el servidor. Y entonces, el servidor caerá en OOM durante el merge. Además, puedo cancelar la mutación, pero los merges ya no.
Sabes que durante las fusiones, el servidor no se caerá por OOM, porque al fusionar solo se utiliza la cantidad de memoria RAM correspondiente a un pequeño rango de datos. Así que todo estará bien independientemente del volumen de datos.
Vladimir Kolobaev: Está bien. Aquí hay un detalle, que después de que hicimos la corrección de errores, descargué una nueva versión y en otra tabla, más pequeña, donde hay muchas particiones, realicé una operación similar. Y durante la fusión, el servidor consumió alrededor de 100 GB de memoria RAM. Tenía 150 ocupados, 100 los consumió, y me quedó un espacio de 50 GB, por lo que no caí en OOM.
¿Qué me protege en este momento de caer en OOM si realmente está consumiendo 100 GB de memoria RAM? ¿Qué hacer en la situación si de repente me quedo sin memoria RAM durante las fusiones?
Alexey Milovidov: Hay un problema, que el consumo de memoria RAM durante las fusiones no tiene límite. Y el segundo problema es que, si se ha programado alguna fusión, es necesario ejecutarla, porque está registrada en el log de replicación. El log de replicación son las acciones necesarias para llevar la réplica a un estado consistente. Si no se realizan las manipulaciones manuales que retroceden este log de replicación, la fusión tendrá que ejecutarse de una forma u otra.
Por supuesto, no estaría de más tener un límite de memoria que proteja 'por si acaso' contra OOM. Esto no ayudará a que la fusión se ejecute; comenzará de nuevo, alcanzará cierto umbral, lanzará una excepción y después comenzará de nuevo; de esto no saldrá nada bueno. Pero, en principio, establecer este límite sería útil.
¿Cómo se desarrollará el controlador Golang para ClickHouse?
El controlador Golang, que fue escrito por Kirill Shvakov, ahora parece ser oficialmente soportado por el equipo de ClickHouse. Él , ahora es grande y real.
Una pequeña observación. Hay un almacenamiento maravilloso y querido por todos, el almacenamiento de formas normales de orden infinito — es Vertica. También tienen su propio controlador oficial de Python, el cual es mantenido por los desarrolladores de Vertica. Y varias veces ha sucedido que las versiones del almacenamiento y del controlador se desincronizaban bastante, y en algún momento el controlador dejaba de funcionar. Y el segundo punto. La atención a este controlador oficial, me parece, se lleva a cabo en el sistema 'nipplo' — tú les envías un problema, y este queda pendiente para siempre.
Tengo dos preguntas. Actualmente, el controlador de Golang de Kirill es casi la forma predeterminada de comunicarse desde Golang con ClickHouse. Excepto que alguien aún se comunica a través de la interfaz http, porque le gusta más así. ¿Cómo se desarrollará este controlador? ¿Se sincronizará con algunos cambios disruptivos en el almacenamiento mismo? ¿Y cuál es el procedimiento para considerar los problemas?
Kirill Shvakov: Primero — cómo funciona todo burocráticamente. Este aspecto no se discutió, así que no tengo respuesta.
Para responder a la pregunta sobre los problemas, se necesita un poco de historia del controlador. Trabajé en una empresa con grandes cantidades de datos. Era un sistema de publicidad con un enorme número de eventos que necesitaban almacenarse. Y en algún momento apareció ClickHouse. Vertimos datos allí, y en un principio todo funcionó bien, pero luego ClickHouse falló. En ese momento decidimos que no lo necesitábamos.
Un año después volvimos a la idea de usar ClickHouse, y necesitábamos alguna forma de escribir datos allí. La premisa era que el hardware era muy débil, había pocos recursos. Pero siempre habíamos trabajado así, así que miramos hacia el protocolo nativo.
Como trabajamos en Go, estaba claro que necesitábamos un controlador en Go. Yo lo desarrollé prácticamente a tiempo completo — era mi tarea laboral. Hasta cierto punto lo completamos, y en principio nadie pensaba que alguien más lo usaría. Luego llegó CloudFlare con exactamente el mismo problema, y durante un tiempo trabajamos muy alineados con ellos, porque tenían las mismas tareas. De hecho, lo hicimos tanto en ClickHouse como en el controlador.
En algún momento simplemente dejé de trabajar en ello, porque mi actividad en términos de ClickHouse y en el trabajo cambió un poco. Por eso, los problemas no se resuelven. De vez en cuando, hay personas que suben cambios al repositorio porque necesitan algo. Entonces miro la solicitud de extracción y a veces incluso corrijo algo yo mismo, pero eso ocurre raramente.
Quiero volver al controlador. Hace unos años, cuando todo esto comenzó, ClickHouse también era diferente y tenía otras capacidades. Ahora hay comprensión sobre cómo rehacer el controlador para que funcione bien. Si esto ocurre, la versión 2 será incompatible debido a los problemas acumulados.
No sé cómo organizar esto. No tengo mucho tiempo. Si hay personas que van a realizar ajustes en el controlador, podré ayudarles y explicarles qué hacer. Pero la participación activa de 'Yandex' en el desarrollo del proyecto aún no se ha discutido.
Alexey Milovidov: En realidad, no hay ninguna burocracia por el momento respecto a estos controladores. La única cosa es que se han trasladado a una organización oficial, es decir, este controlador ha sido reconocido como la solución oficial por defecto para Go. Hay otros controladores, pero estos van por separado.
No tenemos ningún desarrollo interno para estos controladores. La pregunta es si podremos contratar a una persona específica, no para este controlador en concreto, sino para el desarrollo de todos los controladores de la comunidad, o si podremos encontrar a alguien externamente.
El diccionario externo no se carga después de reiniciar con la configuración de lazy_load activada. ¿Qué hacer?
Tenemos activada la configuración de lazy_load, y después de reiniciar el servidor, el diccionario no se carga automáticamente. Solo se carga después de que un usuario accede a este diccionario. Y en la primera solicitud, se produce un error. ¿Hay alguna manera de cargar diccionarios automáticamente con ClickHouse, o necesitamos controlar siempre su disponibilidad para que los usuarios no obtengan errores?
Puede que tengamos una versión antigua de ClickHouse, por lo que el diccionario no se cargó automáticamente. ¿Puede ser eso?
En primer lugar, los diccionarios se pueden cargar forzosamente mediante la consulta system reload dictionaries. En segundo lugar, en cuanto al error: si el diccionario ya está cargado, las consultas funcionarán con los datos que fueron cargados. Si el diccionario aún no se ha cargado, se cargará justo en el momento de la consulta.
Para diccionarios pesados, esto no es muy conveniente. Por ejemplo, es necesario recuperar un millón de filas de MySQL. Algunos hacen un simple select, pero este select esperará esas millones de filas. Aquí hay dos soluciones. La primera es desactivar lazy_load. La segunda es que, cuando se inicia el servidor, antes de ponerle carga, se haga system reload dictionary o simplemente ejecutar una consulta que use el diccionario. Entonces, el diccionario se cargará. Es necesario controlar la disponibilidad de los diccionarios con la configuración lazy_load activada, ya que ClickHouse no los trae automáticamente.
Sobre la última pregunta, la respuesta es que o la versión es antigua, o es necesario depurar.
¿Qué hacer cuando el reinicio del sistema no carga ninguno de los muchos diccionarios si al menos uno de ellos falla con un error?
También hay una pregunta sobre system reload dictionaries. Tenemos dos diccionarios: uno no se carga y el otro sí. System reload dictionaries, en este caso, no carga ninguno de los diccionarios, y se tiene que cargar específicamente uno por su nombre usando system reload dictionary. ¿Esto también está relacionado con la versión de ClickHouse?
Quiero darles buenas noticias. Este comportamiento ha cambiado. Esto significa que si actualizan ClickHouse, también cambiará. Si no están satisfechos con el comportamiento actual system reload dictionaries, actualicen y esperemos que mejore.
¿Hay una manera de configurar las credenciales en la configuración de ClickHouse sin que se muestren en caso de errores?
La siguiente pregunta es sobre los errores relacionados con el diccionario, es decir, las credenciales. Hemos configurado las credenciales de conexión en la configuración de ClickHouse para el diccionario, y en caso de error, recibimos estas credenciales y la contraseña en la respuesta.
Resolver esta error requiere extraer las credenciales a la configuración del controlador ODBC. ¿Hay alguna forma de configurar las credenciales en la configuración de ClickHouse, pero sin mostrar estas credenciales en caso de errores?
Aquí la solución definitiva es indicar estas credenciales en odbc.ini, y en ClickHouse solo indicar el nombre de la fuente de datos ODBC. No habrá esto para otras fuentes de diccionarios: ni para el diccionario de MySQL, ni para los demás, no deberían ver la contraseña en el mensaje de error. También revisaré para ODBC — si existe, simplemente hay que eliminarlo.
Bonus: fondos para Zoom de las reuniones
Al hacer clic en la imagen, para los lectores más persistentes, se abrirán fondos adicionales de las reuniones. Apagamos el fuego junto con las mascotas de las tecnologías de Avito, discutimos con colegas desde la sala del administrador del sistema o el viejo club de computadoras y realizamos un daily bajo el puente con un fondo de graffiti.
Fuente: habr.com
