82 votos

Almacenamiento eficiente de datos intradiarios en tiempo real de forma agnóstica a la aplicación

¿Cuál sería el mejor enfoque para manejar el almacenamiento de datos intradiarios en tiempo real?

Para mi investigación personal siempre he importado solo desde archivos planos en memoria (EOD histórico), así que no tengo mucha experiencia con esto. Actualmente estoy trabajando en un proyecto secundario, que requeriría comillas diarias de acciones actualizadas cada minuto desde una fuente externa. Por el momento, supongo que cualquier solución de base de datos popular debería poder manejarlo sin sudar demasiado en este escenario. Pero me gustaría que la solución adoptada escale fácilmente cuando se necesiten ticks en tiempo real.

Un problema similar ha sido mencionado por Marko, aunque fue principalmente específico de R. Estoy buscando un almacenamiento de datos universal accesible tanto para interfaces web ligeras (PHP/Ruby/Flex) como para la parte analítica de atrás (C++, R o Python, aún no lo sé).

Según lo que chrisaycock mencionó, las bases de datos orientadas a columnas deberían ser la solución más viable. Y parece ser el caso.

Pero no estoy seguro de entender todas las complejidades del almacenamiento orientado a columnas en algunos escenarios de uso ejemplares:

  • Obtener todos o un subconjunto de datos de precios para un ticker específico para trazar en la interfaz gráfica
    • En comparación con las soluciones basadas en filas, la obtención de datos de precios debería ser más rápida porque es una lectura secuencial. Pero ¿cómo influye en esto almacenar múltiples tickers en un solo lugar? Por ejemplo, una declaración como "seleccionar todas las marcas de tiempo y datos de precios donde el ticker sea igual a algo". ¿No tengo que comparar el ticker en cada fila que obtengo? Y en la situación en la que tengo que proporcionar datos completos para alguna aplicación de interfaz gráfica, ¿no sería más eficiente servir un archivo plano crudo para el instrumento solicitado?
  • Análisis realizado en la parte de atrás
    • Cosas como calcular valores únicos para una acción (por ejemplo, varianza, rendimiento de los últimos x días) y series temporales dependientes (rendimientos diarios, indicadores técnicos, etc.). La obtención de datos de entrada para los cálculos debería ser más eficiente que en el caso anterior, pero ¿qué hay de la escritura? La ganancia que veo es escribir en masa el resultado final (como el valor del indicador calculado para cada marca de tiempo), pero aún así no sé cómo maneja la base de datos mi mezcla de diferentes tickers en una tabla. ¿La partición horizontal/sharding lo maneja automáticamente por mí o es mejor dividir manualmente en una estructura de tabla por instrumento (lo cual parece innecesariamente engorroso)?
  • Actualizar la base de datos con nuevos ticks entrantes
    • ¿No sería más eficiente aquí usar orientación basada en filas, verdad? Y lo mismo ocurre con la actualización de datos agregados (por ejemplo, tablas diarias OHLC). ¿No sería un posible cuello de botella?

Todo esto está en el contexto de las soluciones a código abierto disponibles. Inicialmente pensé en InfiniDB o HBase, pero también he visto mencionados aquí MonetDB y InfoBright. Realmente no necesito una "calidad de producción" (al menos por ahora) como mencionó chrisaycock en la pregunta referida, ¿así que sería alguna de estas una mejor elección que las demás?

Y el último problema: ¿a partir de qué punto de carga son necesarias las bases de datos de series temporales especializadas? Desafortunadamente, cosas como kdb+ o FAME están fuera de alcance en este caso, así que estoy contemplando cuánto se puede hacer en hardware estándar con bases de datos relacionales (como MySQL/PostgreSQL) o almacenes de clave-valor (como Tokyo/Kyoto Cabinet's B+ tree) - ¿es realmente un callejón sin salida? ¿Debería quedarme simplemente con algunas de las soluciones mencionadas orientadas a columnas debido a que mi aplicación no es crítica o incluso eso es una precaución innecesaria?

Gracias de antemano por tu aporte a esto. Si alguna parte es demasiado confusa, házmelo saber en un comentario. Intentaré enmendarlo en consecuencia.

EDICIÓN:

Parece que estrictamente hablando HBase no es una tienda orientada a columnas sino más bien un mapa ordenado multidimensional esparcido, distribuido, persistente, por lo que lo he tachado de la pregunta original.

Después de algunas investigaciones, estoy más inclinado hacia InfiniDB. Tiene todas las características que necesito, admite SQL (se pueden usar conectores/envoltorios estándar de MySQL para el acceso) y el subconjunto completo de DML. Lo único que falta en la edición de código abierto es la compresión sobre la marcha y la escalabilidad a clústeres. Pero supongo que sigue siendo un buen negocio por el precio, considerando que es gratis.

2 votos

Por cierto, encontré una buena introducción al tema (www8.cs.umu.se/education/examina/Rapporter/JohanJonsson2009‌​.pdf) que puede resultar útil para futuros lectores de esta pregunta.

52voto

Greg Hurlman Puntos 10944

El almacenamiento orientado por columnas es más rápido para leer debido a la eficiencia de la caché. Al ver tu consulta de ejemplo:

select price, time from data where symbol = `AAPL

Aquí estoy interesado en tres columnas: price, time, y symbol. Si todos los ticks se almacenaran por fila, la base de datos tendría que leer todas las filas solo para buscar los símbolos. Se vería así en disco:

IBM | 09:30:01 | 164.05; IBM | 09:30:02 | 164.02; AAPL | 09:30:02 | 336.85

Por lo tanto, el software debe omitir las entradas de precio y tiempo solo para leer los símbolos. ¡Esto provocaría una falta de caché por cada tick!

Ahora veamos el almacenamiento orientado por columnas:

IBM | IBM | AAPL; 09:30:01 | 09:30:02 | 09:30:02; 164.05 | 164.02 | 336.85

Aquí la base de datos puede escanear secuencialmente la lista de símbolos. Esto es eficiente en caché. Una vez que el software tiene los índices de matriz que representan las ubicaciones de símbolos de interés, la base de datos puede saltar a las entradas específicas de tiempo y precio a través de un acceso aleatorio. (Puede notar que las columnas son en realidad arreglos asociativos; el primer elemento en cada columna se refiere a la primera fila en aggregate, por lo tanto, saltar a la fila N significa simplemente acceder al elemento N en cada array.)

Como puedes imaginar, el almacenamiento orientado por columnas brilla realmente durante el análisis. Para calcular el promedio móvil de los precios por símbolo, la base de datos indexará y ordenará la columna de símbolos para determinar el orden adecuado de las entradas de precio, y luego comenzará el cálculo con los precios en un diseño contiguo (secuencial). Nuevamente, eficiente en caché.


Más allá del diseño orientado por columnas, muchas de estas bases de datos realmente nuevas también almacenan todo en memoria al realizar cálculos. Es decir, si el conjunto de datos es lo suficientemente pequeño, el software leerá toda el historial de ticks en memoria, lo que eliminará las fallas de página al ejecutar consultas. ¡Por lo tanto, nunca accederá al disco!

Una segunda optimización que hace kdb+ es que automáticamente enumera el texto. (Esta característica está inspirada en símbolos Lisp). Por lo tanto, buscar una acción en particular no implica la búsqueda de cadenas típica; es simplemente una búsqueda de enteros después de la búsqueda de enumeración inicial.

Con el almacenamiento secuencial, asignación en memoria y enumeración automática de texto, buscar un símbolo es simplemente escanear un entero en un array. Es por eso que una base de datos como kdb+ es unas pocas órdenes de magnitud más rápida que las bases de datos relacionales comunes para leer y analizar.


Como has señalado en tu pregunta, la escritura es una debilidad del almacenamiento orientado por columnas. Debido a que cada columna es un array (en memoria) o un archivo (en disco), cambiar una sola fila significa actualizar cada array o archivo individualmente en lugar de simplemente transmitir toda la fila a la vez. Además, añadir datos en memoria o en disco es bastante sencillo, al igual que actualizar/insertar datos en memoria, pero es prácticamente imposible actualizar/insertar datos en disco. Es decir, el usuario no puede cambiar datos históricos sin algún enorme truco.

Por esta razón, los datos históricos (almacenados en disco) a menudo se consideran solo para agregar. En la práctica, las bases de datos orientadas por columnas requieren que el usuario adopte un esquema bitemporal o de punto en el tiempo. (De todos modos, aconsejo este esquema para aplicaciones financieras tanto para un mejor análisis de series temporales como para informes de cumplimiento adecuados.)


No sé lo suficiente sobre tu aplicación para determinar los requisitos de rendimiento o de nivel de producción. Solo espero que la guía anterior te ayude a tomar una decisión informada sobre por qué el almacenamiento orientado por columnas suele ser tu mejor opción para análisis.

0 votos

Gracias por una respuesta detallada. Eso es exactamente lo que estoy tratando de hacer, aprender más para saber cuáles son los pros y los contras de las opciones disponibles. Sin embargo, no estoy seguro de haber entendido la sección penúltima. Mencionas que las operaciones de actualización en disco son prácticamente imposibles. ¿Te refieres desde un punto de vista de rendimiento y no de limitaciones de implementación, ¿verdad? ¿Algo así como el peor caso siendo aproximadamente O(n)? Y sobre bitemporal, si no hay requisitos de cumplimiento, ¿no es exagerado inflar artificialmente la cantidad de datos si, por ejemplo, todo lo que necesito es un ajuste de precio de cierre para divisiones de acciones?

0 votos

@Karol Las actualizaciones en disco son imposibles desde un punto de vista de implementación. Las columnas se almacenan como archivos (cada columna es su propio archivo de datos secuenciales, por lo que se puede leer en la memoria como una matriz). Por lo tanto, cualquier actualización (no de apéndice) requiere reescribir todo el archivo; este es exactamente el proceso que hace tu editor de texto cada vez que cambias un poco de código. Eso significa que necesitarás restablecer cualquier proceso de lectura que pueda haber asignado en memoria la columna antes de la actualización para garantizar la consistencia (según el Teorema CAP). Kdb+ no admite el restablecimiento de lectores de forma nativa; un producto competidor podría hacerlo, pero sigue siendo un proceso desagradable.

2 votos

En cuanto al bitemporal, siéntase libre de reescribir toda su tabla OHLC si lo desea, aunque realmente solo necesitaría agregar los datos del mercado del último día de todos modos, ya que los precios sin procesar no cambian. Si su preocupación son los precios ajustados (es decir, acciones corporativas), creo que solo debería almacenar datos sin procesar y luego precalcular los ajustados sobre la marcha. Para hacer esto, simplemente almacene divisiones, dividendos y cambios de nombre en una tabla separada, luego proporcione una función a nivel de usuario para materializar los datos ajustados.

30voto

PaulStock Puntos 4753

He anhelado durante mucho tiempo la solución definitiva, súper rápida y escalable para almacenamiento de datos. He utilizado bases de datos relacionales, kdb, archivos planos y archivos binarios. Al final, utilicé archivos binarios en mi lenguaje de investigación preferido. Mi consejo es KISS. La elección del almacenamiento en realidad no es tan crítica (a menos que tal vez estés trabajando con datos de ticks de opciones). Lo crítico es cómo decides segmentar los datos.

Si miras kdb, en realidad puede ser bastante lento si no divides (separas) los datos según tu necesidad específica. Simplemente te proporciona una capa de gestión rápida, pero depende de ti diseñar el almacenamiento de datos en disco para tus necesidades. Lo que estás intentando hacer es almacenar los datos de tal manera que agrupes juntos los datos que necesitas y minimices la cantidad de datos adicionales que deben ser leídos desde el disco.

Para mí, encontré que almacenar los datos en formato binario en el lenguaje en el que investigo tiene la menor cantidad de sobrecarga. Gestionar una splay sencilla es fácil. Una clave es no tener miedo de almacenar múltiples copias de tus datos para diferentes tareas de investigación, siempre y cuando la creación de las copias se base en una única fuente principal. Así que, por ejemplo, si necesitas con frecuencia todos los ticks para una acción durante los últimos 5 años, entonces segmentaría por acción. Pero si también necesitas todas las acciones para un día dado, entonces almacenaría otro conjunto de datos que se divida por día. Procesa y almacena los datos de una manera que te resulte más útil.

Si eres una gran institución, entonces por todos los medios gasta el gran dinero para obtener kdb y contrata a un programador q genial (porque probablemente no vas a entenderlo fácilmente por tu cuenta). Es bastante bueno. Pero, si eres un individuo, haz lo simple y sigue adelante con trabajos más interesantes.

1 votos

¿Te refieres a "partición"? En el lenguaje de kdb, "splay" es simplemente dividir las columnas en archivos separados. La partición es cuando esas columnas se dividen aún más por una clave, como fecha o símbolo.

2 votos

Sí, tienes razón. Estoy siendo impreciso con mi terminología. Creo que la palabra splay es genial.

3 votos

"Una clave es no tener miedo de almacenar múltiples copias de tus datos para diferentes tareas de investigación" - gracias, es un punto válido que mencionas. Aunque al principio pueda sentirme un poco inseguro/a acerca de esto, puede ser una solución realmente útil y directa en ciertas situaciones. Un problema que veo es mantener sincronizados esos conjuntos de datos derivados con las actualizaciones recientes para que presenten el mismo estado. Sin embargo, mis preocupaciones son teóricas, tendría que verificarlo en algún contexto práctico.

16voto

lomaxx Puntos 32540

Personalmente, hago una distinción entre dos objetivos conflictivos: (1) almacenar datos entrantes en tiempo real para su procesamiento inmediato y (2) almacenar los datos recopilados para fines "offline". Tal enfoque facilita mucho las cosas si estamos hablando de una solución casera.

(1) debe ser lo más rápido posible, pero no necesariamente escalable más allá de unas pocas decenas de millones de ticks (recuerda que todavía estamos hablando de una solución casera y no de un sistema completo para un banco de inversiones). (2) no debe ser lento y debe poder escalar bien para contener miles de millones de ticks.

La solución más simple y rápida para (1) son los arrays guardados en la memoria. Al final del día de operaciones, simplemente los pondrás todos en (2). Tienen una desventaja: en caso de un fallo, tus datos se pierden. Si eso te preocupa, entonces debes reemplazarlos (o respaldarlos) con una base de datos. Cualquier base de datos no juguete fácilmente alojará unos cuantos millones de registros.

(2) es más exigente: no puedes simplemente poner miles de millones de ticks en una base de datos de inmediato. E incluso si lo haces, el rendimiento de recuperación será pésimo. Necesitas dividir tus datos de una manera que sirva mejor para tus propósitos; no hay un único remedio universal aquí y lo que funciona para otros puede que no funcione tan bien para ti.

Personalmente, almaceno los datos recopilados de mi fuente en tiempo real en RAM. En realidad, solo los almaceno con fines de visualización. Para el verdadero negocio, los coloco en mi subsistema de CEP. Después de que termina la sesión, descargo todos los ticks a través de mi cuenta de corretaje y los pongo en (2).

En mi solución, los datos históricos de los ticks se almacenan en una base de datos. La uso únicamente como un motor de almacenamiento, ya que alcancé los límites de un sistema de archivos (NTFS en este caso) después de guardar aproximadamente 20 millones de archivos. Mi base de datos tiene una tabla grande que contiene todos los datos como BLOBs. Cada BLOB corresponde a comillas de un solo valor para un día dado. Por supuesto, este enfoque tiene sus desventajas, pero hay dos ventajas importantes: fue fácil de desarrollar y es capaz de alojar grandes cantidades de datos sin ninguna caída en el rendimiento.

2 votos

Puedes tener un proceso de "spool" que se suscribe a tu planta de datos y cuyo único propósito es registrar los ticks cada pocos segundos en un almacenamiento no volátil. Eso se encargará del (2) sin preocuparte por accidentes o escrituras masivas. En cuanto al (1), supongo que el proceso en memoria también es un suscriptor de tu planta de datos, posiblemente a través de tu CEP. Tus procesos (1) y (2) deberían ser ambos suscriptores, siendo el primero para análisis en tiempo real y el segundo una simple configuración de "spooling".

1 votos

@chrisaycock "Puedes tener un proceso de "spool" que se suscriba a tu planta de tickers y cuyo único propósito sea registrar ticks cada pocos segundos en almacenamiento no volátil. Eso se encargará de (2)" Ten en cuenta que mi solución para (2) no está diseñada "para registrar ticks cada pocos segundos": su arquitectura (series como BLOBs) hace que hacer eso sea ineficaz, especialmente a medida que avanza el día - no es posible agregar datos a BLOBs sin reescribirlos. Por eso necesito (1) como un subsistema separado.

0 votos

Ah, entendido. No me di cuenta de las limitaciones del BLOB.

11voto

Harley Holcombe Puntos 34618

Llevo usando FastBit por un tiempo y lo encuentro bastante eficiente. Es muy poco intrusivo para el formato de almacenamiento binario existente siempre que tus datos estén almacenados de manera columnar.

Probé brevemente Tokyo/KyotoCabinet y no lo encontré adecuado para mis requerimientos de almacenamiento persistente.

3 votos

Para los no iniciados, FastBit es una tienda de datos orientada a columnas.

0 votos

Por curiosidad, ¿qué tipo de consultas estás realizando en tu almacén de datos usando FastBit? ¿Solo temporales? ¿También consultas basadas en precio, etc?

5voto

Andrew Grant Puntos 35305

Me he convertido en un fan de SQLite. Es una base de datos SQL muy ligera, que se puede usar como solución intermedia. Estoy de acuerdo con Rich C en que lo mejor probablemente sea crear una solución personalizada que sea óptima para tus necesidades. Utilizar SQLite como almacenamiento persistente y cargar los datos en memoria cuando quieras realizar cálculos intensivos en ellos parece ser un enfoque que combina lo mejor de ambos mundos.

0 votos

Varios lectores y un escritor a la vez, definitivamente funciona si tienes un proceso dedicado para escribir mientras millones de personas quieren leer

Finanhelp.com

FinanHelp es una comunidad para personas con conocimientos de economía y finanzas, o quiere aprender. Puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X