KDB +, un producto
KX , es una base de datos de columnas de círculo estrecho, extremadamente rápida y ampliamente conocida, diseñada para almacenar series de tiempo y cálculos analíticos basados en ellas. Inicialmente, disfrutó (y disfruta) de una gran popularidad en la industria financiera: es utilizado por los 10 principales bancos de inversión y muchos fondos de cobertura conocidos, intercambios y otras organizaciones. Recientemente, KX decidió expandir su base de clientes y ahora ofrece soluciones en otras áreas donde hay una gran cantidad de datos ordenados por tiempo o de otra manera: telecomunicaciones, bioinformática, producción, etc. En particular, se convirtieron en socios del equipo Aston Martin Red Bull Racing en la Fórmula 1, donde ayudan a recopilar y procesar datos de sensores de automóviles y analizar pruebas en un túnel de viento. En este artículo quiero decirle qué características de KDB + lo hacen superproductivo, por qué las empresas están dispuestas a gastar mucho dinero en él y, finalmente, por qué esto no es realmente una base de datos.

En este artículo trataré de decir en general qué es KDB +, qué características y limitaciones tiene, cuál es su beneficio para las empresas que desean procesar grandes volúmenes de datos. No entraré en los detalles de la implementación de KDB + y los detalles de su lenguaje de programación Q. Ambos temas son muy extensos y merecen artículos separados. Puede encontrar mucha información sobre estos temas en code.kx.com, incluido un libro sobre Q - Q For Mortals (vea el enlace a continuación).
Algunos términos
- Base de datos en memoria. Una base de datos que almacena datos en RAM para un acceso más rápido. Las ventajas de dicha base de datos son comprensibles, y las desventajas son la posibilidad de pérdida de datos, la necesidad de tener mucha memoria en el servidor.
- Base de datos de columnas. Una base de datos donde los datos se almacenan en serie, no registro por registro. La principal ventaja de dicha base de datos es que los datos de una columna se almacenan juntos en el disco y en la memoria, lo que acelera enormemente el acceso a ellos. No es necesario cargar columnas que no se utilizan en la solicitud. La principal desventaja es que es difícil modificar y eliminar registros.
- Series de tiempo. Datos con una columna como fecha u hora. Como regla general, ordenar a tiempo es importante para dichos datos, para que pueda determinar fácilmente qué registro precede o sigue al actual, o para aplicar funciones cuyo resultado depende del orden de los registros. Las bases de datos clásicas se basan en un principio completamente diferente: representan un conjunto de registros como un conjunto, donde el orden de los registros no está definido en principio.
- Vector En el contexto, KDB + es una lista de elementos del mismo tipo atómico, por ejemplo, números. En otras palabras, una matriz de elementos. Las matrices, a diferencia de las listas, pueden almacenarse de forma compacta y procesarse utilizando las instrucciones del procesador de vectores.
Antecedentes historicos
KX fue fundada en 1993 por Arthur Whitney, quien anteriormente trabajó en Morgan Stanley Bank en A +, el sucesor de APL, un lenguaje muy original y popular en el mundo financiero. Por supuesto, en KX, Arthur continuó con el mismo espíritu y creó el lenguaje vectorial funcional K, guiado por las ideas del minimalismo radical. Los programas K parecen un conjunto desordenado de signos de puntuación y caracteres especiales, el significado de los caracteres y las funciones depende del contexto, y cada operación tiene mucho más significado que en los lenguajes de programación habituales. Debido a esto, el programa K ocupa un mínimo de espacio (varias líneas pueden reemplazar páginas de texto de un lenguaje detallado como Java) y es una implementación súper concentrada del algoritmo.
Una función en K que implementa la mayor parte del generador de analizador LL1 de acuerdo con una gramática dada:
1. pp:{q:{(x;p3(),y)};r:$[-11=@x;$x;11=@x;q[`N;$*x];10=abs@@x;q[`N;x] 2. ($)~*x;(`P;p3 x 1);(1=#x)&11=@*x;pp[{(1#x;$[2=#x;;,:]1_x)}@*x] 3. (?)~*x;(`Q;pp[x 1]);(*)~*x;(`M;pp[x 1]);(+)~*x;(`MP;pp[x 1]);(!)~*x;(`Y;p3 x 1) 4. (2=#x)&(@x 1)in 100 101 107 7 -7h;($[(@x 1)in 100 101 107h;`Ff;`Fi];p3 x 1;pp[*x]) 5. (|)~*x;`S,(pp'1_x);2=#x;`C,{@[@[x;-1+#x;{x,")"}];0;"(",]}({$[".sC"~4#x;6_-2_x;x]}'pp'x);'`pp]; 6. $[@r;r;($[1<#r;".s.";""],$*r),$[1<#r;"[",(";"/:1_r),"]";""]]}
Arthur encarnó esta filosofía de eficiencia extrema con un mínimo de movimientos corporales en KDB +, que apareció en 2003 (creo que ahora está claro de dónde viene la letra K del nombre) y no hay nada más que un intérprete de la cuarta versión del lenguaje K. Una versión más agradable para el ojo del usuario se agrega a K K bajo el nombre Q. Q también agrega soporte para un dialecto SQL específico - QSQL, y en el intérprete - soporte para tablas como tipo de datos del sistema, herramientas para trabajar con tablas en memoria y en disco, etc.
Por lo tanto, desde el punto de vista del usuario, KDB + es solo un intérprete de Q con soporte para tablas y expresiones de estilo LINQ tipo SQL de C #. Esta es la diferencia más importante entre KDB + y otras bases de datos y su principal ventaja competitiva, que a menudo se pasa por alto. Esta no es una base de datos + un lenguaje auxiliar que está deshabilitado, sino un potente lenguaje de programación completo + soporte incorporado para funciones de base de datos. Esta diferencia jugará un papel decisivo en la lista de todos los beneficios de KDB +. Por ejemplo ...
Tamaño
Según los estándares modernos, KDB + es solo un tamaño microscópico. Este es literalmente un archivo ejecutable más pequeño que un megabyte y un pequeño archivo de texto con algunas funciones del sistema. En realidad, menos de un megabyte y para este programa las compañías pagan decenas de miles de dólares por año por un procesador en el servidor.
- Este tamaño permite que KDB + se sienta genial en cualquier hardware, desde el microordenador Pi hasta servidores con terabytes de memoria. Esto no afecta la funcionalidad de ninguna manera; además, Q se inicia instantáneamente, lo que permite su uso incluso como lenguaje de script.
- Con este tamaño, el intérprete Q se coloca completamente en la memoria caché del procesador, lo que acelera la ejecución de los programas.
- Con este tamaño del archivo ejecutable, el proceso Q ocupa un espacio de memoria insignificante; puede ejecutarlos en cientos. Al mismo tiempo, si es necesario, Q puede operar con decenas o cientos de gigabytes de memoria en un solo proceso.
Versatilidad
Q es perfecto para una variedad de tareas. El proceso Q puede servir como una base de datos histórica y proporcionar acceso rápido a terabytes de información. Por ejemplo, tenemos docenas de bases de datos históricas, en algunas de las cuales un día de datos sin comprimir toma más de 100 gigabytes. Sin embargo, con restricciones razonables, la consulta de la base de datos se ejecutará en decenas a cientos de milisegundos. En general, tenemos un tiempo de espera universal para las solicitudes de los usuarios (30 segundos) y funciona muy raramente.
Con la misma facilidad, Q puede ser una base de datos en memoria. Agregar datos nuevos a las tablas en la memoria es tan rápido que las consultas de los usuarios son un factor limitante. Los datos en las tablas se almacenan en columnas, lo que significa que cualquier operación en la columna utilizará el caché del procesador a plena capacidad. Además de esto, KX trató de implementar todas las operaciones básicas, como la aritmética a través de instrucciones de procesador de vectores, maximizando su velocidad. Q puede realizar tareas que no son características de las bases de datos, por ejemplo, procesar datos de transmisión y calcular en “tiempo real” (con un retraso de decenas de milisegundos a varios segundos dependiendo de la tarea) varias funciones agregadas para instrumentos financieros para diferentes intervalos de tiempo o construir un modelo del impacto de la perfección transacciones al mercado y llevar a cabo su perfil casi inmediatamente después de su finalización. En tales problemas, la mayoría de las veces el retraso de tiempo principal no es Q, sino la necesidad de sincronizar datos de diferentes fuentes. Se logra una alta velocidad debido al hecho de que los datos y las funciones que los procesan están en el mismo proceso, y el procesamiento se reduce a la ejecución de varias expresiones y uniones QSQL que no se interpretan, pero se ejecutan en código binario.
Finalmente, cualquier proceso de servicio también se puede escribir en Q. Por ejemplo, los procesos de Gateway que distribuyen automáticamente las solicitudes de los usuarios a las bases de datos y servidores necesarios. El programador tiene total libertad para implementar cualquier algoritmo de equilibrio, priorización, tolerancia a fallas, derechos de acceso, cuotas y, en general, lo que su corazón desee. El principal problema aquí es que debe implementar todo esto usted mismo.
Por ejemplo, enumeraré qué tipos de procesos tenemos. Todos ellos se usan activamente y trabajan juntos, combinando docenas de bases de datos diferentes, procesando datos de muchas fuentes y sirviendo a cientos de usuarios y aplicaciones.
- Conectores (manejador de alimentación) a las fuentes de datos. Estos procesos usualmente usan bibliotecas externas que se cargan en Q. La interfaz C en Q es extremadamente simple y le permite crear fácilmente funciones proxy para cualquier biblioteca C / C ++. Q es lo suficientemente rápido como para manejar, por ejemplo, el procesamiento simultáneo del flujo de mensajes FIX de todas las bolsas de valores europeas.
- Distribuidores de tickerplant, que sirven como un enlace intermedio entre los conectores y los consumidores. Al mismo tiempo, escriben datos entrantes en un registro binario especial, proporcionando resistencia a los consumidores a la pérdida de conexión o reinicio.
- Base de datos en memoria (rdb). Estas bases de datos proporcionan el acceso más rápido a datos sin procesar y frescos, almacenándolos en la memoria. Como regla, acumulan datos en tablas durante el día y los ponen a cero por la noche.
- Base de datos persistente (pdb). Estas bases de datos proporcionan almacenamiento de datos hoy en la base de datos histórica. Como regla, a diferencia de rdb, no almacenan datos en la memoria, sino que usan un caché especial en el disco durante un día y copian los datos a medianoche en la base de datos histórica.
- Bases históricas (hdb). Estas bases de datos proporcionan acceso a datos de días, meses y años anteriores. Su tamaño (en días) está limitado solo por el tamaño de los discos duros. Los datos se pueden ubicar en cualquier lugar, en particular en diferentes discos para un acceso más rápido. Es posible comprimir datos usando varios algoritmos para elegir. La estructura de la base de datos está bien documentada y es simple, los datos se almacenan por unidad en archivos ordinarios, para que puedan procesarse, incluido el uso del sistema operativo.
- Bases de datos con información agregada. Se almacenan varias agregaciones, generalmente agrupadas por nombre de instrumento e intervalo de tiempo. Las bases de datos en memoria actualizan su estado con cada mensaje entrante, y las históricas almacenan datos precalculados para acelerar el acceso a los datos históricos.
- Finalmente, los procesos de puerta de enlace al servicio de aplicaciones y usuarios. Q le permite implementar un procesamiento completamente asíncrono de los mensajes entrantes, distribuirlos entre las bases de datos, verificar los derechos de acceso, etc. Observo que los mensajes no se limitan y, a menudo, no son declaraciones SQL, como es el caso en otras bases de datos. Muy a menudo, la expresión SQL se oculta en una función especial y se construye en función de los parámetros solicitados por el usuario: el tiempo se convierte, se filtra, los datos se normalizan (por ejemplo, el precio de las acciones se iguala si se pagan dividendos), etc.
Arquitectura típica para un tipo de datos:

Velocidad
Aunque Q es un lenguaje interpretado, es simultáneamente un lenguaje vectorial. Esto significa que muchas funciones integradas, en particular la aritmética, aceptan argumentos de cualquier forma: números, vectores, matrices, listas, y se espera que el programador implemente el programa como operaciones en matrices. En dicho lenguaje, si agrega dos vectores en un millón de elementos, ya no importa que se interprete el lenguaje, la adición se realizará mediante una función binaria superoptimizada. Dado que la mayor parte del tiempo en los programas Q se dedica a operaciones con tablas que utilizan estas funciones vectorizadas básicas, tenemos una velocidad de salida muy decente que nos permite procesar una gran cantidad de datos incluso en un solo proceso. Esto es similar a las bibliotecas de matemáticas en Python: aunque Python es un lenguaje muy lento, tiene muchas bibliotecas geniales que le permiten procesar datos numéricos a la velocidad de un lenguaje compilado (por cierto, numpy es ideológicamente cercano a Q).
Además, KX abordó con mucho cuidado el diseño de tablas y optimizó el trabajo con ellas. En primer lugar, se admiten varios tipos de índices, que son compatibles con las funciones integradas y se pueden aplicar no solo a las columnas de la tabla, sino también a cualquier vector: agrupación, clasificación, atributo de unicidad y agrupación especial para bases de datos históricas. El índice se superpone de manera elemental y se ajusta automáticamente al agregar elementos a la columna / vector. Los índices pueden superponerse igualmente bien a las columnas de la tabla tanto en la memoria como en el disco. Al ejecutar una consulta QSQL, los índices se usan automáticamente, si es posible. En segundo lugar, el trabajo con datos históricos se realiza a través del mecanismo de asignación de archivos del sistema operativo (mapa de memoria). Las tablas grandes nunca se cargan en la memoria, en cambio, las columnas necesarias se asignan directamente a la memoria y solo esa parte de ellas se carga realmente (los índices también ayudan aquí). No hay diferencia para el programador si los datos están en la memoria o no, el mecanismo para trabajar con mmap está completamente oculto en las entrañas de Q.
KDB + no es una base de datos relacional, las tablas pueden contener datos arbitrarios, mientras que el orden de las filas en la tabla no cambia cuando se agregan nuevos elementos y puede y debe usarse al escribir consultas. Esta característica se necesita con urgencia para trabajar con series de tiempo (datos de intercambios, telemetría, registros de eventos), porque si los datos se ordenan por tiempo, el usuario no necesita usar ningún truco de SQL para encontrar la primera o la última fila o N filas en la tabla , determine qué línea sigue a la enésima línea, etc. Las uniones de tablas se simplifican aún más, por ejemplo, encontrar 16,000 transacciones VOD.L (Vodafone) la última cotización en una tabla de 500 millones de elementos toma aproximadamente un segundo en el disco y una docena de milisegundos en la memoria.
Un ejemplo de una unión de tiempo es que la tabla de cotizaciones está asignada a la memoria, por lo que no es necesario especificar VOD.L en donde, el índice en la columna sym se usa implícitamente y el hecho de que los datos están ordenados por tiempo. Casi todas las uniones en Q son funciones ordinarias, no forman parte de la instrucción select:
1. aj[`sym`time;select from trade where date=2019.03.26, sym=`VOD.L;select from quote where date=2019.03.26]
Finalmente, vale la pena señalar que los ingenieros de KX, comenzando con el propio Arthur Whitney, están realmente obsesionados con la eficiencia y están haciendo todo lo posible para aprovechar al máximo las características Q estándar y optimizar los patrones de uso más comunes.
Resumen
KDB + es popular entre las empresas principalmente debido a su versatilidad excepcional: sirve igualmente bien como una base en memoria y como una base para almacenar terabytes de datos históricos y como una plataforma para el análisis de datos. Debido al hecho de que el procesamiento de datos ocurre directamente en la base de datos, se logra una alta velocidad de operación y ahorro de recursos. Un lenguaje de programación completo, integrado con las funciones de la base de datos, le permite implementar en la misma plataforma toda la pila de procesos necesarios, desde recibir datos hasta procesar las solicitudes de los usuarios.
Información adicional
Desventajas
Un inconveniente significativo de KDB + / Q es su alto umbral de entrada. El lenguaje tiene una sintaxis extraña, algunas funciones están muy sobrecargadas (el valor, por ejemplo, tiene aproximadamente 11 casos de uso). Lo más importante, requiere un enfoque radicalmente diferente para escribir programas. En un lenguaje vectorial, debe pensar todo el tiempo en términos de transformaciones de matriz, implementar todos los ciclos a través de varias opciones de funciones de mapa / reducción (llamadas adverbios en Q), nunca intente ahorrar dinero reemplazando las operaciones vectoriales por otras atómicas. Por ejemplo, para encontrar el índice de la enésima aparición de un elemento en una matriz, escriba:
1. (where element=vector)[N]
aunque esto parece terriblemente ineficiente para los estándares C / Java (= crea un vector booleano, donde devuelve los índices de los elementos verdaderos en él). Pero tal registro hace que el significado de la expresión sea más comprensible y utiliza operaciones rápidas de vectores en lugar de operaciones atómicas lentas. La diferencia conceptual entre el lenguaje vectorial y el resto es comparable a la diferencia entre los enfoques imperativos y funcionales de la programación, y debe estar preparado para esto.
Algunos usuarios tampoco están contentos con QSQL. El hecho es que solo parece SQL real. De hecho, es solo un intérprete de expresiones similares a SQL que no admite la optimización de consultas. El usuario mismo debe escribir las consultas óptimas, y en Q, para las cuales muchas no están listas. Por otro lado, por supuesto, siempre puede escribir su propia consulta óptima usted mismo, y no confiar en un optimizador de caja negra.
Además, un libro sobre Q - Q For Mortals está disponible de forma gratuita en
el sitio web de la compañía , y también hay muchos otros materiales útiles.
Otro gran inconveniente es el costo de la licencia. Esto es decenas de miles de dólares por año para una CPU. Solo las grandes empresas pueden pagar tales gastos. Recientemente, KX ha flexibilizado la política de licencias y ofrece la posibilidad de pagar solo por el tiempo de uso o alquilar KDB + en las nubes de Google y Amazon. KX también ofrece descargar una
versión gratuita para fines no comerciales (versión de 32 bits o 64 bits a pedido).
Competidores
Existen bastantes bases de datos especializadas basadas en principios similares: columnas, en memoria, centradas en grandes cantidades de datos. El problema es que estas son bases de datos especializadas. Un buen ejemplo es Clickhouse. Esta base de datos tiene un principio muy similar al principio KDB + de almacenar datos en el disco y construir el índice; realiza algunas consultas más rápido que KDB +, aunque no significativamente. Pero a pesar de que la base de datos de Clickhouse es más especializada que KDB +: análisis web versus series de tiempo arbitrarias (esta diferencia es muy importante, por eso, por ejemplo, no hay forma de utilizar el registro de pedidos en Clickhouse). Pero, lo más importante, Clickhouse no tiene la universalidad de KDB +, un lenguaje que le permitiría procesar datos directamente en la base de datos, en lugar de cargarlos previamente en una aplicación separada, construir expresiones SQL arbitrarias, usar funciones arbitrarias en una consulta, crear procesos no relacionados con la ejecución de funciones de la base histórica . Por lo tanto, es difícil comparar KDB + con otras bases de datos, pueden ser mejores en casos de uso separados o simplemente mejores si estamos hablando de las tareas de las bases de datos clásicas, pero no conozco otra herramienta igualmente efectiva y universal para procesar datos temporales.
Integración Python
Para hacer que KDB + sea más fácil para las personas nuevas en tecnología, KX ha creado bibliotecas para una estrecha integración con Python en un solo proceso. Puede llamar a cualquier función de Python desde Q, o viceversa: llame a cualquier función de Q desde Python (en particular, expresiones QSQL). Las bibliotecas convierten, si es necesario (en aras de la eficiencia, no siempre) los datos del formato de un idioma al formato de otro. Como resultado, Q y Python viven en una simbiosis tan cercana que los límites entre ellos se borran. Como resultado, un programador, por un lado, tiene acceso completo a numerosas bibliotecas Python útiles, por otro lado, obtiene una base rápida para trabajar con grandes datos integrados en Python, lo cual es especialmente útil para aquellos involucrados en el aprendizaje automático o el modelado.
Trabajando con Q en Python:
1. >>> q() 2.q)trade:([]date:();sym:();qty:()) 3. q)\ 4. >>> q.insert('trade', (date(2006,10,6), 'IBM', 200)) 5. k(',0') 6. >>> q.insert('trade', (date(2006,10,6), 'MSFT', 100)) 7. k(',1')
Referencias
Sitio web de la empresa:
https://kx.com/Sitio web para desarrolladores:
https://code.kx.com/v2/Libro Q For Mortals (en inglés):
https://code.kx.com/q4m3/Artículos sobre el tema de las aplicaciones KDB + / Q de los empleados de kx:
https://code.kx.com/v2/wp/