Accelerare gli inserimenti di database da ORM
-
05-07-2019 - |
Domanda
Ho una vista Django che crea 500-5000 nuovi INSERTI di database in un ciclo. Il problema è che è molto lento! Ricevo circa 100 inserti al minuto su Postgres 8.3. Usavamo MySQL su hardware minore (istanza EC2 più piccola) e non abbiamo mai avuto questo tipo di problemi di velocità.
Dettagli: Postgres 8.3 su Ubuntu Server 9.04. Il server è un "grande" Amazon EC2 con database su EBS (ext3) - 11GB / 20GB.
Ecco alcuni dei miei postgresql.conf: fammi sapere se ne hai bisogno di più
shared_buffers = 4000MB
effective_cache_size = 7128MB
My python:
for k in kw:
k = k.lower()
p = ProfileKeyword(profile=self)
logging.debug(k)
p.keyword, created = Keyword.objects.get_or_create(keyword=k, defaults={'keyword':k,})
if not created and ProfileKeyword.objects.filter(profile=self, keyword=p.keyword).count():
#checking created is just a small optimization to save some database hits on new keywords
pass #duplicate entry
else:
p.save()
Alcuni output dall'alto:
top - 16:56:22 up 21 days, 20:55, 4 users, load average: 0.99, 1.01, 0.94
Tasks: 68 total, 1 running, 67 sleeping, 0 stopped, 0 zombie
Cpu(s): 5.8%us, 0.2%sy, 0.0%ni, 90.5%id, 0.7%wa, 0.0%hi, 0.0%si, 2.8%st
Mem: 15736360k total, 12527788k used, 3208572k free, 332188k buffers
Swap: 0k total, 0k used, 0k free, 11322048k cached
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
14767 postgres 25 0 4164m 117m 114m S 22 0.8 2:52.00 postgres
1 root 20 0 4024 700 592 S 0 0.0 0:01.09 init
2 root RT 0 0 0 0 S 0 0.0 0:11.76 migration/0
3 root 34 19 0 0 0 S 0 0.0 0:00.00 ksoftirqd/0
4 root RT 0 0 0 0 S 0 0.0 0:00.00 watchdog/0
5 root 10 -5 0 0 0 S 0 0.0 0:00.08 events/0
6 root 11 -5 0 0 0 S 0 0.0 0:00.00 khelper
7 root 10 -5 0 0 0 S 0 0.0 0:00.00 kthread
9 root 10 -5 0 0 0 S 0 0.0 0:00.00 xenwatch
10 root 10 -5 0 0 0 S 0 0.0 0:00.00 xenbus
18 root RT -5 0 0 0 S 0 0.0 0:11.84 migration/1
19 root 34 19 0 0 0 S 0 0.0 0:00.01 ksoftirqd/1
Fammi sapere se altri dettagli sarebbero utili.
Soluzione
In primo luogo, le operazioni ORM saranno sempre più lente del puro SQL. Una volta ho scritto un aggiornamento a un database di grandi dimensioni nel codice ORM e l'ho impostato in esecuzione, ma l'ho chiuso dopo diverse ore quando aveva completato solo una piccola parte. Dopo averlo riscritto in SQL, il tutto è stato eseguito in meno di un minuto.
In secondo luogo, tieni presente che il tuo codice qui sta eseguendo fino a quattro operazioni di database separate per ogni riga nel tuo set di dati: get
in get_or_create, possibilmente anche create
, il count
sul filtro e infine il save
. C'è molto accesso al database.
Tenendo presente che un massimo di 5000 oggetti non è enorme, dovresti essere in grado di leggere l'intero set di dati in memoria all'inizio. Quindi puoi fare un singolo filtro
per ottenere tutti gli oggetti Keyword esistenti in una volta sola, salvando un numero enorme di query nel Keyword get_or_create
ed evitando anche la necessità di istanziare duplicati ProfileKeywords in primo luogo.
Altri suggerimenti
Un motivo comune per operazioni in blocco lente come questa è che ogni inserzione avviene nella propria transazione. Se riesci a farli accadere tutti in una singola transazione, potrebbe andare molto più veloce.