2012-09-15 19 views
6

Estoy usando algunos scripts de Python para hacer estadísticas. contenido Un tipo de registros son como esto lo llamo Una registros: cada Unos registros tiene el formato de:manejo de muchos archivos de registro enormes con Python

[2012-09-12 12:23:33] SOME_UNIQ_ID filesize 

otros registros que llamo registra B tiene el formato de:

[2012-09-12 12:24:00] SOME_UNIQ_ID 

Necesito contar cuántos registros en los registros A también están en los registros B, y obtener el intervalo de tiempo de los dos registros con el mismo id. De registro. Mi implementación fue cargar todo el tiempo e ID de registros B en un mapa, luego iterar los registros A para verificar si su ID existía en el mapa. El problema es que arroja demasiada memoria porque tengo casi 100 millones de registros en los registros B. Cualquier sugerencia para mejorar el rendimiento y el uso de la memoria? Gracias.

+0

¿Cuántos registros en el mapa A? ¿También 100 millones? – nneonneo

+0

Registrar en el registro A no se cargará en el mapa, solo cargue los registros en B. La A y la B tienen casi el mismo tamaño. – cheneydeng

+1

Vea también http://stackoverflow.com/questions/7331700/how-can-i-find-intersection-of-two-large-file-efficientlyusing-python – nneonneo

Respuesta

3

Puede intentar invertir la búsqueda dependiendo de si "A" cabe en la memoria y escanea secuencialmente "B".

De lo contrario, la carga de los archivos de registro en una base de datos SQLite3 con dos tablas (log_a, log_b) que contienen (marca de tiempo, uniq_id, rest_of_line), a continuación, ejecutar una consulta SQL se unen en uniq_id, y ningún proceso que necesita en los resultados de ese . Esto mantendrá la memoria bajo costo operativo, permite que el motor de SQL para hacer la unión, pero por supuesto no requieren efectivamente la duplicación de los archivos de registro en disco (pero eso no es generalmente un problema en la mayoría de los sistemas)

ejemplo

import sqlite3 
from datetime import datetime 

db = sqlite3.connect(':memory:') 

db.execute('create table log_a (timestamp, uniq_id, filesize)') 
a = ['[2012-09-12 12:23:33] SOME_UNIQ_ID filesize'] 
for line in a: 
    timestamp, uniq_id, filesize = line.rsplit(' ', 2) 
    db.execute('insert into log_a values(?, ?, ?)', (timestamp, uniq_id, filesize)) 
db.commit() 

db.execute('create table log_b (timestamp, uniq_id)') 
b = ['[2012-09-12 13:23:33] SOME_UNIQ_ID'] 
for line in b: 
    timestamp, uniq_id = line.rsplit(' ', 1) 
    db.execute('insert into log_b values(?, ?)', (timestamp, uniq_id)) 
db.commit() 

TIME_FORMAT = '[%Y-%m-%d %H:%M:%S]' 
for matches in db.execute('select * from log_a join log_b using (uniq_id)'): 
    log_a_ts = datetime.strptime(matches[0], TIME_FORMAT) 
    log_b_ts = datetime.strptime(matches[3], TIME_FORMAT) 
    print matches[1], 'has a difference of', abs(log_a_ts - log_b_ts) 
    # 'SOME_UNIQ_ID has a difference of 1:00:00' 
    # '1:00:00' == datetime.timedelta(0, 3600) 

Tenga en cuenta que:

  • la .connect en sqlite3 debe ser un nombre de archivo
  • a y 0.123.deberían ser sus archivos
+0

No tengo esa experiencia, ¿llevará demasiado tiempo insertar registros en un SQLite? Y tengo que calcular el intervalo de tiempo, ¿admite SQL alguna función para obtenerlo? – cheneydeng

+0

@cheneydeng No lo hubiera pensado, no más de unos minutos dependiendo del hardware ... Es uno de esos que lo prueban y ven lo que sucede, cosas me temo ... http://docs.python.org /library/sqlite3.html –

+0

@cheneydeng La parte que tardará más tiempo es 'join', ya que SQLite3 probablemente decidirá ordenar los archivos (lo que significa, en efecto, que estará recuperando los resultados de un merge-sort como Geordee Naliyath ha propuesto) –

0

En primer lugar, ¿cuál es el formato de la identificación? ¿Es globalmente único?

Elegiría una de esas tres opciones.

  • uso de bases de datos
  • unión de dos conjuntos de identificadores de
  • herramientas de Unix

Asumo prefiere una segunda opción. Cargue solamente ID de A y B. Suponiendo que el ID se ajuste al entero de 32 bits, el uso de la memoria será inferior a 1 GB. A continuación, cargue la fecha y hora de los mismos identificadores y calcule el espacio. La primera opción sería la mejor para los requisitos.

+0

La identificación es una uuid como esta/48d4493d-cb57-4505-a64d-b12c89c09dca – cheneydeng

+0

¿es hash aleatorio de 128 bits? 200 mln * 16 byte prob. ser demasiado –

1

Prueba esto:

  • Externamente ordenar tanto los archivos
  • leer el archivo de registro estándar y ahorrar SOME_UNIQ_ID (A)
  • leer el archivo B Registros y guardar el SOME_UNIQ_ID (B)
  • Comparar la SOME_UNIQ_ID (B) con SOME_UNIQ_ID (A)
    • Si es menor, leer el archivo B Registros de nuevo
    • Si i t es mayor, leer un archivo de registros de nuevo y comparar con SOME_UNIQ_ID salvado (B)
    • Si es igual a encontrar la diferencia de tiempo

Suponiendo tipo externo funciona de manera eficiente, se termina el proceso de lectura, tanto archivos solo una vez

+0

Si preselecciona ambos archivos el {id, date} la huella de memoria máxima para un paso de fusión sería la del ID con la mayor cantidad de registros A * B coincidentes/superpuestos. – wildplasser

+0

"Si es menor, lea B Si es mayor, lea A" ID's son GUIDs no números secuenciales. –

+1

No es necesario que los ID sean números secuenciales. El algoritmo funcionará siempre que las ID puedan ser ordenadas. –

0

Si la ID única se puede ordenar (por ejemplo, alfabéticamente o numéricamente), puede realizar un lote de las comparaciones.

Supongamos, por ejemplo, que la ID es numérica con un rango de 1 - 10^7. Luego, primero puede colocar los primeros 10^6 elementos en su hashtable, hacer un escaneo secuencial a través del segundo archivo para buscar registros coincidentes.

En pseudopython, no he probado esto:

for i in xrange(0,9): 
    for line in file1: 
     time, id = line.split(']') 
     id = int(id) 
     if i * 10**6 < id < (i+1) * 10**6: 
      hash_table[id] = time 

    for line in file2: 
     time, id = line.split(']') # needs a second split to get the id 
     id = int(id) 
     if id in hashtable: 
      # compare timestamps 

Si las identificaciones no son numéricos, puede crear lotes mediante una carta clave:

if id.startswith(a_letter_from_the_alphabet): 
    hash_table[id] = time 
0

A medida que el cuello de botella es la traducción de marcas de tiempo. Divido esta acción en muchas máquinas aisladas que generan registros A y registros B. Estas máquinas traducen los sellos de cadena a una hora de época, y la máquina CENTRAL que utiliza todos estos registros para calcular mi resultado ahora toma casi 1/20 tiempo de la manera original. Publiqué mi solución aquí, gracias a todos ustedes.

0

Sugiero usar la base de datos que tiene soporte para datetime y uniqueidentifier para la forma mostrada de la identificación única. Viene de Windows, y si usa Windows para la tarea, puede usar decir Microsoft SQL 2008 R2 Express edition (gratis). Las dos tablas no usarán ningún tipo de clave.

Puede usar bcp utility del MS SQL que probablemente sea una de las maneras más rápidas de insertar los datos del archivo de texto (o el comando BULK INSERT).

Los índices en el identificador único deben crearse solo después de insertar todos los recors. De lo contrario, la existencia de índices hace que la operación de inserción sea más lenta. Entonces la unión interna debe ser tan rápida como sea técnicamente posible.

Cuestiones relacionadas