Productos secos 丨 Tutorial de importación de datos DolphinDB para la base de datos de series de tiempo

Cuando las empresas utilizan plataformas de análisis de big data, primero deben migrar cantidades masivas de datos desde múltiples fuentes de datos a la plataforma de big data.


Antes de importar datos, necesitamos comprender los conceptos y características básicos de la base de datos DolphinDB.

Las tablas de datos de DolphinDB se dividen en 3 tipos según el medio de almacenamiento:

  • Tabla de memoria: los datos solo se almacenan en la memoria de este nodo y la velocidad de acceso es la más rápida, pero los datos se perderán después de que se apague el nodo.
  • Tabla de disco local: los datos se guardan en el disco local. Incluso si se reinicia el nodo, los datos se pueden cargar fácilmente en la memoria a través de un script.
  • Tabla distribuida: Los datos se distribuyen físicamente en diferentes nodos, a través del motor de computación distribuida de DolphinDB, lógicamente, todavía es posible realizar consultas unificadas como tablas locales.

Las tablas de datos de DolphinDB se dividen en 2 tipos según estén particionadas:

  • Mesa ordinaria
  • Tabla de particiones

En las bases de datos tradicionales, la partición es para tablas de datos, es decir, cada tabla de datos en la misma base de datos puede tener diferentes esquemas de partición; mientras que la partición de DolphinDB es para bases de datos, es decir, una base de datos solo puede usar un esquema de partición. Si los esquemas de partición de las dos tablas son diferentes, no se pueden colocar en la misma base de datos.


DolphinDB proporciona 3 métodos flexibles de importación de datos:

  • Importar mediante archivo de texto CSV
  • Importar a través de un archivo HDF5
  • Importar a través de ODBC


1. Importar mediante archivo de texto CSV

La transferencia de datos a través de archivos CSV es una forma más común de migración de datos. DolphinDB proporciona tres funciones loadText , ploadText y loadTextEx para importar archivos CSV. A continuación, utilizamos un archivo CSV de muestra Candle_201801.csv para ilustrar el uso de estas tres funciones.

1.1 loadText

Sintaxis: loadText (nombre de archivo, [delimitador = ','], [esquema])

parámetro:

filename es el nombre del archivo.

Tanto el delimitador como el esquema son parámetros opcionales.

delimitador se utiliza para especificar el delimitador de diferentes campos, el valor predeterminado es ",".

El esquema se utiliza para el tipo de datos de cada campo después de que se importan los datos, es un tipo de tabla. DolphinDB proporciona la función de reconocimiento automático de tipo de campo, pero en algunos casos el tipo de datos reconocido automáticamente por el sistema no cumple con los requisitos. Por ejemplo, cuando importamos el CSV de muestra Candle_201801.csv , el campo de volumen será reconocido como el tipo INT. De hecho, necesitamos el tipo LONG. , Entonces necesitas usar el parámetro de esquema.

Script para crear tabla de esquema:

nameCol = `symbol`exchange`cycle`tradingDay`date`time`open`high`low`close`volume`turnover`unixTime 
typeCol = [ SYMBOL, SYMBOL, INT, DATE, DATE, INT, DOUBLE, DOUBLE, DOUBLE, DOUBLE , INT, DOUBLE, LONG] 
schemaTb = table (nameCol como nombre, typeCol como tipo)

Cuando la tabla tiene muchos campos, el script para crear la tabla de esquema será muy detallado. Para evitar este problema, DolphinDB proporciona la función extractTextSchema, que puede extraer la estructura de la tabla del archivo de texto, solo necesitamos modificar el tipo de campo que se debe especificar.

dataFilePath = "/home/data/candle_201801.csv" 
schemaTb = extractTextSchema (dataFilePath) 
actualizar schemaTb set type = `LONG donde name =` volume         
tt = loadText (dataFilePath ,, schemaTb)


1.2 ploadText

ploadText carga archivos de datos en la memoria en paralelo como una tabla de particiones. La sintaxis es exactamente la misma que loadText, pero ploadText es más rápido. ploadText se utiliza principalmente para cargar archivos grandes rápidamente. Está diseñado para hacer un uso completo de múltiples núcleos para cargar archivos en paralelo. El grado de paralelismo depende del número de núcleos del servidor en sí y de la configuración localExecutors del nodo.

A continuación, comparamos el rendimiento de loadText y ploadText.

Primero, genere un archivo CSV 4G a través del script:

filePath = "/home/data/testFile.csv" 
appendRows = 100000000 
dateRange = 2010.01.01..2018.12.30 ints = 
rand (100, appendRows) 
símbolos = take (string ('A' .. 'Z'), appendRows ) 
date = take (dateRange, appendRows) 
floats = rand (float (100), appendRows) 
times = 00: 00: 00.000 + rand (86400000, appendRows) 
t = table (ints como int, símbolos como símbolo, fechas como fecha, flota como flotante, veces como tiempo) 
t.saveText (filePath)

Utilice loadText y ploadText para importar archivos, respectivamente. Este nodo es una CPU de 4 núcleos y 8 hilos.

temporizador loadText (filePath); 
// Tiempo transcurrido: 39728,393 ms 
temporizador ploadText (filePath); 
// Tiempo transcurrido: 10685.838 ms

Los resultados muestran que el rendimiento de ploadText es casi 4 veces mayor que el de loadText.


1.3 loadTextEx

语法 : loadTextEx (dbHandle, tableName, [partitionColumns], fileName, [delimiter = ','], [schema])

parámetro:

dbHandle es el identificador de la base de datos.

tableName es el nombre de la tabla distribuida que contiene los datos.

Partición Columnas , delimitador y esquema son parámetros opcionales.

Cuando el esquema de partición de la partición no es secuencial, partitionColumns necesitan especificarse para indicar las columnas de partición.

fileName representa el nombre del archivo importado.

delimitador se utiliza para especificar el delimitador de diferentes campos, el valor predeterminado es ",".

El esquema se utiliza para el tipo de datos de cada campo después de que se importan los datos, es un tipo de tabla.

La función loadText siempre importa datos a la memoria.Cuando el archivo de datos es muy grande, la memoria de la máquina en funcionamiento puede convertirse fácilmente en un cuello de botella. loadTextEx puede resolver bien este problema. Guarda el archivo CSV estático como una tabla distribuida de DolphinDB en un modo de flujo de datos relativamente fluido al importar y guardar, en lugar de importar toda la memoria y luego guardarla como El método de la tabla de particiones reduce en gran medida los requisitos de uso de memoria.

Primero cree una tabla distribuida para almacenar datos:

dataFilePath = "/home/data/candle_201801.csv" 
tb = loadText (dataFilePath) 
db = database ("dfs: // dataImportCSVDB", VALUE, 2018.01.01..2018.01.31)   
db.createPartitionedTable (tb, "ciclo" , "tradingDay")

Luego importe el archivo a la tabla distribuida:

loadTextEx (db, "ciclo", "tradingDay", dataFilePath)

Cuando es necesario utilizar datos para el análisis, los metadatos de la partición se cargan primero en la memoria a través de la función loadTable. Cuando la consulta se ejecuta realmente, DolphinDB cargará los datos en la memoria a pedido.

tb = database ("dfs: // dataImportCSVDB") .loadTable ("ciclo")


2. Importar mediante archivo HDF5

HDF5 es un formato de archivo de datos binarios que es más eficiente que CSV y se usa ampliamente en el campo del análisis de datos. DolphinDB también admite la importación de datos a través de archivos de formato HDF5.

DolphinDB utiliza el complemento HDF5 para acceder a los archivos HDF5. El complemento proporciona los siguientes métodos:

  • hdf5 :: ls: Lista todos los objetos Grupo y Conjunto de datos en el archivo h5.
  • hdf5 :: lsTable: enumera todos los objetos del conjunto de datos en el archivo h5.
  • hdf5 :: hdf5DS: Devuelve los metadatos del conjunto de datos en el archivo h5.
  • hdf5 :: loadHdf5: Importa el archivo h5 a la tabla de memoria.
  • hdf5 :: loadHdf5Ex: Importe el archivo h5 a la tabla de particiones.
  • hdf5 :: extractHdf5Schema: Extrae la estructura de la tabla del archivo h5.

Al llamar a un método de complemento, debe proporcionar un espacio de nombres delante del método, como hdf5 :: loadHdf5 al llamar a loadHdf5. Si no desea usar el espacio de nombres cada vez que lo llame, puede usar la palabra clave use:

use hdf5 
loadHdf5 (filePath, tableName)

Para usar el complemento DolphinDB, primero debe descargar el complemento HDF5 y luego implementar el complemento en el directorio de complementos del nodo. Cargue el complemento antes de usarlo. Utilice la siguiente secuencia de comandos:

loadPlugin ("complementos / hdf5 / PluginHdf5.txt")

La importación de archivos HDF5 es similar a los archivos CSV. Por ejemplo, queremos importar el archivo HDF5 de muestra Candle_201801.h5 , que contiene un Conjunto de datos: Candle_201801, luego el método de importación más simple es el siguiente:

dataFilePath = "/home/data/candle_201801.h5" 
datasetName = "vela_201801" 
tmpTB = hdf5 :: loadHdf5 (dataFilePath, datasetName)

Si necesita especificar el tipo de datos para importar, puede usar hdf5 :: extractHdf5Schema, el script es el siguiente:

dataFilePath = "/home/data/candle_201801.h5" 
datasetName = "vela_201801" 
esquema = hdf5 :: extractHdf5Schema (dataFilePath, datasetName) 
actualizar esquema conjunto tipo = `LONG donde nombre =` volume         
tt = hdf5 :: loadHdf5 (dataFilePath, datasetName ,esquema)

Si el archivo HDF5 es muy grande y la memoria de la máquina en funcionamiento no puede soportar la carga completa, puede usar hdf5 :: loadHdf5Ex para cargar los datos.

Primero cree una tabla distribuida para almacenar datos:

dataFilePath = "/home/data/candle_201801.h5" 
datasetName = "vela_201801" 
dfsPath = "dfs: // dataImportHDF5DB" 
tb = hdf5 :: loadHdf5 (dataFilePath, datasetName) 
db = base de datos (dfsPath, VALUE, 2018.01.01 .. 2018.01.31)   
db.createPartitionedTable (tb, "ciclo", "tradingDay")

Luego importe el archivo HDF5 a través de la función hdf5 :: loadHdf5Ex:

hdf5 :: loadHdf5Ex (base de datos, "ciclo", "día comercial", ruta de archivo de datos, nombre del conjunto de datos)


3. Importar a través de la interfaz ODBC

DolphinDB admite la interfaz ODBC para conectarse a bases de datos de terceros y leer tablas directamente desde la base de datos en tablas de datos de memoria de DolphinDB. Utilice el complemento ODBC proporcionado por DolphinDB para migrar fácilmente datos de bases de datos compatibles con ODBC a DolphinDB.

El complemento ODBC proporciona los siguientes cuatro métodos para manipular datos de fuentes de datos de terceros:

  • odbc :: connect: abre la conexión.
  • odbc :: close: cierra la conexión.
  • odbc :: query: consulta los datos de acuerdo con la instrucción SQL dada y los devuelve a la tabla de memoria de DolphinDB.
  • odbc :: execute: ejecuta la instrucción SQL dada en la base de datos de terceros sin devolver datos.

Antes de utilizar el complemento ODBC, primero debe instalar el controlador ODBC; consulte el tutorial del complemento ODBC .

Lo siguiente es para conectarse a SQL Server como ejemplo, la configuración específica de la base de datos existente es:

servidor: 172.18.0.15

Puerto predeterminado: 1433

Nombre de usuario de conexión: sa

Contraseña: 123456

Nombre de la base de datos: SZ_TAQ

La tabla de la base de datos selecciona los datos el 1 de enero de 2016, el nombre de la tabla es candle_201801 y los campos son los mismos que los del archivo CSV.

Para usar el complemento ODBC para conectarse a la base de datos de SQL Server, el primer paso es descargar el complemento, descomprimir y copiar todos los archivos en el directorio plugins \ odbc al directorio plugins / odbc de DolphinDB Server y completar la inicialización del complemento mediante el siguiente script:

// 载入 插件
loadPlugin ("plugins / odbc / odbc.cfg") 
// 连接 SQL Server 
conn = odbc :: connect ("Driver = ODBC Driver 17 para SQL Server; Server = 172.18.0.15; Database = SZ_TAQ; Uid = sa; 
Pwd = 123456; ")

Antes de importar datos, cree una base de datos de disco distribuida para guardar los datos:

// Obtener la estructura de la tabla de SQL Server como plantilla para la tabla de importación de DolphinDB 
tb = odbc :: query (conn, "select top 1 * from candle_201801") 
db = database ("dfs: // dataImportODBC", VALUE, 2018.01. 01..2018.01.31) 
db.createPartitionedTable (tb, "ciclo", "tradingDay")

Importe datos de SQL Server y guárdelos como una tabla de particiones DolphinDB:

data = odbc :: query (conn, "seleccionar * de la vela_201801") 
tb = database ("dfs: // dataImportODBC") .loadTable ("ciclo") 
tb.append! (data);

La importación de datos a través de ODBC evita el proceso de exportación e importación de archivos y, a través del mecanismo de operación de temporización de DolphinDB, también se puede utilizar como canal de datos para sincronizar los datos de temporización.


4. Caso de importación de datos financieros

El siguiente es un ejemplo de importación del archivo de datos del gráfico de líneas K diario del mercado de valores. Los datos se guardan en el disco en formato de archivo CSV. Se guarda un total de 10 años de datos según el subdirectorio anual. Un total de aproximadamente 100 G de datos. El ejemplo de ruta es el siguiente:

2008

---- 000001.csv

---- 000002.csv

---- 000003.csv

---- 000004.csv

---- ...

2009

...

2018

La estructura de cada archivo es consistente, como se muestra en la figura:

edcac7665c7b5c30c4316c15c573e93e.png


4.1 Planificación de zonificación

Antes de importar datos, primero debe planificar la partición de los datos, lo que implica dos consideraciones:

  • Determine el campo de la partición.
  • Determine la granularidad de la partición.

En primer lugar, de acuerdo con la frecuencia de ejecución de las declaraciones de consulta diarias, utilizamos dos campos de comercio y símbolo para la partición de rango combinado (RANGE). Al dividir los campos de búsqueda de uso común, la eficiencia de la recuperación y el análisis de datos se puede mejorar enormemente.

Lo siguiente que debe hacer es definir la granularidad de las dos particiones respectivamente.

El intervalo de tiempo de los datos existentes es de 2008 a 2018, por lo que los datos se dividen en el tiempo según el año. Al planificar la partición de tiempo, es necesario considerar dejar suficiente espacio para los datos entrantes posteriores, por lo que el intervalo de tiempo se establece aquí como 2008-2030.

yearRange = fecha (2008.01M + 12 * 0..22)

Aquí hay varios miles de códigos de acciones. Si los códigos de acciones están divididos por valor (VALOR), entonces cada partición tiene un tamaño de solo unos pocos megabytes y el número de particiones es grande. Cuando un sistema distribuido ejecuta una consulta, divide la instrucción de la consulta en múltiples subtareas y las distribuye a diferentes particiones para su ejecución. Por lo tanto, el método de partición por valor dará como resultado una gran cantidad de tareas y el tiempo de ejecución de la tarea es extremadamente corto, lo que hace que el sistema gaste en tareas de administración. En cambio, el tiempo es mayor que el tiempo de ejecución de la tarea en sí. Este método de partición es obviamente irrazonable. Aquí dividimos todos los códigos de acciones en 100 intervalos de acuerdo con el rango, y cada intervalo se utiliza como una partición. El tamaño final de la partición es de aproximadamente 100 M. Teniendo en cuenta que los nuevos datos de stock llegarán más tarde, se agrega un código virtual 999999 para formar una partición con el último código de stock para guardar los datos de los nuevos stocks posteriores.

Obtenga el rango de partición del campo de símbolo a través del siguiente script:

// Recorre todos los directorios anuales, ordena la lista de códigos bursátiles y divídela en 100 intervalos a través de los 
símbolos cutPoint = array (SYMBOL, 0, 100) 
yearDirs = files (rootDir) [`filename] 
for (yearDir in yearDirs) { 
	path = rootDir + "/" + yearDir 
	symbols.append! (files (path) [`filename] .upper (). strReplace (". CSV "," ")) 
} 
// Elimina la duplicación y aumenta el espacio de expansión: 
símbolos = symbols.distinct (). sort! (). append! ("999999"); 
// 
Igualmente dividido en 100 partes symRanges = symbols.cutPoints (100) 
Utilice el siguiente script para definir particiones de combinación bidimensional (COMPO), crear una base de datos y Tabla de partición: 
columnas = `símbolo`exchange`ciclo`tradingDay`date`time`open`high`low`close`volume`turnover`unixTime 
types = [ SYMBOL, SYMBOL , INT, DATE, DATE, TIME, DOUBLE, DOUBLE, DOBLE, DOBLE, LARGO, DOBLE, LARGO] 

dbDate = database ("", RANGE, yearRange)
dbID = database ("", RANGE, symRanges) 
db = database (dbPath, COMPO, [dbDate, dbID]) 

pt = db.createPartitionedTable (tabla (1000000: 0, columnas, tipos), tableName, símbolo `tradingDay`)

Cabe señalar que la partición es la unidad más pequeña de DolphinDB para almacenar datos, y DolphinDB escribe exclusivamente en la partición.Cuando las tareas se realizan en paralelo, es necesario evitar que múltiples tareas escriban datos simultáneamente en una partición. En este caso, los datos de cada año se transfieren a una tarea separada y los límites de datos de cada operación de tarea no se superponen, por lo que es imposible escribir varias tareas en la misma partición.


4.2 Importar datos

La idea principal del script de importación de datos es muy simple. Lee y escribe todos los archivos CSV uno por uno en la tabla de base de datos distribuida dfs: // SAMPLE_TRDDB haciendo un bucle en el árbol de directorios. Sin embargo, todavía hay muchos detalles en el proceso de importación específico.

El primer problema encontrado es que el formato de datos guardados en el archivo CSV es diferente del formato de datos interno de DolphinDB, como el campo de hora. En el archivo, "9390100000" indica la hora exacta en milisegundos. Si se lee directamente, se reconocerá como un tipo numérico. , En lugar del tipo de hora, aquí debe usar la función de conversión de datos datetimeParse combinada con la función de formato format para convertir cuando se importan los datos. El guión clave es el siguiente:

datetimeParse (formato (hora, "000000000"), "HHmmssSSS")

Aunque es muy sencillo de implementar mediante la importación circular, de hecho, los datos 100G se componen de una gran cantidad de archivos pequeños de aproximadamente 5M. Si la operación de un solo subproceso esperará mucho tiempo, para aprovechar al máximo los recursos del clúster, importamos y dividimos los datos según el año. Divida en múltiples subtareas y envíelas a la cola de tareas de cada nodo para ejecutarlas en paralelo para mejorar la eficiencia de la importación. Este proceso se implementa en los siguientes dos pasos:

(1) Defina una función personalizada. La función principal de la función es importar todos los archivos en el directorio anual especificado:

// 循环 处理 年度 目录 下 的 所有 数据 文件
def loadCsvFromYearPath (ruta, dbPath, tableName) { 
	símbolos = archivos (ruta) [`nombre de archivo] 
	para (símbolo en símbolos) { 
		filePath = ruta +" / "+ sym 
		t = loadText (filePath) 
		database (dbPath) .loadTable (tableName) .append! (seleccione símbolo, intercambio, ciclo, tradingDay, date, datetimeParse (format (time, "000000000"), "HHmmssSSS"), open, high, low, close , volumen, facturación, unixTime de t)			 
	} 
}

(2) Envíe la función definida anteriormente a cada nodo para su ejecución a través de la función rpc combinada con la función submitJob:

nodesAlias ​​= "NODE" + cadena (1..4) 
años = archivos (rootDir) [`nombre de archivo] 

índice = 0; 
para (año en años) {	 
	yearPath = rootDir + "/" + año 
	des = "loadCsv_" + año 
	rpc (nodesAlias ​​[index% nodesAlias.size ()], submitJob, des, des, loadCsvFromYearPath, yearPath, dbPath, tableName) 
	índice = índice + 1 
}

Durante el proceso de importación de datos, puede utilizar pnodeRun (getRecentJobs) para observar la finalización de las tareas en segundo plano.

Guión completo del caso


Supongo que te gusta

Origin blog.51cto.com/15022783/2562266
Recomendado
Clasificación