Tricks för att hantera ett stort antal smÄ filer

Idén till artikeln föddes spontant ur en diskussion i kommentarerna till en artikel »NÄgot om inode«.

Tricks för att hantera ett stort antal smÄ filer

Problemet Àr att den interna specifikationen för vÄra tjÀnsters arbete innebÀr att vi lagrar ett enormt antal smÄ filer. För nÀrvarande har vi cirka hundratals terabyte av sÄdana data. Och vi har stött pÄ vissa uppenbara och mindre uppenbara fallgropar och har framgÄngsrikt tagit oss förbi dem.

DÀrför delar jag med mig av vÄr erfarenhet, kanske det kan vara till nytta för nÄgon.

Problem ett: »Inte tillrÀckligt med utrymme pÄ enheten«

Som nÀmnts i den ovan nÀmnda artikeln Àr problemet att det finns lediga block pÄ filsystemet, men inoderna Àr slut.

Antalet anvÀnda och lediga inoder kan kontrolleras med kommandot df -ih:

Tricks för att hantera ett stort antal smÄ filer

Jag tĂ€nker inte sammanfatta artikeln, men kort sagt, pĂ„ disken finns det bĂ„de block direkt för data och block för meta-information, som ocksĂ„ kallas inoder (index-nod). Deras antal anges vid initiering av filsystemet (det handlar om ext2 och dess efterföljare) och Ă€ndras inte efter det. Balansen mellan datablock och inoder berĂ€knas utifrĂ„n genomsnittliga data; i vĂ„rt fall, nĂ€r det finns mĂ„nga smĂ„ filer, bör balansen luta mer Ă„t antalet inoder—de bör vara fler.

I Linux finns redan alternativ med olika balans, och alla dessa förberÀknade konfigurationer finns i filen /etc/mke2fs.conf.
DÀrför kan rÀtt profil anges vid den första initialiseringen av filsystemet genom mke2fs.

HÀr Àr nÄgra exempel frÄn filen:

    small = {
        blocksize = 1024
        inode_size = 128
        inode_ratio = 4096
    }

    big = {
        inode_ratio = 32768
    }

    largefile = {
        inode_ratio = 1048576
        blocksize = -1
    }

Det valda alternativet kan anges med alternativet »-T« nÀr man anropar mke2fs. Man kan ocksÄ manuellt ange nödvÀndiga parametrar om det inte finns en fÀrdig lösning.

Mer information finns i manualerna för mke2fs.conf och mke2fs.

En sÀrskild egenskap som inte berördes i den ovan nÀmnda artikeln Àr att blockstorlek för data kan anges. Uppenbarligen finns det för stora filer en fördel med en större blockstorlek, för smÄfiler en mindre.

Det Àr dock vÀrt att ta hÀnsyn till en intressant funktion, nÀmligen processorarkitekturen.
Jag funderade en gÄng pÄ att jag behövde en större blockstorlek för stora fotofiler. Det var under hemaförhÄllanden, pÄ en hemlagringslösning av WD byggd pÄ ARM-arkitektur. Utan att tÀnka för mycket, stÀllde jag blockstorleken till antingen 8k eller 16k istÀllet för standard 4k, efter att ha mÀtt fördelen. Och allt var utmÀrkt precis tills lagringslösningen gick sönder, samtidigt som disken var intakt. NÀr jag satte disken i en vanlig dator med en vanlig Intel-processor fick jag en överraskning: en icke-stödd blockstorlek. Vilken överraskning. Data fanns, allt var bra, men det gick inte att lÀsa. i386-processorer och liknande klarar inte av blockstorlekar som inte motsvarar minnessidans storlek, som Àr exakt 4k. I allmÀnhet slutade det med att vi anvÀnde verktyg frÄn anvÀndarrummet, allt var lÄngsamt och sorgligt, men vi rÀddade data. För den som Àr intresserad - googla namnet pÄ verktyget. fuseext2. Moralen: antingen tÀnker man igenom alla scenarier pÄ förhand, eller sÄ försöker man inte vara en superhjÀlte och anvÀnder standardinstÀllningar för hushÄll.

UPD. PÄ anvÀndarens anmÀrkning berez förtydligar jag att för i386 fÄr blockstorleken inte överstiga 4k, men den behöver inte nödvÀndigtvis vara exakt 4k, sÄ 1k och 2k Àr godkÀnda.

SÄ hÀr löste vi problemen.

För det första stötte vi pÄ problemet nÀr den multiterabyte disken var full av data och vi inte kunde Àndra konfigurationen av filsystemet.

För det andra behövdes en snabb lösning.

Till slut kom vi fram till att vi var tvungna att Àndra balansen och minska antalet filer.
För att minska antalet filer beslutade vi att packa filerna i ett gemensamt arkiv. Med tanke pÄ vÄr specifik profil packade vi alla filer frÄn en viss tidsperiod i ett arkiv och genomförde arkivering med en cron-jobb varje natt.

Vi valde zip-arkivet. I kommentarerna till den tidigare artikeln föreslogs tar, men det finns en svÄrighet: det har ingen katalog, och filerna i det Àr sekventiella (inte för inte som "tar" Àr en förkortning av "Tape Archive", ett arv frÄn bandlagring), dvs. om du behöver lÀsa en fil i slutet av arkivet - mÄste du lÀsa hela arkivet, eftersom det inte finns nÄgra förskjutningar för varje fil i förhÄllande till början av arkivet. DÀrför Àr det en lÄngsam operation. Med zip Àr allt mycket bÀttre: det har just den katalogen och filernas förskjutningar inuti arkivet, och Ätkomsttiden till varje fil beror inte pÄ dess placering. I vÄrt fall kunde vi stÀlla in komprimeringsalternativet "0" eftersom alla filer redan var komprimerade i gzip.

Kunder hÀmtar filer via nginx, och enligt det gamla API:t anges bara filnamnet, till exempel sÄ hÀr:

http://www.server.com/hydra/20170416/0453/3bd24ae7-1df4-4d76-9d28-5b7fcb7fd8e5

För att i realtid packa upp filer, hittade och kopplade vi modulen nginx-unzip-module (https://github.com/youzee/nginx-unzip-module) och konfigurerade tvÄ upstreams.

Resultatet blev en sÄdan konfiguration:

Tricks för att hantera ett stort antal smÄ filer

De tvÄ vÀrdarna i instÀllningarna sÄg ut sÄ hÀr:

server {
  listen *:8081;

  location / {
    root      /home/filestorage;
  }
}

server {
  listen *:8082;

  location ~ ^/hydra/(\d+)/(\d+)/(.*)$ {
    root      /home/filestorage;
    file_in_unzip_archivefile "/home/filestorage/hydra/$1/$2.zip";
    file_in_unzip_extract "$2/$3";
    file_in_unzip;
  }
}

Och upstream-konfigurationen pÄ den överordnade nginx:

upstream storage {
  server server.com:8081;
  server server.com:8082;
}

Hur det fungerar:

  • Klienten gĂ„r till front nginx
  • Front nginx försöker ge filen frĂ„n den första upstreamen, dvs. direkt frĂ„n filsystemet
  • Om filen inte finns - försöker den ge frĂ„n den andra upstreamen, som försöker hitta filen inuti arkivet

Det andra problemet: Äterigen "No space left on device"

Detta Àr det andra problemet vi stötte pÄ nÀr det finns mÄnga filer i katalogen.
Vi försöker skapa en fil, systemet klagar pÄ att det inte finns nÄgot utrymme kvar. Vi Àndrar filnamnet och försöker skapa den igen.

Det fungerar.

Det ser ut ungefÀr sÄ hÀr:

Tricks för att hantera ett stort antal smÄ filer

Kontroll av inodes gav inget resultat - det finns mÄnga lediga.
Kontroll av utrymmet - samma sak.
Vi tĂ€nkte att det kanske Ă€r för mĂ„nga filer i katalogen, och det finns en begrĂ€nsning för detta, men Ă„terigen nej: Maximum number of files per directory: ~1.3 × 10^20

Och man kan skapa en fil, om man Àndrar namnet.
Slutsatsen - problemet ligger i filnamnet.

Ytterligare undersökningar visade att problemet ligger i hash-algoritmen vid konstruktion av katalogindexet, med ett stort antal filer förekommer kollisioner med alla följande konsekvenser. Du kan lÀsa mer om detta hÀr: https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout#Hash_Tree_Directories

Du kan stÀnga av det hÀr alternativet, men... att söka efter filer med namn kan bli oförutsÀgbart lÄngsamt nÀr man gÄr igenom alla filer.

 tune2fs -O "^dir_index" /dev/sdb3

Överlag kan det fungera som en tillfĂ€llig lösning.

Moral: MÄnga filer i en katalog Àr vanligtvis dÄligt. SÄ gör man inte.

I sÄdana fall skapar man vanligtvis underkataloger, antingen efter de första bokstÀverna i filnamnet eller efter nÄgot annat kriterium, till exempel datum. I de flesta fall rÀddar det situationen.
Men det totala antalet smĂ„ filer Ă€r fortfarande dĂ„ligt, Ă€ven om man delar upp dem i kataloger – dĂ„ hĂ€nvisar vi till det första problemet.

Problem tre: Hur ser man en lista med filer om det finns mÄnga?

I vÄr situation, dÀr vi har mÄnga filer, har vi hur som helst stött pÄ problemet att se innehÄllet i katalogen.

Den standardlösning som anvÀnds Àr kommandot ls.
Okej, lÄt oss se vad som hÀnder med 4772098 filer:


$ time ls /home/app/express.repository/offercache/ >/dev/null

real	0m30.203s
user	0m28.327s
sys	0m1.876s

30 sekunder... det Àr lite för mycket. Huvudtiden gÄr Ät till att bearbeta filer i anvÀndarrummet, snarare Àn att kÀrnan arbetar.

Men det finns en lösning:


$ time find /home/app/express.repository/offercache/ >/dev/null

real	0m3.714s
user	0m1.998s
sys	0m1.717s

3 sekunder. 10 gÄnger snabbare.
Hurra!

UPD.

Ännu en snabbare lösning frĂ„n en anvĂ€ndare berez – stĂ€ng av sortering för ls


time ls -U /home/app/express.repository/offercache/ >/dev/null
real	0m2.985s
user	0m1.377s
sys	0m1.608s

Problem fyra: hög LA nÀr man arbetar med filer

Det uppstÄr ibland situationer dÀr man behöver kopiera mÄnga filer frÄn en maskin till en annan. I dessa fall kan LA öka dramatiskt, eftersom det beror pÄ prestandan hos sjÀlva diskarna.

Det mest rationella vore att anvÀnda SSD. Verkligen fantastiskt. FrÄgan Àr bara kostnaden för flera terabyte SSD.

Men om diskarna Àr vanliga, mÄste man kopiera filer, och det hÀr Àr dessutom ett produktsystem dÀr överbelastning leder till missnöje frÄn kunderna? Det finns Ätminstone tvÄ anvÀndbara verktyg: trevlig och ionice.

trevlig – minskar processens prioritet, vilket gör att schemalĂ€ggaren ger mer tid till andra, mer prioriterade processer.
I vĂ„r praktik har det hjĂ€lpt att stĂ€lla in nice till max (19 – detta Ă€r minimi-prioritet, -20 (minus 20) – max).

ionice – justerar prioriteten för in- och utdata (I/O-schemalĂ€ggning)

Om du anvÀnder RAID och det plötsligt behöver synkroniseras (efter en misslyckad omstart eller för att ÄterstÀlla RAID-arrayen efter diskbyte), kan det i vissa situationer vara meningsfullt att minska synkroniseringshastigheten sÄ att andra processer kan fungera mer eller mindre adekvat. För detta kan följande kommando hjÀlpa:


echo 1000 > /proc/sys/dev/raid/speed_limit_max

Problem fem: Hur man synkroniserar filer i realtid

Vi har alla dessa enorma mĂ€ngder filer som mĂ„ste sĂ€kerhetskopieras till en andra server för att undvika
 Filer skrivs stĂ€ndigt, dĂ€rför, för att minimera förluster, mĂ„ste de kopieras sĂ„ snabbt som möjligt.

Standardlösning: Rsync över SSH.

Det Ă€r ett bra alternativ om det inte behöver göras var nĂ„gra sekunder. Det finns mĂ„nga filer. Även om vi inte kopierar dem - mĂ„ste vi fortfarande pĂ„ nĂ„got sĂ€tt förstĂ„ vad som har Ă€ndrats, och att jĂ€mföra flera miljoner filer tar tid och belastning pĂ„ diskarna.

Det vill sÀga, vi mÄste veta direkt vad som ska kopieras, utan att starta en jÀmförelse varje gÄng.

Lösningen Ă€r - lsyncd. Lsyncd — Live Syncing (Mirror) Daemon. Det fungerar ocksĂ„ via rsync, men monitorerar dessutom filsystemet för förĂ€ndringar med hjĂ€lp av inotify och fsevents och startar kopiering bara för de filer som har tillkommit eller Ă€ndrats.

Problem sex: hur man förstÄr vem som belastar diskarna

Det vet nog alla, men för fullstĂ€ndighetens skull: för att övervaka diskundersystemet finns det kommandot iotop – liknande top, men visar de processer som mest aktivt anvĂ€nder diskarna.

Tricks för att hantera ett stort antal smÄ filer

För övrigt kan den gamla hederliga top ocksÄ hjÀlpa till att förstÄ om det finns problem med diskarna eller inte. För detta finns tvÄ sÀrskilt lÀmpliga parametrar: Load Average och IOwait.

Tricks för att hantera ett stort antal smÄ filer

Den första visar hur mÄnga processer som stÄr i kö för service, vanligtvis mer Àn 2 - nÄgot gÄr redan fel. Vid aktiv kopiering till backupservrar tillÄter vi upp till 6-8, efter detta anses situationen vara onormal.

Den andra - hur mycket processorn Àr upptagen med diskoperationer. IOwait >10% - orsaken till oro, Àven om vi pÄ servrar med specifik belastningsprofil har stabilt 40-50%, och det Àr faktiskt normen.

Jag avslutar hÀr, Àven om det sÀkert finns mÄnga moment som vi inte har stött pÄ, ser jag fram emot kommentarer och beskrivningar av intressanta verkliga fall.

KĂ€lla: habr.com

Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar đŸ”„ Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar | ProHoster