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


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.


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.


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.


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.


E agora estou curioso para saber qual seria o efeito do disco se você fizesse algo como "mv cache.dir-old / dev / null"
— ivanivan
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.