2010-05-21 19 views
5

Sto analizzando un log e inserendolo in MySQL o SQLite utilizzando SQLAlchemy e Python. In questo istante apro una connessione al DB e, mentre faccio il loop su ogni riga, lo inserisco dopo che è stato analizzato (questo è solo un grande tavolo in questo momento, non molto esperto con SQL). Quindi chiudo la connessione quando il ciclo è terminato. Il codice riassunto è:Inserimento più rapido di record in una tabella 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 

Le mie due domande sono:

  • Esiste un modo per accelerare gli inserti all'interno di questo quadro di base? Forse hanno una coda di inserti e alcuni thread di inserimento, una sorta di inserimenti di massa, ecc?
  • Quando ho usato MySQL, per circa 1,2 milioni di record il tempo di inserimento era di 15 minuti. Con SQLite, il tempo di inserimento è stato di poco più di un'ora. Questa differenza di orario tra i motori db sembra giusta, o significa che sto facendo qualcosa di molto sbagliato?
+0

Devo anche dire che quando mi sto chiedendo un modo per accelerarlo, voglio dire c'è qualcosa di fondamentale che dovrei fare e non sono che mi darà un grande guadagno (cioè, almeno più del 25% aumento della velocità di tempo).La velocità non è l'essenza qui, mi chiedo solo se sto facendo qualcosa in modo pedone poiché questo è tutto nuovo per me. –

risposta

4

La cosa più importante che si dovrebbe provare è inserire una transazione attorno a più inserti poiché è il commit del database su disco che richiede davvero molto tempo. Avrai bisogno di decidere il livello di batch, ma un primo tentativo grezzo sarebbe quello di avvolgere una transazione su tutto il lotto.

+0

Quindi qualcosa come un array di oggetti ins che creo, e quindi eseguo quando l'array è pieno? O non è quello che intendi? –

+0

@Kyle: dovrai creare una transazione con transaction = session.create_transaction(); e poi fare un transaction.commit. Vedi http://www.rmunn.com/sqlalchemy-tutorial/tutorial.html e scorri verso il basso fino all'intestazione "Transazioni". –

+0

Non proprio quello che ho fatto, ma questa era l'idea dietro ciò che facevo e mi piace dare punti alle persone :-). Spiegato cosa ho fatto di seguito. –

3

Senza conoscere il motore di tabella (MyISAM? InnoDB?), Lo schema e gli indici, è difficile commentare le specifiche tra i due database in uso.

Tuttavia, quando si utilizza MySQL in questo modo, è probabile che sia molto più veloce scrivere i dati in un file di testo temporaneo e quindi use the LOAD DATA INFILE syntax per caricarlo nel database. Sembra che you can call the execute method on your connection object esegua l'SQL necessario per eseguire questa operazione.

Inoltre, se si è morti impostare l'aggiunta di elementi riga per riga e si sta ricreando la tabella ogni volta, è possibile verificare i vincoli di chiave nel programma e aggiungere tali vincoli solo dopo aver inserito tutte le righe, salvando il DB il tempo di fare controlli di vincoli su ogni inserto.

+0

"è possibile verificare i vincoli di chiave nel programma e aggiungere quei vincoli solo dopo che tutte le righe sono state inserite, salvando il DB il tempo di eseguire controlli di vincoli su ogni inserto." Puoi crearlo un po 'per me, quella parte è andata oltre la mia testa :-P –

+0

@Kyle È difficile dare specifiche senza uno schema di tabella su cui lavorare. Ma, per esempio, se hai degli indici UNIQUE, quell'unicità è un vincolo sul tavolo. Ogni volta che inserisci una riga, il database assicura che non ci sia un'altra riga in conflitto con quella. Dato che hai solo una tabella, non devi preoccuparti dei vincoli delle chiavi esterne, ma se dovessi aggiungerli successivamente, questo si applicherebbe anche a loro. –

2

Ho fatto la seguente per raggiungere un certo dosaggio:

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) 

Questo non usa le transazioni, ma in un modo molto pigro mi ha permesso di girare l'inserto/parse tappa da ~ 13 secondi a circa ~ 2 secondi con backend mysql usando un campione più piccolo. Vedrò quale differenza tra mysql e sqlite è ora con questa modifica utilizzando l'intero campione.

Ho trovato le informazioni di base per questo here.

Risultati:
del motore: non raggruppata Inserire tempo in minuti: raggruppato Inserire tempo in minuti
Sqlite: 61: 8
MySql: 15: 2,5

io non scaricava la cache tra mysql e sqlite che avrebbero avuto probabilmente il file di testo sorgente, ma non penso che sarebbe una differenza relativamente significativa.

Problemi correlati