Histórico: servidor físico, com cerca de dois anos, unidades SATA de 7200-RPM conectadas a uma placa 3Ware RAID, ext3 FS montado noatime e dados = pedidos, sem carga louca, kernel 2.6.18-92.1.22.el5, tempo de atividade 545 dias . O diretório não contém subdiretórios, apenas milhões de arquivos pequenos (~ 100 bytes), com alguns maiores (alguns KB).
Temos um servidor que ficou um pouco covarde ao longo dos últimos meses, mas só o notamos outro dia quando começou a não conseguir gravar em um diretório por conter muitos arquivos. Especificamente, ele começou a lançar esse erro em / var / log / messages:
ext3_dx_add_entry: Directory index full!
O disco em questão possui muitos inodes restantes:
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/sda3 60719104 3465660 57253444 6% /
Então, acho que isso significa que atingimos o limite de quantas entradas podem estar no próprio arquivo de diretório. Não faço ideia de quantos arquivos seriam, mas não pode ser mais do que três milhões, como você pode ver. Não que isso seja bom, lembre-se! Mas essa é uma parte da minha pergunta: exatamente qual é esse limite superior? É sintonizável? Antes de eu chegar gritado-Quero ajustá-lo para baixo ; esse diretório enorme causou todo tipo de problemas.
Enfim, rastreamos o problema no código que estava gerando todos esses arquivos e o corrigimos. Agora estou preso em excluir o diretório.
Algumas opções aqui:
rm -rf (dir)
Eu tentei isso primeiro. Desisti e matei depois que durou um dia e meio, sem qualquer impacto perceptível.
- unlink (2) no diretório: definitivamente vale a pena considerar, mas a questão é se seria mais rápido excluir os arquivos dentro do diretório via fsck do que excluir via unlink (2). Ou seja, de uma forma ou de outra, tenho que marcar esses inodes como não utilizados. Isso pressupõe, é claro, que eu posso dizer ao fsck para não soltar entradas nos arquivos em / lost + found; caso contrário, acabei de mudar meu problema. Além de todas as outras preocupações, depois de ler um pouco mais sobre isso, provavelmente eu teria que chamar algumas funções internas do FS, pois nenhuma das variantes de desvinculação (2) que posso encontrar me permitiria excluir alegremente um diretório com entradas. Pooh.
while [ true ]; do ls -Uf | head -n 10000 | xargs rm -f 2>/dev/null; done )
Esta é realmente a versão abreviada; o real que estou executando, que apenas adiciona alguns relatórios de progresso e uma parada limpa quando ficamos sem arquivos para excluir, é:
exportar i = 0; time (while [true]; faça ls -Uf | cabeça -n 3 | grep -qF '.png' || pausa; ls -Uf | cabeça -n 10000 | xargs rm -f 2> / dev / null; exportar i = $ (($ i + 10000)); eco "$ i ..."; feito )
Isso parece estar funcionando muito bem. Enquanto escrevo isso, ele excluiu 260.000 arquivos nos últimos trinta minutos.
- Como mencionado acima, o limite de entrada por diretório é ajustável?
- Por que demorou "7m9.561s / usuário 0m0.001s / sys 0m0.001s" reais para excluir um único arquivo que foi o primeiro na lista retornado
ls -U
e demorou talvez dez minutos para excluir as primeiras 10.000 entradas com o comando comando no # 3, mas agora ele está indo muito feliz? Na verdade, ele excluiu 260.000 em cerca de trinta minutos, mas agora são necessários mais quinze minutos para excluir mais 60.000. Por que as enormes oscilações de velocidade? - Existe uma maneira melhor de fazer esse tipo de coisa? Não armazene milhões de arquivos em um diretório; Eu sei que isso é bobagem, e isso não teria acontecido no meu relógio. Pesquisar no Google e analisar SF e SO oferece muitas variações
find
que não serão significativamente mais rápidas do que minha abordagem por várias razões evidentes. Mas a idéia delete-via-fsck tem alguma perna? Ou algo completamente diferente? Estou ansioso para ouvir o pensamento fora da caixa (ou dentro da caixa não é bem conhecida).
Saída final do script !:
2970000...
2980000...
2990000...
3000000...
3010000...
real 253m59.331s
user 0m6.061s
sys 5m4.019s
Assim, três milhões de arquivos excluídos em pouco mais de quatro horas.
rm -rfv | pv -l >/dev/null
. O pv deve estar disponível no repositório EPEL .Respostas:
A
data=writeback
opção de montagem merece ser tentada, a fim de impedir o registro no diário do sistema de arquivos. Isso deve ser feito apenas durante o tempo de exclusão, no entanto, existe um risco se o servidor estiver sendo desligado ou reiniciado durante a operação de exclusão.De acordo com esta página ,
A opção é configurada dentro
fstab
ou durante a operação de montagem, substituindodata=ordered
pordata=writeback
. O sistema de arquivos que contém os arquivos a serem excluídos precisa ser remontado.fonte
commit
opção : "Esse valor padrão (ou qualquer valor baixo) prejudicará o desempenho, mas é bom para a segurança dos dados. A configuração para 0 terá o mesmo efeito que deixá-lo no padrão (5 segundos) A configuração para valores muito grandes melhorará o desempenho ".data=writeback
ainda registra os metadados antes de gravá-los no sistema de arquivos principal. Pelo que entendi, simplesmente não impõe a ordem entre coisas como escrever um mapa de extensão e gravar dados nessas extensões. Talvez haja outras restrições de pedidos que relaxam também, se você perceber um ganho de desempenho com isso. Obviamente, montar sem o diário pode ter um desempenho ainda maior. (Isso pode permitir que as alterações de metadados aconteçam na RAM, sem a necessidade de ter nada no disco antes da conclusão da operação de desvinculação).Embora uma das principais causas desse problema seja o desempenho ext3 com milhões de arquivos, a causa raiz real desse problema é diferente.
Quando um diretório precisa ser listado, readdir () é chamado no diretório que produz uma lista de arquivos. O readdir é uma chamada posix, mas a chamada real do sistema Linux que está sendo usada aqui é chamada de 'getdents'. Os getdents listam as entradas do diretório preenchendo um buffer com as entradas.
O problema se deve principalmente ao fato de que o readdir () usa um tamanho de buffer fixo de 32 KB para buscar arquivos. À medida que um diretório se torna cada vez maior (o tamanho aumenta à medida que os arquivos são adicionados), o ext3 fica cada vez mais lento para buscar entradas e o tamanho do buffer de 32 KB do readdir adicional é suficiente apenas para incluir uma fração das entradas no diretório. Isso faz com que o readdir faça um loop repetidamente e invoque a chamada cara do sistema repetidamente.
Por exemplo, em um diretório de teste que criei com mais de 2,6 milhões de arquivos, executar "ls -1 | wc-l" mostra uma grande saída de rastreio de muitas chamadas de sistema getdent.
Além disso, o tempo gasto nesse diretório foi significativo.
O método para tornar esse processo mais eficiente é chamar getdents manualmente com um buffer muito maior. Isso melhora significativamente o desempenho.
Agora, você não deve chamar os getdents manualmente, para que não exista uma interface para usá-lo normalmente (verifique a página de manual para obter os getdents!), No entanto, você pode chamá-lo manualmente e tornar a chamada de sistema mais eficiente.
Isso reduz drasticamente o tempo necessário para buscar esses arquivos. Eu escrevi um programa que faz isso.
Enquanto isso não combate o problema fundamental subjacente (muitos arquivos, em um sistema de arquivos com baixo desempenho). É provável que seja muito, muito mais rápido do que muitas das alternativas postadas.
Como uma previsão, deve-se remover o diretório afetado e refazê-lo depois. Os diretórios apenas aumentam de tamanho e podem permanecer com desempenho fraco, mesmo com alguns arquivos internos devido ao tamanho do diretório.
Edit: Eu limpei isso um pouco. Adicionada uma opção para permitir que você exclua na linha de comando em tempo de execução e removeu um monte de coisas na calçada que, honestamente, olhando para trás, era questionável na melhor das hipóteses. Também foi mostrado para produzir corrupção de memória.
Agora você pode fazer
dentls --delete /my/path
Novos resultados. Baseado em um diretório com 1,82 milhão de arquivos.
Fiquei surpreso que isso ainda funcione tão bem!
fonte
[256]
provavelmente deveria ser[FILENAME_MAX]
, e duas, meu Linux (2.6.18 == CentOS 5.x) parece não incluir uma entrada d_type no dirent (pelo menos de acordo com getdents (2)).Seria possível fazer backup de todos os outros arquivos deste sistema de arquivos em um local de armazenamento temporário, reformatar a partição e restaurar os arquivos?
fonte
Não existe um limite de arquivo por diretório no ext3, apenas o limite de inode do sistema de arquivos (acho que existe um limite no número de subdiretórios).
Você ainda pode ter problemas após remover os arquivos.
Quando um diretório possui milhões de arquivos, a própria entrada do diretório se torna muito grande. A entrada do diretório deve ser varrida para todas as operações de remoção e isso leva várias quantidades de tempo para cada arquivo, dependendo de onde sua entrada está localizada. Infelizmente, mesmo depois que todos os arquivos foram removidos, a entrada do diretório mantém seu tamanho. Portanto, outras operações que exigem a verificação da entrada do diretório ainda levarão muito tempo, mesmo que o diretório esteja vazio agora. A única maneira de resolver esse problema é renomear o diretório, criar um novo com o nome antigo e transferir os arquivos restantes para o novo. Em seguida, exclua o renomeado.
fonte
Eu não fiz comparações, mas esse cara fez :
fonte
O find simplesmente não funcionou para mim, mesmo depois de alterar os parâmetros do ext3 fs, conforme sugerido pelos usuários acima. Consumiu muita memória. Este script PHP fez o truque - uso rápido e insignificante da CPU, uso insignificante de memória:
Publiquei um relatório de bug sobre esse problema com o find: http://savannah.gnu.org/bugs/?31961
fonte
Recentemente, enfrentei um problema semelhante e não consegui fazer a
data=writeback
sugestão do ring0 funcionar (possivelmente devido ao fato de os arquivos estarem na minha partição principal). Enquanto pesquisava soluções alternativas, deparei-me com isso:Isso desativará completamente o diário, independentemente da
data
opção a ser fornecidamount
. Combinei isso comnoatime
o volumedir_index
definido e parecia funcionar muito bem. A exclusão realmente terminou sem que eu precisasse matá-la, meu sistema permaneceu responsivo e agora está de volta a funcionar (com o diário novamente) sem problemas.fonte
Certifique-se de fazer:
o que também deve acelerar um pouco as coisas.
fonte
É um comando muito lento. Experimentar:
fonte
strace -r -p <pid of rm>
para se conectar ao processo rm já em execução. Então você pode ver com que rapidez asunlink
chamadas do sistema estão passando. (-r
Coloca o tempo desde que a chamada de sistema anterior, no início de cada linha.)Está
dir_index
definido para o sistema de arquivos? (tune2fs -l | grep dir_index
) Caso contrário, ative-o. Geralmente está ativado para o novo RHEL.fonte
Alguns anos atrás, encontrei um diretório com 16 milhões de arquivos XML no
/
sistema de arquivos. Devido à crítica do servidor, usamos o seguinte comando que levou cerca de 30 horas para concluir:Era um disco rígido antigo de 7200 rpm e, apesar do gargalo de E / S e dos picos de CPU, o servidor da web antigo continuou seu serviço.
fonte
Minha opção preferida é a abordagem newfs, já sugerida. O problema básico é, novamente, como já observado, a verificação linear para lidar com a exclusão é problemática.
rm -rf
deve estar próximo do ideal para um sistema de arquivos local (o NFS seria diferente). Mas em milhões de arquivos, 36 bytes por nome de arquivo e 4 por inode (um palpite, sem verificar o valor do ext3), são 40 * milhões, para serem mantidos na RAM apenas para o diretório.Suponho que você esteja debulhando a memória cache de metadados do sistema de arquivos no Linux, para que os blocos de uma página do arquivo de diretório sejam eliminados enquanto você ainda estiver usando outra parte, apenas para acessar a página do cache novamente na próxima arquivo é excluído. O ajuste de desempenho do Linux não é minha área, mas / proc / sys / {vm, fs} / provavelmente contém algo relevante.
Se você puder pagar o tempo de inatividade, considere ativar o recurso dir_index. Ele muda o índice do diretório de linear para algo muito mais ideal para exclusão em diretórios grandes (árvores com hash b).
tune2fs -O dir_index ...
seguido pore2fsck -D
iria funcionar. No entanto, embora eu esteja confiante de que isso ajudaria antes que-D
ocorram problemas, não sei como a conversão (e2fsck com the ) é executada ao lidar com um diretório v.large existente. Backups + suck-it-and-see.fonte
/proc/sys/fs/vfs_cache_pressure
pode ser o valor a ser usado, mas não sei se o próprio diretório conta para o cache da página (porque é isso que é) ou para o cache do inode (porque, apesar de não ser um inode, são metadados FS e agrupados por esse motivo). Como eu disse, o ajuste da VM do Linux não é minha área. Jogue e veja o que ajuda.Obviamente não maçãs para maçãs aqui, mas eu configurei um pequeno teste e fiz o seguinte:
Criou 100.000 arquivos de 512 bytes em um diretório (
dd
e/dev/urandom
em um loop); esqueci de cronometrar, mas levou cerca de 15 minutos para criar esses arquivos.Execute o seguinte para excluir os arquivos mencionados:
ls -1 | wc -l && time find . -type f -delete
Esta é uma caixa Pentium 4 de 2,8 GHz (algumas centenas de GB de IDE 7200 RPM, eu acho; EXT3). Kernel 2.6.27.
fonte
rm
é terrivelmente lento em um grande número de arquivos, daí afind -delete
opção. Com um curinga no shell, ele expandirá cada nome de arquivo correspondente, e eu suponho que haja um buffer de memória limitado para isso, para que você possa ver como isso seria ineficiente.Às vezes, Perl pode fazer maravilhas em casos como este. Você já tentou se um script pequeno como esse poderia superar o bash e os comandos básicos do shell?
Ou outra abordagem, talvez ainda mais rápida, do Perl:
Edição: Eu apenas tentei meus scripts Perl. Quanto mais detalhado alguém faz algo certo. No meu caso, tentei isso com um servidor virtual com 256 MB de RAM e meio milhão de arquivos.
time find /test/directory | xargs rm
resultados:comparado com
fonte
*(oN)
]Pelo que me lembro, a exclusão de inodes nos sistemas de arquivos ext é O (n ^ 2); portanto, quanto mais arquivos você excluir, mais rápido o restante será.
Houve uma vez em que me deparei com um problema semelhante (embora minhas estimativas parecessem um tempo de exclusão de ~ 7h), no final a rota sugerida pelo jftuga no primeiro comentário .
fonte
Bem, esta não é uma resposta real, mas ...
Seria possível converter o sistema de arquivos para ext4 e ver se as coisas mudam?
fonte
Tudo bem, isso foi abordado de várias maneiras no restante do tópico, mas eu pensei em colocar meus dois centavos. O culpado de desempenho no seu caso provavelmente é o readdir. Você está recebendo de volta uma lista de arquivos que não são necessariamente seqüenciais no disco, o que está causando acesso ao disco em todo o lugar quando você desvincula. Os arquivos são pequenos o suficiente para que a operação de desvinculação provavelmente não salte demais zerando o espaço. Se você ler e depois classificar por inode crescente, provavelmente obterá melhor desempenho. Então readdir em ram (classificar por inode) -> desvincular -> lucro.
Inode é uma aproximação aproximada aqui, eu acho .. mas com base no seu caso de uso, pode ser bastante preciso ...
fonte
Eu provavelmente teria escolhido um compilador C e feito o equivalente moral do seu script. Ou seja, use
opendir(3)
para obter um identificador de diretório, usereaddir(3)
para obter o nome dos arquivos e, em seguida, registre os arquivos conforme eu os desassocio e de vez em quando imprima "% d arquivos excluídos" (e possivelmente o tempo decorrido ou o carimbo de data / hora atual).Eu não espero que seja visivelmente mais rápido que a versão do shell script, é só que eu tenho que rasgar o compilador de vez em quando, porque não há uma maneira limpa de fazer o que eu quero do shell ou porque enquanto praticável com casca, é improdutivamente lento dessa maneira.
fonte
Você provavelmente está tendo problemas de reescrita no diretório. Tente excluir os arquivos mais recentes primeiro. Veja as opções de montagem que adiarão o write-back para o disco.
Para uma barra de progresso, tente executar algo como
rm -rv /mystuff 2>&1 | pv -brtl > /dev/null
fonte
Aqui está como eu excluo os milhões de arquivos de rastreamento que às vezes podem se reunir em um grande servidor de banco de dados Oracle:
Acho que isso resulta em uma exclusão bastante lenta que tem baixo impacto no desempenho do servidor, geralmente algo como uma hora por milhão de arquivos em uma configuração "típica" de 10.000 IOPS.
Geralmente, leva alguns minutos para que os diretórios sejam verificados, a lista de arquivos inicial gerada e o primeiro arquivo seja excluído. A partir daí, a. é ecoado para cada arquivo excluído.
O atraso causado pelo eco no terminal provou ser um atraso suficiente para evitar qualquer carga significativa enquanto a exclusão está em andamento.
fonte
find /u* -maxdepth 3 -mindepth 3 -type d -path '*/app/*' -name diag -print0 | xargs -0I = find = -mindepth 4 -maxdepth 4 -type d -name 'trace' -print0 | xargs -0I = find = -mindepth 1 -maxdepth 1 -name '*.tr'
:? Adicione-delete
ao último para realmente excluir as coisas; conforme escrito, apenas lista o que excluiria. Observe que isso é otimizado para circunstâncias em que você tem muitas coisas desinteressantes em diretórios próximos; se não for esse o caso, você pode simplificar bastante a lógica.rm
acontece), então você tem relativamente eficiente I / O na inicialização disso, seguido por dolorosa, out-of-orderrm
s isso provavelmente não causa muita E / S, mas envolvescandir
andar repetidamente no diretório (não causando E / S porque já foi carregado no cache do bloco; veja tambémvfs_cache_pressure
). Se você quiser desacelerar as coisas,ionice
é uma opção, mas eu provavelmente usaria segundos fracionáriossleep
.find /u*/app/*/diag -path '*/trace/*.tr' -execdir rm {} +
executaria umrm
por diretório, para que você tivesse menos sobrecarga da CPU. Contanto que você tenha muito tempo de CPU disponível, limitando a E / S do disco, bifurcando umrm
processo inteiro para todas asunlink
obras, eu acho, mas é feio. o perl com uma suspensão por desvinculação seria melhor se a suspensão entrerm
diretórios inteiros por vez estiver muito cheia. (-execdir sh -c ...
Talvez)Você pode usar os recursos de paralelização 'xargs':
fonte
fonte
na verdade, este é um pouco melhor se o shell que você usa expandir a linha de comando:
fonte