Latar belakang: server fisik, sekitar dua tahun, drive SATA 7200-RPM terhubung ke kartu RAID 3Ware, noatime yang dipasang di ext3 FS dan data = dipesan, bukan di bawah beban gila, kernel 2.6.18-92.1.22.el5, uptime 545 hari . Direktori tidak mengandung subdirektori, hanya jutaan file kecil (~ 100 byte), dengan beberapa yang lebih besar (beberapa KB).
Kami memiliki server yang sudah agak cuckoo selama beberapa bulan terakhir, tetapi kami baru menyadarinya pada hari lain ketika server mulai tidak dapat menulis ke direktori karena mengandung terlalu banyak file. Secara khusus, itu mulai melempar kesalahan ini di / var / log / messages:
ext3_dx_add_entry: Directory index full!
Disk yang dimaksud memiliki banyak inode yang tersisa:
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/sda3 60719104 3465660 57253444 6% /
Jadi saya menduga itu berarti kita mencapai batas berapa banyak entri dapat di file direktori itu sendiri. Tidak tahu berapa banyak file yang akan, tetapi tidak bisa lebih, seperti yang Anda lihat, lebih dari tiga juta. Bukan itu bagus, ingatlah! Tapi itu bagian pertama dari pertanyaan saya: persis apa batas atas itu? Apakah bisa merdu? Sebelum saya dimarahi - saya ingin meremehkannya ; direktori besar ini menyebabkan segala macam masalah.
Bagaimanapun, kami melacak masalah dalam kode yang menghasilkan semua file itu, dan kami telah memperbaikinya. Sekarang saya terjebak dengan menghapus direktori.
Beberapa opsi di sini:
rm -rf (dir)
Saya mencoba ini dulu. Saya menyerah dan membunuhnya setelah berjalan selama satu setengah hari tanpa dampak yang jelas.
- batalkan tautan (2) pada direktori: Pasti layak dipertimbangkan, tetapi pertanyaannya adalah apakah akan lebih cepat untuk menghapus file di dalam direktori melalui fsck daripada menghapus melalui batalkan tautan (2). Artinya, dengan satu atau lain cara, saya harus menandai inode tersebut sebagai tidak terpakai. Ini mengasumsikan, tentu saja, bahwa saya dapat memberitahu fsck untuk tidak menjatuhkan entri ke file in / lost + found; jika tidak, saya baru saja memindahkan masalah saya. Selain semua kekhawatiran lain, setelah membaca tentang ini sedikit lebih banyak, ternyata saya mungkin harus memanggil beberapa fungsi FS internal, karena tidak ada varian unlink (2) yang dapat saya temukan yang akan memungkinkan saya untuk menghapus dengan blithely direktori dengan entri di dalamnya. Pooh.
while [ true ]; do ls -Uf | head -n 10000 | xargs rm -f 2>/dev/null; done )
Ini sebenarnya adalah versi singkat; yang sebenarnya saya jalankan, yang hanya menambahkan beberapa pelaporan kemajuan dan berhenti bersih ketika kita kehabisan file untuk dihapus, adalah:
ekspor i = 0; waktu (sementara [benar]; lakukan ls -Uf | kepala -n 3 | grep -qF '.png' || istirahat; ls -Uf | kepala -n 10000 | xargs rm -f 2> / dev / null; ekspor i = $ (($ i + 10000)); gema "$ i ..."; Selesai )
Ini sepertinya bekerja dengan baik. Saat saya menulis ini, ia telah menghapus 260.000 file dalam sekitar tiga puluh menit terakhir.
- Seperti disebutkan di atas, apakah batas entri per-direktori dapat diatur?
- Mengapa diperlukan "real 7m9.561s / pengguna 0m0.001s / sys 0m0.001s" untuk menghapus satu file yang merupakan yang pertama dalam daftar yang dikembalikan oleh
ls -U
, dan mungkin butuh sepuluh menit untuk menghapus 10.000 entri pertama dengan perintah di # 3, tapi sekarang sudah cukup senang? Untuk itu, itu dihapus 260.000 dalam waktu sekitar tiga puluh menit, tetapi sekarang butuh lima belas menit lagi untuk menghapus 60.000 lebih. Mengapa ayunan besar dalam kecepatan? - Apakah ada cara yang lebih baik untuk melakukan hal semacam ini? Tidak menyimpan jutaan file dalam direktori; Saya tahu itu konyol, dan itu tidak akan terjadi pada jam tangan saya. Menelusuri masalah dan melihat melalui SF dan SO menawarkan banyak variasi
find
yang tidak akan secara signifikan lebih cepat daripada pendekatan saya karena beberapa alasan yang jelas. Tapi apakah ide delete-via-fsck punya kaki? Atau sesuatu yang lain sama sekali? Saya ingin sekali mendengar pemikiran out-of-the-box (atau di dalam-tidak-terkenal).
Output skrip akhir !:
2970000...
2980000...
2990000...
3000000...
3010000...
real 253m59.331s
user 0m6.061s
sys 5m4.019s
Jadi, tiga juta file dihapus dalam waktu lebih dari empat jam.
rm -rfv | pv -l >/dev/null
. pv harus tersedia di repositori EPEL .Jawaban:
The
data=writeback
mount option layak untuk dicoba, untuk mencegah journal dari sistem file. Ini harus dilakukan hanya selama waktu penghapusan, namun ada risiko jika server sedang dimatikan atau di-reboot selama operasi penghapusan.Menurut halaman ini ,
Opsi ini diatur dalam
fstab
atau selama operasi pemasangan, digantidata=ordered
dengandata=writeback
. Sistem file yang berisi file yang akan dihapus harus di-remount.sumber
commit
opsi : "Nilai default ini (atau nilai rendah apa pun) akan merusak kinerja, tetapi bagus untuk keamanan data. Mengaturnya ke 0 akan memiliki efek yang sama dengan membiarkannya pada default (5 detik). Pengaturan ke nilai yang sangat besar akan meningkatkan kinerja ".data=writeback
masih jurnal metadata sebelum menulisnya ke sistem file utama. Seperti yang saya pahami, itu hanya tidak memaksakan pemesanan antara hal-hal seperti menulis peta luas dan menulis data ke luasan tersebut. Mungkin ada kendala pemesanan lain yang membuatnya rileks juga, jika Anda melihat keuntungan dari hal ini. Tentu saja, pemasangan tanpa jurnal sama sekali bisa menjadi kinerja yang lebih tinggi. (Mungkin membiarkan perubahan metadata terjadi begitu saja di RAM, tanpa perlu memiliki apa pun pada disk sebelum op tautan keluar selesai).Sementara penyebab utama masalah ini adalah kinerja ext3 dengan jutaan file, penyebab sebenarnya dari masalah ini berbeda.
Ketika direktori perlu terdaftar readdir () dipanggil pada direktori yang menghasilkan daftar file. readdir adalah panggilan posix, tetapi panggilan sistem Linux yang digunakan di sini disebut 'getdents'. Entri daftar direktori entri dengan mengisi buffer dengan entri.
Masalahnya terutama karena fakta bahwa readdir () menggunakan ukuran buffer tetap sebesar 32Kb untuk mengambil file. Ketika direktori semakin besar dan lebih besar (ukurannya bertambah ketika file ditambahkan) ext3 semakin lambat untuk mengambil entri dan ukuran buffer 32Kb readdir tambahan hanya cukup untuk memasukkan sebagian kecil dari entri dalam direktori. Ini menyebabkan readdir berulang-ulang dan memanggil system call yang mahal berulang kali.
Sebagai contoh, pada direktori pengujian yang saya buat dengan lebih dari 2,6 juta file di dalamnya, menjalankan "ls -1 | wc-l" menunjukkan output strace besar dari banyak panggilan sistem getdent.
Selain itu waktu yang dihabiskan dalam direktori ini sangat signifikan.
Metode untuk menjadikan ini proses yang lebih efisien adalah dengan memanggil getdents secara manual dengan buffer yang jauh lebih besar. Ini meningkatkan kinerja secara signifikan.
Sekarang, Anda tidak seharusnya memanggil getdents sendiri secara manual sehingga tidak ada antarmuka untuk menggunakannya secara normal (lihat halaman manual untuk getdents untuk melihat!), Namun Anda dapat memanggilnya secara manual dan membuat doa pemanggilan sistem Anda lebih efisien.
Ini secara drastis mengurangi waktu yang diperlukan untuk mengambil file-file ini. Saya menulis sebuah program yang melakukan ini.
Meskipun ini tidak memerangi masalah mendasar yang mendasarinya (banyak file, dalam sistem file yang berkinerja buruk dalam hal itu). Kemungkinan besar, jauh lebih cepat daripada banyak alternatif yang diposting.
Sebagai pemikiran, seseorang harus menghapus direktori yang terkena dampak dan membuat ulang setelahnya. Direktori hanya bertambah ukurannya dan dapat tetap berkinerja buruk bahkan dengan beberapa file di dalamnya karena ukuran direktori.
Sunting: Saya sudah membersihkan ini sedikit. Menambahkan opsi untuk memungkinkan Anda menghapus pada baris perintah saat runtime dan menghapus banyak hal treewalk yang, jujur melihat ke belakang dipertanyakan di terbaik. Juga terbukti menghasilkan korupsi memori.
Sekarang Anda bisa melakukannya
dentls --delete /my/path
Hasil baru. Didasarkan pada direktori dengan 1,82 juta file.
Terkejut ini masih bekerja dengan baik!
sumber
[256]
mungkin seharusnya[FILENAME_MAX]
, dan dua, Linux saya (2.6.18 == CentOS 5.x) tampaknya tidak memasukkan entri d_type dalam direktori (setidaknya menurut getdents (2)).Apakah mungkin untuk membuat cadangan semua file lain dari sistem file ini ke lokasi penyimpanan sementara, memformat ulang partisi, dan kemudian mengembalikan file?
sumber
Tidak ada batas per direktori file dalam ext3 hanya batas inode filesystem (saya pikir ada batas pada jumlah subdirektori meskipun).
Anda mungkin masih mengalami masalah setelah menghapus file.
Ketika sebuah direktori memiliki jutaan file, entri direktori itu sendiri menjadi sangat besar. Entri direktori harus dipindai untuk setiap operasi penghapusan, dan itu membutuhkan berbagai jumlah waktu untuk setiap file, tergantung di mana entri itu berada. Sayangnya bahkan setelah semua file dihapus, entri direktori mempertahankan ukurannya. Jadi operasi lebih lanjut yang memerlukan pemindaian entri direktori masih akan memakan waktu lama bahkan jika direktori sekarang kosong. Satu-satunya cara untuk mengatasi masalah itu adalah mengubah nama direktori, membuat yang baru dengan nama lama, dan mentransfer file yang tersisa ke yang baru. Kemudian hapus yang diganti namanya.
sumber
Saya belum membandingkannya, tetapi orang ini melakukannya :
sumber
temukan sama sekali tidak bekerja untuk saya, bahkan setelah mengubah parameter fs ext3 seperti yang disarankan oleh pengguna di atas. Memori yang dikonsumsi terlalu banyak. Script PHP ini melakukan trik - penggunaan CPU yang cepat dan tidak signifikan, penggunaan memori yang tidak signifikan:
Saya memposting laporan bug mengenai masalah ini dengan menemukan: http://savannah.gnu.org/bugs/?31961
sumber
Saya baru-baru ini menghadapi masalah yang sama dan tidak bisa mendapatkan
data=writeback
saran ring0 untuk bekerja (mungkin karena fakta bahwa file-file tersebut ada di partisi utama saya). Saat mencari solusi, saya menemukan ini:Ini akan mematikan penjurnalan sepenuhnya, terlepas dari
data
opsi yang diberikan untukmount
. Saya menggabungkan ini dengannoatime
dan volume telahdir_index
diatur, dan tampaknya bekerja dengan cukup baik. Penghapusan benar-benar selesai tanpa saya perlu membunuhnya, sistem saya tetap responsif, dan sekarang kembali aktif dan berjalan (dengan penjurnalan kembali aktif) tanpa masalah.sumber
Pastikan Anda melakukannya:
yang seharusnya mempercepat sedikit juga.
sumber
Perintahnya sangat lambat. Mencoba:
sumber
strace -r -p <pid of rm>
untuk melampirkan ke proses rm yang sudah berjalan. Kemudian Anda dapat melihat seberapa cepatunlink
panggilan sistem bergulir. (-r
Menempatkan waktu sejak system call sebelumnya di awal setiap baris.)Sudah
dir_index
diatur untuk sistem file? (tune2fs -l | grep dir_index
) Jika tidak, aktifkan. Biasanya aktif untuk RHEL baru.sumber
Beberapa tahun yang lalu, saya menemukan direktori dengan 16 juta file XML di sistem
/
file. Karena kritikan server, kami menggunakan perintah berikut yang memakan waktu sekitar 30 jam untuk menyelesaikan:Itu adalah hdd 7200 rpm lama , dan meskipun hambatan IO dan paku CPU, server web lama melanjutkan layanannya.
sumber
Opsi pilihan saya adalah pendekatan newfs, sudah disarankan. Masalah dasarnya adalah, sekali lagi seperti yang sudah dicatat, pemindaian linier untuk menangani penghapusan bermasalah.
rm -rf
harus mendekati optimal untuk sistem file lokal (NFS akan berbeda). Tetapi pada jutaan file, 36 byte per nama file dan 4 per inode (tebakan, tidak memeriksa nilai untuk ext3), itu 40 * jutaan, harus disimpan dalam RAM hanya untuk direktori.Pada tebakan, Anda meronta-ronta memori cache metadata sistem file di Linux, sehingga blok untuk satu halaman file direktori sedang dihapus sementara Anda masih menggunakan bagian lain, hanya untuk menekan halaman cache lagi ketika berikutnya file dihapus. Penyesuaian kinerja Linux bukan area saya, tetapi / proc / sys / {vm, fs} / mungkin berisi sesuatu yang relevan.
Jika Anda dapat melakukan downtime, Anda dapat mempertimbangkan untuk mengaktifkan fitur dir_index. Ini mengalihkan indeks direktori dari linear ke sesuatu yang jauh lebih optimal untuk dihapus dalam direktori besar (hashed b-tree).
tune2fs -O dir_index ...
diikuti olehe2fsck -D
akan bekerja. Namun, sementara saya yakin ini akan membantu sebelum ada masalah, saya tidak tahu bagaimana kinerja konversi (e2fsck dengan-D
) dilakukan ketika berhadapan dengan direktori v.large yang ada. Backup + pay-it-and-see.sumber
/proc/sys/fs/vfs_cache_pressure
mungkin itu nilai untuk digunakan, tapi saya tidak tahu apakah direktori itu sendiri diperhitungkan terhadap cache halaman (karena memang seperti itu) atau cache inode (karena, meskipun tidak menjadi inode, itu metadata FS dan dibundel ke sana karena alasan itu). Seperti yang saya katakan, penyetelan Linux VM bukan area saya. Mainkan dan lihat apa yang bisa membantu.Jelas bukan apel untuk apel di sini, tapi saya menyiapkan sedikit tes dan melakukan yang berikut:
Menciptakan 100.000 file 512-byte dalam suatu direktori (
dd
dan/dev/urandom
dalam satu lingkaran); lupa mengatur waktu, tetapi butuh sekitar 15 menit untuk membuat file-file itu.Jalankan yang berikut untuk menghapus file yang dikatakan:
ls -1 | wc -l && time find . -type f -delete
Ini adalah kotak Pentium 4 2.8GHz (beberapa ratus GB IDE 7200 RPM saya pikir; EXT3). Kernel 2.6.27.
sumber
rm
sangat lambat pada sejumlah besar file, karenanyafind -delete
pilihan. Dengan wildcard pada shell, itu akan memperluas setiap nama file yang cocok, dan saya mengasumsikan ada buffer memori terbatas untuk itu, sehingga Anda bisa melihat bagaimana itu akan menjadi tidak efisien.Terkadang Perl dapat bekerja secara ajaib dalam kasus-kasus seperti ini. Sudahkah Anda mencoba jika skrip kecil seperti ini dapat mengungguli bash dan perintah dasar shell?
Atau yang lain, mungkin pendekatan Perl yang lebih cepat:
EDIT: Saya baru saja mencoba skrip Perl saya. Semakin banyak yang melakukan sesuatu dengan benar. Dalam kasus saya, saya mencoba ini dengan server virtual dengan 256 MB RAM dan setengah juta file.
time find /test/directory | xargs rm
hasil:dibandingkan dengan
sumber
*(oN)
]Dari apa yang saya ingat penghapusan inode dalam sistem file ext adalah O (n ^ 2), sehingga semakin banyak file yang Anda hapus semakin cepat sisanya akan pergi.
Ada satu kali saya dihadapkan dengan masalah yang sama (meskipun perkiraan saya melihat ~ 7h waktu penghapusan), pada akhirnya pergi rute yang disarankan jftuga di komentar pertama .
sumber
Yah, ini bukan jawaban yang nyata, tapi ...
Apakah mungkin untuk mengubah filesystem ke ext4 dan melihat apakah ada perubahan?
sumber
Baiklah ini telah dibahas dalam berbagai cara di sisa utas tapi saya pikir saya akan membuang dua sen saya. Penyebab kinerja dalam kasus Anda mungkin readdir. Anda mendapatkan kembali daftar file yang tidak selalu berurutan pada disk yang menyebabkan akses disk di semua tempat ketika Anda memutuskan tautan. File-file tersebut cukup kecil sehingga operasi pembatalan tautan mungkin tidak melompat terlalu jauh sehingga ruang kosong. Jika Anda membaca dan kemudian mengurutkan dengan menaik inode Anda mungkin akan mendapatkan kinerja yang lebih baik. Jadi readdir menjadi ram (urutkan berdasarkan inode) -> unlink -> profit.
Inode adalah perkiraan kasar di sini saya pikir .. tetapi mendasarkan pada kasus penggunaan Anda mungkin cukup akurat ...
sumber
Saya mungkin akan mengeluarkan kompiler C dan melakukan moral yang setara dengan skrip Anda. Yaitu, gunakan
opendir(3)
untuk mendapatkan pegangan direktori, kemudian gunakanreaddir(3)
untuk mendapatkan nama file, lalu menghitung data ketika saya memutuskan tautan dan sesekali cetak "% d file dihapus" (dan mungkin waktu yang berlalu atau cap waktu saat ini).Saya tidak berharap ini akan terasa lebih cepat daripada versi skrip shell, hanya saja saya terbiasa harus merobek kompiler sekarang dan lagi, baik karena tidak ada cara bersih untuk melakukan apa yang saya inginkan dari shell atau karena sementara bisa dilakukan di shell, itu lambat secara produktif seperti itu.
sumber
Anda mungkin mengalami masalah penulisan ulang dengan direktori. Coba hapus file terbaru terlebih dahulu. Lihatlah opsi-opsi mount yang akan menunda writeback ke disk.
Untuk bilah kemajuan coba jalankan sesuatu seperti
rm -rv /mystuff 2>&1 | pv -brtl > /dev/null
sumber
Inilah cara saya menghapus jutaan file jejak yang kadang-kadang dapat dikumpulkan pada server database Oracle besar:
Saya menemukan bahwa ini menghasilkan penghapusan yang cukup lambat yang berdampak rendah pada kinerja server, biasanya sekitar sejam per satu juta file pada pengaturan 10.000 IOPS "khas".
Sering dibutuhkan beberapa menit sebelum direktori dipindai, daftar file awal dibuat dan file pertama dihapus. Dari sana dan seterusnya, a. diulang untuk setiap file yang dihapus.
Keterlambatan yang disebabkan oleh gema ke terminal telah membuktikan cukup penundaan untuk mencegah beban signifikan saat penghapusan sedang berlangsung.
sumber
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'
:? Tambahkan-delete
ke yang terakhir untuk benar-benar menghapus sesuatu; seperti tertulis, itu hanya mencantumkan apa yang akan dihapus. Perhatikan bahwa ini dioptimalkan untuk keadaan di mana Anda memiliki banyak hal yang tidak menarik di direktori terdekat; jika bukan itu masalahnya, Anda bisa menyederhanakan logika banyak.rm
terjadi), sehingga Anda memiliki I / O yang relatif efisien pada saat startup, diikuti oleh s, menyakitkan out-of-orderrm
. yang mungkin tidak menyebabkan banyak I / O, tetapi melibatkanscandir
berjalan direktori berulang kali (tidak menyebabkan I / O karena itu sudah dimuat ke dalam blok cache; lihat jugavfs_cache_pressure
). Jika Anda ingin memperlambat,ionice
ini merupakan pilihan, tetapi saya mungkin akan menggunakan fraksional-detiksleep
.find /u*/app/*/diag -path '*/trace/*.tr' -execdir rm {} +
akan menjalankan saturm
per direktori, jadi Anda akan memiliki lebih sedikit overhead CPU. Selama Anda memiliki banyak waktu CPU untuk cadangan, pelambatan disk IO dengan mengacaukan seluruhrm
proses untuk setiapunlink
pekerjaan, saya kira, tapi itu jelek. perl dengan sleep per unlink akan lebih baik jika tidur di antararm
seluruh direktori pada satu waktu terlalu bursty. (-execdir sh -c ...
mungkin)Anda dapat menggunakan fitur paralelisasi 'xargs':
sumber
sumber
sebenarnya, yang ini sedikit lebih baik jika shell yang Anda gunakan melakukan ekspansi baris perintah:
sumber