contribuição única da pasta para o uso do disco

4

Eu tenho um backup contendo pastas para instantâneos diários. Para economizar espaço, arquivos idênticos em snapshots diferentes são desduplicados através de hard links (gerados pelo rsync).

Quando estou ficando sem espaço, uma opção é excluir os instantâneos mais antigos. Mas por causa dos hard links, é difícil descobrir quanto espaço eu ganharia ao excluir um determinado snapshot.

Uma opção que posso imaginar seria usar du -s primeiro em todas as pastas de instantâneos, depois em todas, exceto a que eu poderia excluir, e a diferença me daria o espaço esperado esperado. No entanto, isso é bastante complicado e teria que ser repetido quando estou tentando encontrar um instantâneo adequado para exclusão.

Existe uma maneira mais fácil?

Depois de experimentar e pensar nas respostas de Stéphane Chazelas e derobert Percebi que minha pergunta não era precisa o suficiente. Aqui está uma tentativa de ser mais preciso:

Eu tenho um conjunto de diretórios ("snapshots") que contém arquivos que são parcialmente idênticos ao armazenamento (link físico) com arquivos em outro snapshot. Eu estou procurando uma solução que me dá uma lista dos instantâneos e para cada a quantidade de armazenamento em disco usado pelos arquivos nele, mas sem esse armazenamento que também é usado por um arquivo em outro instantâneo. Eu gostaria de permitir a possibilidade de haver hard links em cada instantâneo.

A ideia é que eu possa olhar para essa lista para decidir qual dos snapshots eu deveria deletar quando ficar sem espaço, o que é um trade-off entre espaço de armazenamento ganho por exclusão e valor do snapshot (por exemplo, baseado em idade).

    
por A. Donda 31.10.2018 / 21:02

3 respostas

2

Você poderia fazer isso manualmente com o GNU find :

find snapshot-dir -type d -printf '1 %b\n' -o -printf '%n %b %i\n' |
   awk '$1 == 1 || ++c[$3] == $1 {t+=$2;delete c[$3]}
   END{print t*512}'

Isso conta o uso do disco de arquivos cuja contagem de links cairia para 0 após todos os links encontrados no diretório de snapshots terem sido encontrados.

find impressões:

  • 1 <disk-usage> para diretórios
  • <link-count> <disk-usage> <inode-number> para outros tipos de arquivos.

Pretendemos que a contagem de links seja sempre uma para diretórios, porque, quando na prática não é, é por causa das entradas .. e find não lista essas entradas, e os diretórios geralmente não têm outros hardlinks .

A partir dessa saída, awk conta o uso do disco das entradas que possuem contagem de links de 1 e também dos inodes que ele viu <link-count> times (são aqueles cujos hard links estão no diretório atual e assim, como aqueles com uma contagem de links de um, seu espaço seria recuperado assim que a árvore de diretórios fosse excluída).

Você também pode usar find snapshot-dir1 snapshot-dir2 para descobrir quanto espaço em disco seria recuperado se ambas as pastas fossem removidas (o que pode ser mais do que a soma do espaço para os dois diretórios considerados individualmente se houver arquivos encontrados em ambos e apenas nesses instantâneos).

Se você quiser saber quanto espaço você salvaria após cada exclusão de snapshot-dir (de forma acumulada), você poderia fazer:

find snapshot-dir* \( -path '*/*' -o -printf "%p:\n" \) \
  -type d -printf '1 %b\n' -o -printf '%n %b %i\n' |
   awk '/:$/ {if (NR>1) print t*512; printf "%s ", $0; next}
        $1 == 1 || ++c[$3] == $1 {t+=$2;delete c[$3]}
        END{print t*512}'

Isso processa a lista de instantâneos em ordem lexical. Se você o processou em uma ordem diferente, isso provavelmente forneceria números diferentes, exceto o final (quando todos os instantâneos forem removidos).

Veja numfmt para tornar os números mais legíveis.

Isso pressupõe que todos os arquivos estejam no mesmo sistema de arquivos. Se não, você pode substituir %i por %D:%i (se eles não estiverem todos no mesmo sistema de arquivos, isso significaria que você teria um ponto de montagem lá que você não poderia remover de qualquer maneira).

    
por 31.10.2018 / 22:54
1

Se os nomes dos seus arquivos não contiverem caracteres padrão ou novas linhas, você poderá usar o recurso de exclusão find + du para fazer isso:

find -links +1 -type f \
    | cut -d/ -f2- \
    | du --exclude-from=- -s *

O find bit obtém todos os arquivos ( -type f ) com uma contagem de hardlink maior que 1 ( -links +1 ). O cut reduz o ./ principal imprime. Em seguida, du é solicitado a usar o disco de todos os diretórios, excluindo todos os arquivos com vários links. É claro que, depois de excluir um instantâneo, é possível que agora haja arquivos com apenas um link que anteriormente possuíam dois - portanto, a cada poucas exclusões, você realmente deveria executá-lo novamente.

Se for necessário trabalhar com nomes de arquivos arbitrários, será necessário mais alguns scripts para substituir du (esses são padrões de shell, portanto, não é possível escapar).

Além disso, como aponta Stéphane Chazelas, se houver hardlinks dentro de um snapshot (todos os nomes do arquivo residem em um único snapshot, não hardlinks entre snapshots), esses arquivos serão excluídos dos totais (mesmo se excluídos) o instantâneo recuperaria esse espaço).

    
por 31.10.2018 / 21:33
0

Desde que escrevi esta resposta, Stéphane Chazelas me convenceu de que sua resposta estava certa o tempo todo. Deixo minha resposta incluindo código porque também funciona bem e fornece algumas impressões bonitas. Sua saída é assim:

              total               unique
--T---G---M---k---B  --T---G---M---k---B
     91,044,435,456          665,754,624  back-2018-03-01T06:00:01
     91,160,015,360          625,541,632  back-2018-04-01T06:00:01
     91,235,970,560          581,360,640  back-2018-05-01T06:00:01
     91,474,846,208          897,665,536  back-2018-06-01T06:00:01
     91,428,597,760          668,853,760  back-2018-07-01T06:00:01
     91,602,767,360          660,594,176  back-2018-08-01T06:00:01
     91,062,218,752        1,094,236,160  back-2018-09-01T06:00:01
    230,810,647,552       50,314,291,712  back-2018-11-01T06:00:01
    220,587,811,328          256,036,352  back-2018-11-12T06:00:01
    220,605,425,664          267,876,352  back-2018-11-13T06:00:01
    220,608,163,328          268,711,424  back-2018-11-14T06:00:01
    220,882,714,112          272,000,000  back-2018-11-15T06:00:01
    220,882,118,656          263,202,304  back-2018-11-16T06:00:01
    220,882,081,792          263,165,440  back-2018-11-17T06:00:01
    220,894,113,280          312,208,896  back-2018-11-18T06:00:01

Desde que eu não estava 100% feliz com qualquer uma das duas respostas (a partir de 2018-11-18) - embora eu aprendi com os dois - eu criei minha própria ferramenta e estou publicando aqui.

Semelhante a Stéphane Chazelas ' Como resposta, ele usa find para obter uma lista de inodes e tamanhos de arquivos / diretórios associados, , mas não depende da heurística "no máximo um link". Em vez disso, cria uma lista de inodes exclusivos (não arquivos / diretórios!) Para cada diretório de entrada, filtra os inodes dos outros diretórios e soma os tamanhos dos inodes remanescentes. Desta forma, é responsável por possíveis hardlinks dentro de cada diretório de entrada. Como efeito colateral, desconsidera possíveis hardlinks externos ao conjunto de diretórios de entrada.

Ferramentas externas bash usadas: find , xargs , mktemp , sort , tput , awk , tr , numfmt , touch , cat , comm , rm . Eu sei, não exatamente leve, mas faz exatamente o que eu quero fazer. Compartilho aqui caso alguém tenha necessidades semelhantes.

Se qualquer coisa puder ser feita de forma mais eficiente ou infalível, os comentários serão bem-vindos! Eu sou tudo menos um mestre bash.

Para usá-lo, salve o seguinte código em um arquivo de script duu.sh . Uma instrução de uso curto está contida no primeiro bloco de comentário.

#!/bin/bash

# duu
#
# disk usage unique to a directory within a set of directories
#
# Call with a list of directory names. If called without arguments,
# it operates on the subdirectories of the current directory.


# no arguments: call itself with subdirectories of .
if [ "$#" -eq 0 ]
then
    exec find . -maxdepth 1 -type d ! -name . -printf '%P
              total               unique
--T---G---M---k---B  --T---G---M---k---B
     91,044,435,456          665,754,624  back-2018-03-01T06:00:01
     91,160,015,360          625,541,632  back-2018-04-01T06:00:01
     91,235,970,560          581,360,640  back-2018-05-01T06:00:01
     91,474,846,208          897,665,536  back-2018-06-01T06:00:01
     91,428,597,760          668,853,760  back-2018-07-01T06:00:01
     91,602,767,360          660,594,176  back-2018-08-01T06:00:01
     91,062,218,752        1,094,236,160  back-2018-09-01T06:00:01
    230,810,647,552       50,314,291,712  back-2018-11-01T06:00:01
    220,587,811,328          256,036,352  back-2018-11-12T06:00:01
    220,605,425,664          267,876,352  back-2018-11-13T06:00:01
    220,608,163,328          268,711,424  back-2018-11-14T06:00:01
    220,882,714,112          272,000,000  back-2018-11-15T06:00:01
    220,882,118,656          263,202,304  back-2018-11-16T06:00:01
    220,882,081,792          263,165,440  back-2018-11-17T06:00:01
    220,894,113,280          312,208,896  back-2018-11-18T06:00:01
' | sort -z \ | xargs -r --null "$0" exit fi # create temporary directory T='mktemp -d' # array of directory names dirs=("$@") # number of directories n="$#" # for each directory, create list of (unique) inodes with size for i in $(seq 1 $n) do echo -n "reading $i/$n: ${dirs[$i - 1]} " find "${dirs[$i - 1]}" -printf "%i\t%b\n" | sort -u > "$T/$i" # find %b: "The amount of disk space used for this file in 512-byte blocks." echo -ne "\r" tput el done # print header echo " total unique" echo "--T---G---M---k---B --T---G---M---k---B" # for each directory for i in $(seq 1 $n) do # compute and print total size # sum block sizes and multiply by 512 awk '{s += $2} END{printf "%.0f", s * 512}' "$T/$i" \ | tr -d '\n' \ | numfmt --grouping --padding 19 echo -n " " # compute and print unique size # create list of (unique) inodes in the other directories touch "$T/o$i" for j in $(seq 1 $n) do if [ "$j" -ne "$i" ] then cat "$T/$j" >> "$T/o$i" fi done sort -o "$T/o$i" -u "$T/o$i" # create list of (unique) inodes that are in this but not in the other directories comm -23 "$T/$i" "$T/o$i" > "$T/u$i" # sum block sizes and multiply by 512 awk '{s += $2} END{printf "%.0f", s * 512}' "$T/u$i" \ | tr -d '\n' \ | numfmt --grouping --padding 19 # append directory name echo " ${dirs[$i - 1]}" done # remove temporary files rm -rf "$T"
    
por 18.11.2018 / 02:33