En el mundo actual, donde las empresas manejan volúmenes de datos cada vez más grandes, la capacidad para procesar y almacenar información masiva de manera eficiente se ha convertido en un requisito indispensable. Esta necesidad es especialmente evidente en plataformas que gestionan datos en tiempo real o que procesan grandes volúmenes provenientes de múltiples fuentes, como las configuraciones en la nube o registros de actividades. CloudQuery, una plataforma de gobierno en la nube, ha enfrentado estos desafíos y ha desarrollado una solución innovadora para manejar la inserción de miles de millones de filas en ClickHouse sin sacrificar rendimiento ni estabilidad. ClickHouse es reconocido como un sistema de base de datos columnar de alto rendimiento diseñado para cargas analíticas, con una velocidad notable tanto en la escritura como en la lectura de grandes cantidades de datos. Su diseño le permite simular una experiencia similar a Postgres pero con la potencia necesaria para procesar grandes datasets.
Sin embargo, a medida que CloudQuery escaló y comenzó a manejar ingestas de datos masivas que superan los miles de millones de filas al mes, surgieron limitaciones importantes, especialmente relacionadas con el consumo de memoria durante las operaciones de inserción masiva. El problema central radica en cómo ClickHouse maneja la memoria durante las operaciones complejas o voluminosas. Durante inserciones masivas, consultas de agrupación o ordenamientos, ClickHouse inicialmente carga todo el conjunto de datos en memoria antes de optar por estrategias de derrame a disco. Esta operación, si bien optimiza la velocidad bajo cargas moderadas, se convierte en un cuello de botella crítico cuando el volumen de datos ingresa en la escala de miles de millones de filas. La sobrecarga de memoria no sólo ralentiza la operación sino que provoca que el sistema finalice las consultas para evitar caídas mayores, lo que en entornos productivos representa un problema significativo.
Aunque existen configuraciones dentro de ClickHouse para mitigar este problema, como ajustar parámetros que controlan cuándo y cómo se realiza el derrame a disco, estas soluciones son insuficientes para cargas muy pesadas. En la práctica, incluso con un ajuste fino de dichos parámetros, las consultas podían fallar debido a que el sistema intenta procesar bloques de datos de gran tamaño en memoria, mucho antes de que las estrategias de derrame puedan activarse eficazmente. Ante esta situación, el equipo técnico de CloudQuery implementó una solución que no implicó cambios en el esquema ni la incorporación de infraestructura adicional como colas de mensajes o sistemas intermedios. La solución, denominada Insert-Splitter con segmentación por rango UUID, parte del principio de dividir las grandes operaciones de inserción en piezas más pequeñas y manejables que se procesan de forma independiente. El fundamento del método se basa en la asignación de UUID a cada fila de datos.
Estos identificadores únicos globales, debido a su naturaleza, pueden segmentarse en rangos que permiten dividir un conjunto masivo de datos en bloques equitativamente distribuidos. Así, en vez de insertar miles de millones de filas en una sola operación que sobrecarga la memoria, la tarea se fragmenta en muchas inserciones parciales que ClickHouse puede manejar más eficientemente. El algoritmo Insert-Splitter calcula cuántas particiones son necesarias según el tamaño total de datos y el número máximo de filas por inserción deseado. El siguiente paso es generar rangos UUID que respeten la distribución interna de estos identificadores, teniendo en cuenta ciertas particularidades de ClickHouse, como la forma en que ordena los UUID y las variaciones específicas de los bits de versión en UUIDv4. Estas consideraciones permiten asegurar que la distribución de las particiones sea uniforme, evitando que alguna partición quede desbalanceada y se convierta en un punto de presión para la memoria.
A nivel práctico, esto se traduce en un proceso donde cada inserción parcial filtra el conjunto original utilizando los rangos UUID correspondientes. Este diseño garantiza que la memoria consumida en cada operación se mantenga bajo control y evita que se ejecuten operaciones costosas sobre conjuntos de datos masivos simultáneamente. Los resultados de esta estrategia han sido notables en producción. Por ejemplo, una sincronización realizada para un cliente con más de mil cuentas AWS mostró una reducción aproximada del 75% en el uso máximo de memoria durante las inserciones. Lo que antes exigía cerca de 8.
5 GB de memoria en una única inserción, se tradujo en operaciones fragmentadas que consumían en promedio poco más de 2 GB por inserción, manteniendo tiempos totales de procesamiento similares y al mismo tiempo eliminando fallos relacionados con límites de memoria. Más allá de mejorar la estabilidad, esta fragmentación permite la ejecución paralela de los procesos de inserción, lo que optimiza aún más el rendimiento general y reduce el tiempo total necesario para sincronizar grandes volúmenes de datos. Además, al ser una solución implementada a nivel de aplicación, no requiere modificar la estructura de la base de datos ni añadir complejidades operativas significativas, lo que facilita su adopción y mantenimiento. Este enfoque también destaca por ser determinístico y fiable. Al usar rangos UUID definidos cuidadosamente, CloudQuery garantiza que cada fila solo se procese una vez durante cada segmento, evitando conflictos o duplicidades.
Además, gracias a la uniformidad en la distribución de los datos, se consigue un balance óptimo en el uso de recursos y un alto nivel de predictibilidad en el comportamiento del sistema. La experiencia demuestra que muchas bases de datos columnar, como ClickHouse, tienen limitaciones intrínsecas en gestión de memoria ante cargas masivas, especialmente en operaciones que requieren procesamiento completo en memoria antes de cualquier optimización como el derrame a disco o el uso de índices. Por lo tanto, implementar estrategias de preprocesamiento o segmentación en el nivel de aplicación puede marcar una diferencia considerable para alcanzar escalabilidad y estabilidad en entornos de big data. Este descubrimiento y desarrollo por parte de CloudQuery aporta una valiosa lección para la comunidad técnica: frente a problemas complejos de memoria y rendimiento, no siempre es necesario complicar el ecosistema con infraestructuras adicionales o reestructuraciones profundas. A veces, una solución más sencilla, basada en dividir el problema en partes más pequeñas y manejables, puede ser la clave para superar los retos más desafiantes.
Sin embargo, el Insert-Splitter con segmentación por rangos UUID no es un reemplazo para seguir buenas prácticas en diseño de bases de datos. Es fundamental seguir optimizando esquemas, configurar adecuadamente claves de ordenación, particionar datos cuando sea posible y aplicar compresión adecuada para reducir el consumo de recursos. También es importante monitorear continuamente el comportamiento en producción, analizando indicadores como el número de partes activas y la eficiencia de los procesos de merge, para evitar degradaciones en el rendimiento. Para organizaciones que gestionan volúmenes masivos de datos en la nube y encuentran limitaciones en sus herramientas de base de datos, la solución de CloudQuery representa un camino viable para mejorar la escalabilidad sin sacrificar estabilidad ni incrementar la complejidad. Su enfoque demuestra cómo ingenio, comprensión profunda de las herramientas y ajustes a nivel de aplicación pueden llevar a resultados sorprendentes en el mundo del análisis de grandes datos.
En conclusión, la combinación de una técnica inteligente de segmentación con la experiencia en administración de datos a escala, como la de CloudQuery, ha demostrado que es posible superar las limitaciones de memoria de sistemas como ClickHouse para manejar billones de filas. La clave está en abordar el problema desde la raíz, dividiendo la carga en partes pequeñas y distribuidas uniformemente que el sistema pueda procesar sin colapsar, cambiando radicalmente la forma en que se gestionan las ingestas masivas sin necesidad de arquitecturas complejas ni costos adicionales. Para los interesados en adoptar o adaptar este enfoque, es recomendable comenzar por entender la naturaleza de sus UUIDs, probar la distribución en sus datasets y experimentar con distintos tamaños de partición hasta encontrar el equilibrio más adecuado para sus entornos. Al hacerlo, no solo mejorarán el rendimiento sino que disfrutarán de mayor control y confianza en sus flujos de datos, garantizando que sus infraestructuras de bases de datos estén preparadas para los retos del futuro.