Acelerar las inserciones de base de datos de ORM
-
05-07-2019 - |
Pregunta
Tengo una vista de Django que crea 500-5000 nuevos INSERTOS de base de datos en un bucle. El problema es que es muy lento! Estoy recibiendo alrededor de 100 inserciones por minuto en Postgres 8.3. Solíamos usar MySQL en hardware menor (instancia EC2 más pequeña) y nunca tuvimos este tipo de problemas de velocidad.
Detalles: Postgres 8.3 en Ubuntu Server 9.04. El servidor es un " grande " Amazon EC2 con base de datos en EBS (ext3) - 11GB / 20GB.
Aquí hay algunos de mis postgresql.conf: avíseme si necesita más
shared_buffers = 4000MB
effective_cache_size = 7128MB
Mi 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()
Algunos resultados de la parte superior:
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
Hazme saber si algún otro detalle sería útil.
Solución
En primer lugar, las operaciones de ORM siempre serán más lentas que SQL puro. Una vez escribí una actualización de una base de datos grande en código ORM y la puse en marcha, pero la dejé después de varias horas cuando había completado solo una pequeña fracción. Después de reescribirlo en SQL, todo se ejecutó en menos de un minuto.
En segundo lugar, tenga en cuenta que su código aquí está realizando hasta cuatro operaciones de base de datos separadas para cada fila en su conjunto de datos: el get
en get_or_create, posiblemente también el create
, el count
en el filtro, y finalmente el save
. Eso es un montón de acceso a la base de datos.
Teniendo en cuenta que un máximo de 5000 objetos no es enorme, debería poder leer todo el conjunto de datos en la memoria al comienzo. Luego puede hacer un solo filter
para obtener todos los objetos de palabras clave existentes de una sola vez, guardando una gran cantidad de consultas en la palabra clave get_or_create
y también evitando la necesidad de crear instancias duplicadas ProfileKeywords en primer lugar.
Otros consejos
Una razón común para las operaciones masivas lentas como esta es que cada inserción ocurre en su propia transacción. Si puede lograr que todos ocurran en una sola transacción, podría ir mucho más rápido.