2010-05-21 22 views
5

Estoy analizando un registro e insertándolo en MySQL o SQLite usando SQLAlchemy y Python. Ahora mismo, abro una conexión con el DB, y mientras recorro cada línea, la inserto luego de que se haya analizado (esta es solo una gran tabla en este momento, no tiene mucha experiencia con SQL). Luego cierro la conexión cuando termina el ciclo. El código es resumida:Inserción más rápida de registros en una tabla con SQLAlchemy

log_table = schema.Table('log_table', metadata, 
         schema.Column('id', types.Integer, primary_key=True), 
         schema.Column('time', types.DateTime), 
         schema.Column('ip', types.String(length=15)) 
.... 
engine = create_engine(...) 
metadata.bind = engine 
connection = engine.connect() 
.... 
for line in file_to_parse: 
    m = line_regex.match(line) 
    if m: 
     fields = m.groupdict() 
     pythonified = pythoninfy_log(fields) #Turn them into ints, datatimes, etc 
     if use_sql: 
      ins = log_table.insert(values=pythonified) 
      connection.execute(ins) 
      parsed += 1 

Mis dos preguntas son:

  • ¿Hay una manera de acelerar los insertos dentro de este marco básico? Tal vez tenga una cola de inserciones y algunos hilos de inserción, algún tipo de inserciones a granel, etc.
  • Cuando utilicé MySQL, para aproximadamente ~ 1.2 millones de registros, el tiempo de inserción fue de 15 minutos. Con SQLite, el tiempo de inserción fue de poco más de una hora. ¿Esa diferencia de tiempo entre los motores DB parece correcta, o significa que estoy haciendo algo muy malo?
+0

Debo también decir que cuando me pregunto sobre una forma de acelerarlo, quiero decir que hay algo básico que debería hacer y que no me dará una gran ganancia (es decir, al menos más del 25% aumento de velocidad de tiempo).La velocidad no es la esencia aquí, solo me pregunto si estoy haciendo algo de manera peatonal ya que esto es nuevo para mí. –

Respuesta

4

Lo importante que debe intentar es realizar una transacción en varias inserciones, ya que es la confirmación de la base de datos en el disco lo que realmente lleva mucho tiempo. Tendrá que decidir el nivel de procesamiento por lotes, pero un primer intento crudo sería envolver una transacción en todo el lote.

+0

¿Algo así como una matriz de los ins que creo, y luego se ejecuta cuando la matriz está llena? ¿O no es eso lo que quieres decir? –

+0

@Kyle: Deberá crear una transacción con transaction = session.create_transaction(); y luego hacer una transacción.commit. Consulte http://www.rmunn.com/sqlalchemy-tutorial/tutorial.html y desplácese hacia abajo hasta el encabezado "Transacciones". –

+0

No es lo que hice, pero esta es la idea detrás de lo que hice y me gusta darle puntos a la gente :-). Explicado lo que hice a continuación. –

3

Sin conocer el motor de tablas (MyISAM? InnoDB?), El esquema y los índices, es difícil comentar los detalles entre las dos bases de datos que está utilizando allí.

Sin embargo, al utilizar MySQL de esta manera, es probable que encuentre que es mucho más rápido escribir sus datos en un archivo de texto temporal y luego use the LOAD DATA INFILE syntax para cargarlo todo en su base de datos. Parece que you can call the execute method on your connection object ejecuta el SQL necesario para hacer esto.

Además, si no está acostumbrado a agregar cosas fila por fila, y está recreando la tabla cada vez, puede verificar las restricciones clave en su programa y agregar esas restricciones solo después de que se hayan insertado todas las filas, guardando la DB el tiempo de hacer comprobaciones de restricciones en cada inserción.

+0

"puede verificar las restricciones clave en su programa y agregar esas restricciones solo después de que se hayan insertado todas las filas, ahorrándole al DB el tiempo de hacer comprobaciones de restricciones en cada inserción". Puedes romper eso un poco para mí, esa parte se me pasó por la cabeza :-P –

+0

@Kyle Es difícil dar detalles sin un esquema de tabla para trabajar. Pero, por ejemplo, si tiene cualquier índice ÚNICO, esa unicidad es una restricción en la tabla. Cada vez que inserta una fila, la base de datos se asegura de que no haya otra fila que entre en conflicto con esa. Como solo tiene una tabla, no tiene que preocuparse por las restricciones de clave externa, pero si debe agregarlas más adelante, esto se aplicaría a ellas también. –

2

hice lo siguiente para lograr algún procesamiento por lotes:

inserts = [] 
insert_every = 1000 
for line in file_to_parse: 
    m = line_regex.match(line) 
    if m: 
     fields = m.groupdict() 
     if use_sql: #This uses Globals, Ick :-/ 
      inserts.append(pythonified) 
      if (parsed % insert_every) == 0: 
       connection.execute(log_table.insert(), inserts) 
       inserts = [] 
      parsed += 1 
if use_sql: 
    if len(inserts) > 0: 
     connection.execute(log_table.insert(), inserts) 

Esto no utiliza transacciones, pero de una manera muy perezoso Me permitió gire la pieza/analizo etapa de ~ 13 segundos hasta aproximadamente ~ 2 segundos con back-end mysql usando una muestra más pequeña. Veré cuál es la diferencia entre mysql y sqlite ahora con este cambio usando la muestra completa.

Encontré la información básica para este here.

Resultados:
del motor: no agrupados Insertar hora en minutos: Agrupados Insertar tiempo en minutos
SQLite: 61: 8
MySQL: 15: 2.5

no se podía tirar mi caché entre el mysql y sqlite que habría tenido el archivo de texto fuente posiblemente, pero no creo que sea una diferencia relativamente significativa.

Cuestiones relacionadas