Acelerar as inserções de banco de dados de ORM
-
05-07-2019 - |
Pergunta
Eu tenho uma visão Django que cria 500-5000 novas pastilhas de banco de dados em um loop. O problema é que é muito lento! Estou recebendo cerca de 100 inserções por minuto em Postgres 8.3. Nós costumávamos usar MySQL em menos hardware (menor instância EC2) e nunca teve esses tipos de problemas de velocidade.
Detalhes: Postgres 8.3 no Ubuntu Servidor 9,04. Server é um "grande" Amazon EC2 com banco de dados sobre EBS (ext3) -. 11GB / 20GB
Aqui está um pouco da minha postgresql.conf - deixe-me saber se você precisar de mais
shared_buffers = 4000MB
effective_cache_size = 7128MB
O meu 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()
Alguns saída do topo:
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
Deixe-me saber se quaisquer outros detalhes seria útil.
Solução
Em primeiro lugar, as operações ORM são sempre vai ser mais lento do que o SQL puro. Uma vez escrevi uma atualização para um grande banco de dados em código ORM e configurá-lo correr, mas abandonou-o após várias horas quando ele tinha completado apenas uma pequena fração. Depois de reescrever-lo em SQL toda a coisa correu em menos de um minuto.
Em segundo lugar, tenha em mente que o seu código aqui está fazendo até quatro operações de banco de dados separados para cada linha em seu conjunto de dados - o get
em get_or_create, possivelmente também o create
, o count
no filtro, e finalmente o save
. Isso é um monte de acesso de banco de dados.
Tendo em conta que um máximo de 5000 objetos não é enorme, você deve ser capaz de ler todo o conjunto de dados na memória no início. Então você pode fazer um único filter
para obter todas as palavras-chave existentes objetos de uma só vez, economizando um grande número de consultas no get_or_create
palavras-chave e também evitando a necessidade de instanciar ProfileKeywords duplicados em primeiro lugar.
Outras dicas
Uma razão comum para operações em massa lentos como esta é cada inserção acontecendo em sua própria transação. Se você pode obter todos eles para acontecer em uma única transação, que poderia ir muito mais rápido.