Назад | Перейти на главную страницу

Создание большого количества грязных страниц блокирует синхронную запись

У нас есть процессы, выполняющие фоновую запись больших файлов. Хотелось бы, чтобы они оказали минимальное влияние на другие процессы.

Вот тест, реализованный на SLES11 SP4. Сервер имеет большую память, что позволяет ему создавать 4 ГБ грязных страниц.

> dd if=/dev/zero of=todel bs=1048576 count=4096
4096+0 records in
4096+0 records out
4294967296 bytes (4.3 GB) copied, 3.72657 s, 1.2 GB/s
> dd if=/dev/zero of=zer oflag=sync bs=512 count=1  
1+0 records in
1+0 records out
512 bytes (512 B) copied, 16.6997 s, 0.0 kB/s

real    0m16.701s
user    0m0.000s
sys     0m0.000s
> grep Dirty /proc/meminfo
Dirty:           4199704 kB

Это мое расследование на данный момент:

Вот мои вопросы:

смотрите также

https://www.novell.com/support/kb/doc.php?id=7010287

ограничить сброс фона Linux (грязные страницы)

https://stackoverflow.com/questions/3755765/what-posix-fadvise-args-for-sequential-file-write/3756466?sgp=2#3756466

http://yarchive.net/comp/linux/dirty_limits.html


РЕДАКТИРОВАТЬ:

есть ext2 файловая система на том же устройстве. На этом девайсе вообще нет зависаний! Единственное влияние на производительность возникает во время очистки грязных страниц, когда синхронный вызов может занимать до 0,3 секунды, что очень далеко от того, что мы наблюдаем с нашей файловой системой ext3.


РЕДАКТИРОВАТЬ2:

Следуя комментарию @Matthew Ife, я попытался выполнить синхронную запись, открыв файл без O_TRUNC, и вы не поверите результату!

> dd if=/dev/zero of=zer oflag=sync bs=512 count=1
> dd if=/dev/zero of=todel bs=1048576 count=4096
> dd if=/dev/zero of=zer oflag=sync bs=512 count=1 conv=notrunc
1+0 records in
1+0 records out
512 bytes (512 B) copied, 0.000185427 s, 2.8 MB/s

dd открывал файл с параметрами:

open("zer", O_WRONLY|O_CREAT|O_TRUNC|O_SYNC, 0666) = 3

меняется с опцией notrunc, теперь это

open("zer", O_WRONLY|O_CREAT|O_SYNC, 0666) = 3

и синхронная запись завершается мгновенно!

Что ж, это не совсем удовлетворительно для моего варианта использования (я выполняю msync в эта мода. Однако теперь я могу отслеживать, что пишут и msync по-разному!


окончательное РЕДАКТИРОВАНИЕ: я не могу поверить, что ударил это: https://www.novell.com/support/kb/doc.php?id=7016100

Фактически под SLES11 dd открывает файл с

open("zer", O_WRONLY|O_CREAT|O_DSYNC, 0666) = 3

и O_DSYNC == O_SYNC!

Вывод:

Для моего варианта использования я, вероятно, должен использовать

dd if=/dev/zero of=zer oflag=dsync bs=512 count=1 conv=notrunc

В SLES11 выполнение oflag = sync действительно будет запускаться oflag = dsync независимо от того, что говорит strace.

Несколько вещей, которые мне было бы интересно узнать о результате.

  1. изначально создавая большой файл с fallocate затем писать в него.

  2. Установка dirty_background_bytes намного ниже (скажем, 1 ГБ) и использование CFQ в качестве планировщика. Обратите внимание, что в этом тесте лучше всего было бы запустить малое в середине большого.

Так что для варианта 1 вы можете избежать всех data=ordered семантика, поскольку распределение блоков уже выполнено (и быстро), потому что оно было предварительно выделено через fallocate и метаданные настраиваются до записи. Было бы полезно проверить, так ли это на самом деле. У меня есть некоторая уверенность, хотя это улучшит производительность.

Для варианта 2 можно немного больше использовать ionice. Крайний срок явно быстрее, чем CFQ, хотя CFQ пытается организовать ввод-вывод для каждого процесса, так что вы обнаружите, что он дает вам лучшую долю ввода-вывода через каждый процесс.

Я где-то читал (сейчас не могу найти источник), что dirty_background_ratio блокирует запись для отдельного процесса фиксации (фактически замедляя большой процесс), чтобы один процесс не истощил все остальные. Учитывая, как мало информации я могу найти сейчас об этом поведении, я не уверен, что это сработает.

О: я должен указать, что fallocate полагается на экстенты, и вам нужно будет использовать ext4.

Отвечаю на свои вопросы, но если бы кто-нибудь мог предложить что-то получше, я был бы крайне признателен :)

имея 4 ГБ грязной памяти в конце теста, я прихожу к выводу, что планировщик ввода-вывода не был вызван в приведенном выше тесте. Это правильно?

Это совершенно неверно. Количество грязной памяти - не лучший показатель. В этом легко убедиться, просто запустив iostat и убедившись, что во время выполнения dd oflag = sync выполняется много записей.

есть ли способ глубже изучить, что заблокировано? Есть какие-нибудь интересные счетчики для просмотра?

perf record -e 'jbd:*' -e 'block:*' -ag

Для более новых ядер замените jbd на jbd2.

Есть идеи об источнике разногласий?

Фактически для ext3 с данные = заказано, поток журналирования отвечает за сброс данных на диск. Сброс происходит в порядке записи. Частоту промывки можно регулировать с помощью совершить вариант при монтировании файловой системы.

Интересный эксперимент: смонтируйте файловую систему с commit = 60 и отключите поток обратной записи. При запуске первого dd он завершается за 2 секунды, и iostat показывает, что никакого ввода-вывода не было!

При запуске второго dd с oflag = sync все операции ввода-вывода, созданные первым dd, сбрасываются на диск.

мы думаем либо об уменьшении значений dirty_ratio, либо о выполнении первого dd в синхронном режиме.

к сведению, оба решения дают хорошие результаты. Еще одна хорошая идея - поместить эти большие файлы в отдельную файловую систему (возможно, смонтированную с помощью data = writeback).

Это не связано конкретно с SLES11 или более ранними ядрами. Такое же поведение наблюдается на всех ядрах, которые я пробовал.

Это ожидаемое поведение ext3: в этой файловой системе синхронная запись блокируется до все предыдущие грязные блоки записываются на диск. Это точная причина, по которой разработчики так полностью пропускали синхронизированную запись в последние годы.

Ext4, XFS, ZFS и BTRFS в этом отношении намного лучше. Учитывая, что ext4 более или менее заменяет ext3, вам действительно следует обновить файловую систему.