В отличие от обычных статей, это скорее инсайдерский отчёт о том, как происходит регулярная работа над Sherlock, чтобы поддерживать его в наилучшем виде для наших пользователей. Надеемся в будущем публиковать больше таких статей.
Листинг многих файлов занимает время
Всё началось с вопроса в техподдержку от пользователя. Он сообщил о проблеме, что выполнение
ls
занимает несколько минут в каталоге c более 15 000 записей в $SCRATCH
[каталог для временных файлов — прим. пер.].Тысячи файлов в одном каталоге обычно создают трудности для файловой системы и такое определённо не рекомендуется. Пользователь знал это и признал, что это нехорошо, но упомянул, что на его ноутбуке листинг выполняется в 1000 раз быстрее, чем в Sherlock. Конечно, это нас задело. Поэтому мы заглянули глубже.
Потому что ls выглядит красиво
Мы рассмотрели, что на самом деле делает
ls
при листинге каталога, и почему процесс занимает так много времени. В большинстве современных дистрибутивов ls
по умолчанию выполняется как ls --color=auto
, потому что всем нравится расцветка.Но красивые цвета имеют свою цену: для каждого файла
ls
должен получить информацию о типе файла, его разрешениях, флагах, расширенных атрибутах и тому подобном, чтобы выбрать соответствующий цвет.Одно из простых решений проблемы — вообще отключить цвет в ls, но представьте возмущение пользователей. Ни в коем случае нельзя забирать цветной вывод, мы не монстры.
Поэтому мы заглянули глубже.
ls
раскрашивает записи через переменную среды LS_COLORS
, которую задаёт dircolors(1)
на основе файла конфигурации dir_colors(5)
. Да, исполняемый файл считывает конфигурационный файл для создания переменной среды, которую потом использует ls (а если вы не знаете о файлах door (do), то dir_colors сработает, несмотря ни на что).Разберёмся подробнее
Чтобы определить, какая из схем расцвечивания вызывает замедление, мы создали экспериментальную среду:
$ mkdir $SCRATCH/dont
$ touch $SCRATCH/dont/{1..10000} # don't try this at home!
$ time ls --color=always $SCRATCH/dont | wc -l
10000
real 0m12.758s
user 0m0.104s
sys 0m0.699s
12,7 секунд для 10 000 файлов, не очень хорошо.
Кстати, нужен флагТак что же занимает столько времени? Мы посмотрели с помощью--color=always
: хотя он обращается вls --color=auto
, ноls
обнаруживает, когда он не подключен к терминалу (например, по каналу или с перенаправлением выдачи) и отключает раскраску, если установлено значениеauto
. Умный парень.
strace
:$ strace -c ls --color=always $SCRATCH/dont | wc -l
10000
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
44.21 0.186617 19 10000 lstat
42.60 0.179807 18 10000 10000 getxattr
12.19 0.051438 5 10000 capget
0.71 0.003002 38 80 getdents
0.07 0.000305 10 30 mmap
0.05 0.000217 12 18 mprotect
0.03 0.000135 14 10 read
0.03 0.000123 11 11 open
0.02 0.000082 6 14 close
[...]
Ничего себе: 10 000 вызовов
lstat()
, 10 000 вызовов getxattr()
(которые все терпят неудачу, потому что в нашей среде нет атрибутов, которые ищет ls), 10 000 вызовов capget()
.Наверняка это можно оптимизировать.
Атрибут capabilities? Неа
Следуя советам бага 10-летней давности, мы попытались отключить проверку атрибута capabilities:
$ eval $(dircolors -b | sed s/ca=[^:]*:/ca=:/)
$ time strace -c ls --color=always $SCRATCH/dont | wc -l
10000
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
98.95 0.423443 42 10000 lstat
0.78 0.003353 42 80 getdents
0.04 0.000188 10 18 mprotect
0.04 0.000181 6 30 mmap
0.02 0.000085 9 10 read
0.02 0.000084 28 3 mremap
0.02 0.000077 7 11 open
0.02 0.000066 5 14 close
[...]
------ ----------- ----------- --------- --------- ----------------
100.00 0.427920 10221 6 total
real 0m8.160s
user 0m0.115s
sys 0m0.961s
Ух ты, ускорение до 8 секунд! Мы избавились от всех этих дорогих вызовов
getxattr()
, и вызовы capget()
тоже исчезли, отлично.Но ещё остались эти надоедливые вызовы
lstat()
, хотя…Сколько нужно цветов?
Поэтому мы более подробно рассмотрели
LS_COLORS
.Сначала просто отключили эту переменную:
$ echo $LS_COLORS rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30;43:ca=:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.axa=00;36:*.oga=00;36:*.spx=00;36:*.xspf=00;36: $ unset LS_COLORS $ echo $LS_COLORS $ time ls --color=always $SCRATCH/dont | wc -l 10000 real 0m13.037s user 0m0.077s sys 0m1.092s
Что!?! По-прежнему 13 секунд?
Оказывается, когда переменная среды
LS_COLORS
не определена или отсутствует только один из её элементов <type>=color:
, она по умолчанию использует встроенную базу данных и всё равно использует цвета. Поэтому, если вы хотите отключить раскраску для определённого типа файла, вам нужно переопределить её с помощью <type>=:
или <type> 00
в файле DIR_COLORS
.После множества проб и ошибок мы сузили круг поиска до этого:
EXEC 00
SETUID 00
SETGID 00
CAPABILITY 00
что записывается как
LS_COLORS='ex=00:su=00:sg=00:ca=00:'
Это означает: не раскрашивай файлы ни по атрубуту capabilities, ни по битам
setuid/setgid
, ни по флагу исполняемости.Ускоряем ls
И если не делать ни одной из этих проверок, то вызовы
lstat()
исчезают, и теперь совсем другое дело:$ export LS_COLORS='ex=00:su=00:sg=00:ca=00:'
$ time strace -c ls --color=always $SCRATCH/dont | wc -l
10000
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
63.02 0.002865 36 80 getdents
8.10 0.000368 12 30 mmap
5.72 0.000260 14 18 mprotect
3.72 0.000169 15 11 open
2.79 0.000127 13 10 read
[...]
------ ----------- ----------- --------- --------- ----------------
100.00 0.004546 221 6 total
real 0m0.337s
user 0m0.032s
sys 0m0.029s
0,3 секунды на списке 10 000 файлов, рекорд.
Настраиваем Sherlock
От 13 секунд с настройками по умолчанию до 0,3 секунды с небольшой настройкой
LS_COLORS
означает 40-кратное ускорение за счёт отсутствия setuid
/ setgid
и раскрашенных исполняемых файлов. Не такая большая потеря.Конечно, теперь это настроено в Sherlock для каждого пользователя.
Но если вы хотите вернуть раскраску, то можете просто вернуться к настройкам по умолчанию:
$ unset LS_COLORS
Но тогда на каталогах с большим количеством файлов обязательно заваривайте кофе, пока работает
ls
. Комментарии (9)
legolegs
07.05.2019 15:39Я проводил некоторые исследования в области директорий с большим количеством файлов и могу сказать следующее:
- Современная ФС, такая как ext4 пережёвывает директории с любым количеством файлов без повреждений и заметной потери производительности. Проверял на десятках миллионов.
- Устаревшие ФС, такие как FAT32 могут при обращении к одному файлу тормозить линейно, а иногда и квадратично от к-ва файлов в папке
- Простейшим из быстрейших способов получить список файлов является вызов find без параметров, у ls надо читать ман и приказывать ему не сортировать список (и не раскрашивать, как понятно из статьи)
- Быстрейшим способом очистить такую директорию является rsync с пустой директорией. А rm * подвесит вам шелл. Если рсинка под рукой нет я бы попробовал find -delete или даже "rm -r .".
- Если вы используете ls --color или ls -l то вы не избежите вызова stat() на каждый файл, а это совсем не то же самое, что просто получить список имён.
- Если файлы созданы чем-то вроде touch {1..10000000} то их inode расположены подряд и stat() работает сравнительно быстро
- Если вы годами копили свои файлы (например, не чистили сессии php) то inode раскиданы по таблице и stat() будет очень медленным, особенно на НЖМД
- Вам не нужно читать глазами листинг директории с миллионом файлов. Даже парсить его не нужно, нет и не должно быть таких задач.
ppl2scripts
07.05.2019 17:354. Если подождать несколько часов, то rm умрёт, сказав что не хватает памяти. find – примерно та-же история. Единственным надёжным и быстрым способом является rsync с пустой.
Проверялось несколько лет назад на (очень) больших нечищеных кэшах squid.
AcckiyGerman
08.05.2019 12:39+1Я еще заметил, что после удаления или перемещения файлов из конкретной папки, остаются inode записи (ну я думаю, что это они). Их видно в листинге 'ls -l' как большое число в первой строчке, которая начинается с точки и обозначает текущую директорию.
Вызов 'ls' в такой, вроде бы очищенной (а раньше в ней была пара миллионов файлов), папке занимает длительное время, даже если файлов внутри "уже нет".
Лечится удалением и пересоздание папки.
P.s. Ubuntu Server 14.04, ext4
legolegs
08.05.2019 14:58Лечится удалением и пересоздание папки.
Да, как это часто бывает в IT, некоторые структуры данных могут расти но не обучены уменьшатся. 'e2fsck -D' по идее сожмёт такую директорию.
PS Это не inode, они сидят в своей таблице глобальной для всей ФС. Это имена файлов или места, где эти имена ранее были, ведь директория — это такой файл со списком имён файлов и номеров ячеек в таблице inode.
Vlad_fox
07.05.2019 15:53+113 секунд пользователю съекономили… теперь он сможет намного больше файлов обработать за трудодень…
чего бы еще такого полезного замутить?
Kopilov
Нюанс критичный, статья годная. Интересно только, что именно за процесс ускоряется в 40 раз.
Если это просмотр пользователем каталога из 10 000 файлов ценой удаления метаданных — сочувствую этому пользователю.
Если это какой-то автоматизированный процесс, использующий вывод ls в своих нуждах — я обязательно оставлю это здесь:
urtow
Не «удаления метаданных», а «не раскрашивания вывода ls на основании части метаданных»
FRiMN
На самом деле хороший вопрос. Зачем кому-то глазами смотреть 10000 файлов? По поводу скриптов: в статье сказано, что ls не использует раскраску при auto, если не выводится непосредственно в консоль.
Psychopompe
Ну у меня такое бывает иногда (10-20к), нужно проверить, как скрипт отработал.