Como excluir milhões de arquivos sem perturbar o servidor

11

Gostaria de excluir um diretório de cache nginx, que eu limpei rapidamente:

mv cache cache.bak
mkdir cache
service nginx restart

Agora eu tenho uma cache.bakpasta que possui 2 milhões de arquivos. Eu gostaria de excluí-lo, sem perturbar o servidor.

Uma simples rm -rf cache.baklixeira do servidor, até a resposta HTTP mais simples leva 16 segundos enquanto a rm está em execução, portanto não posso fazer isso.

Eu tentei ionice -c3 rm -rf cache.bak, mas não ajudou. O servidor tem um HDD, não um SSD, provavelmente em um SSD, isso pode não ser um problema.

Acredito que a melhor solução seria algum tipo de limitação, como o gerenciador de cache embutido do nginx.

Como você resolveria isso? Existe alguma ferramenta que possa fazer exatamente isso?

ext4 no Ubuntu 16.04

hyperknot
fonte
1
Como você se recuperou do "rm -rf cache.bak"? Parece que o nginx estava em execução quando você renomeou, portanto, pode ter mantido os descritores de arquivo e até ter mudado para o novo diretório. Eu acho que você precisa desligar o nginx completamente, excluir o cache e reiniciar.
Jan Steinman
6
No futuro, cole seu cache em um sistema de arquivos separado. Dessa forma, você pode simplesmente destruir o sistema de arquivos, o que é muito mais rápido do que tentar excluir milhões de arquivos. Aprendi isso da maneira mais difícil há alguns anos atrás, com um diretório de spool hylafax contendo zilhões de arquivos.
Dennis Kaarsemaker
Você já tentou correr rmusando nice ?
Vladislav Rastrusny
Tente rsync para excluir rapidamente - respostas para um caso semelhante - unix.stackexchange.com/questions/37329/…
kawu
Obrigado por todos os comentários, resumi minhas descobertas na resposta do artigo.
hyperknot

Respostas:

9

Faça um script bash como este:

#!/bin/bash
rm -- "$*"
sleep 0.5

Salve-o com o nome, deleter.shpor exemplo. Execute chmod u+x deleter.shpara torná-lo executável.

Esse script exclui todos os arquivos passados ​​para ele como argumentos e dorme 0,5 segundos.

Então, você pode executar

find cache.bak -print0 | xargs -0 -n 5 deleter.sh

Este comando recupera uma lista de todos os arquivos no cache.bak e passa os cinco nomes de arquivos por vez para o script de exclusão.

Portanto, você pode ajustar quantos arquivos são excluídos por vez e quanto tempo demora entre cada operação de exclusão.

Tero Kilkanen
fonte
Obrigado por esta solução, eu a incluí na minha redação geral. Uma pergunta, porém, como isso está lidando com ns grandes? Normalmente, eu tinha problemas com caracteres * em grandes diretórios, fornecendo erros, não é o caso aqui?
hyperknot
xargsentende o tamanho máximo de uma linha de comando e tenta não exceder esse valor por padrão. Este possui limites adicionais de não mais que 5 caminhos por vez.
BowlOfRed
1
Lembre-se de que, na taxa de 10 arquivos por segundo, levará 55 horas para excluir 2 milhões de arquivos.
Andrew Henle
4

Você deve salvar seu cache em um sistema de arquivos separado, para montar / desmontar como indicado nos comentários. Até que você faça isso, você pode usar este liner /usr/bin/find /path/to/files/ -type f -print0 -exec sleep 0.2 \; -exec echo \; -deleteassumindo que o seu binário de localização esteja localizado em / usr / bin e que você deseja ver o progresso na tela. Ajuste o sono adequadamente, para não estressar demais o disco rígido.

Alex
fonte
Não é necessário -print0aqui, pois você não está canalizando a saída de findnenhum lugar.
Tero Kilkanen
Você pode estar interessado no que está acontecendo. Pode chamar de paranóia, mas eu sempre quero ter certeza de que estou excluindo os arquivos corretos.
31416 Alex
Ah verdade, eu não estava decodificando o comando corretamente, meu mal.
Tero Kilkanen
3

Você pode tentar o ionice em um script que consome a saída de um comando find. Algo como o seguinte:

ionice -c3 $(
for file in find cache.bak -type f; do
    rm $file
done
for dir in find cache.bak -depthe -type d -empty; do
    rmdir $dir
done
)

Dependendo do sistema de arquivos, cada exclusão de arquivo pode resultar na reescrita de todo o diretório. Para diretórios grandes, isso pode ser um sucesso. Há atualizações adicionais necessárias para a tabela de inodes e, possivelmente, uma lista de espaço livre.

Se o sistema de arquivos tiver um diário, as alterações serão gravadas no diário; aplicado; e removido do diário. Isso aumenta os requisitos de E / S para atividades intensivas de gravação.

Você pode querer usar um sistema de arquivos sem um diário para o cache.

Em vez de ionice, você pode usar um comando de suspensão para limitar a taxa de ações. Isso funcionará mesmo que o ionice não funcione, mas levará muito tempo para excluir todos os seus arquivos.

BillThor
fonte
2

Eu tenho muitas respostas / comentários úteis aqui, que eu gostaria de concluir e mostrar minha solução também.

  1. Sim, a melhor maneira de impedir que isso aconteça é manter o diretório do cache em um sistema de arquivos separado. Nuking / formatação rápida de um sistema de arquivos sempre leva alguns segundos (talvez minutos), não relacionados a quantos arquivos / diretórios estavam presentes nele.

  2. As soluções ionice/ nicenão fizeram nada, porque o processo de exclusão realmente causou quase nenhuma E / S. O que causou a E / S foi que eu acredito que as filas / buffers no nível do kernel / sistema de arquivos estão sendo preenchidos quando os arquivos foram excluídos muito rapidamente pelo processo de exclusão.

  3. A maneira como resolvi é semelhante à solução de Tero Kilkanen, mas não exigia a chamada de um shell script. Eu usei o --bwlimitswitch embutido do rsync para limitar a velocidade da exclusão.

O comando completo foi:

mkdir empty_dir
rsync -v -a --delete --bwlimit=1 empty_dir/ cache.bak/

Agora bwlimit especifica a largura de banda em kilobyes, que neste caso se aplica ao nome do arquivo ou caminho dos arquivos. Ao defini-lo como 1 KBps, ele excluía cerca de 100.000 arquivos por hora ou 27 arquivos por segundo. Os arquivos tinham caminhos relativos cache.bak/e/c1/db98339573acc5c76bdac4a601f9ec1e, com 47 caracteres, portanto, daria 1000/47 ~ = 21 arquivos por segundo, algo semelhante ao meu palpite de 100.000 arquivos por hora.

Agora porque --bwlimit=1? Eu tentei vários valores:

  • 10000, 1000, 100 -> sistema desacelerando como antes
  • 10 -> sistema funcionando muito bem por um tempo, mas produz desacelerações parciais uma vez por minuto, aproximadamente. Tempos de resposta HTTP ainda <1 s.
  • 1 -> sem desaceleração do sistema. Não tenho pressa e 2 milhões de arquivos podem ser excluídos em <1 dia dessa maneira, por isso escolhi.

Eu gosto da simplicidade do método interno do rsync, mas essa solução depende do comprimento do caminho relativo. Não é um grande problema, pois a maioria das pessoas encontraria o valor certo por tentativa e erro.

hyperknot
fonte
E agora estou curioso para saber qual seria o efeito do disco se você fizesse algo como "mv cache.dir-old / dev / null"
ivanivan