Получить доступ

Почему повторное чтение файла быстрее первого?

MiddleОперационные системы
Ещё спрашивают так:
  • Что такое page cache?
  • Что делает fsync и зачем он нужен?
  • Почему после записи в файл данные могут пропасть при сбое питания?
Коротко

ОС кэширует содержимое файлов в свободной памяти — page cache. Повторное чтение берётся из памяти, а запись сначала попадает в кэш и на диск уходит позже.

  • write() вернул управление — данные ещё не на диске
  • fsync() ждёт, пока данные физически запишутся; без него при сбое питания их можно потерять
  • «Свободной» памяти на сервере мало — это нормально, она занята кэшем

А ещё Базы данных делают fsync журнала на каждый коммит, отсюда цена записи

Так подсказку видит пользователь sotto на живом собеседовании — за пару секунд, пока вы думаете вслух.

Подробно

Диск на порядки медленнее памяти, поэтому ядро держит прочитанные страницы файлов в оперативной памяти — это page cache. Под него идёт вся память, не занятая процессами; когда процессам нужно больше, кэш ужимается. Поэтому в free -h на долго работающем сервере колонка free маленькая, а available большая — паниковать не нужно.

Чтение. Первый read идёт на диск и заодно заполняет кэш; ядро ещё и читает наперёд (readahead), если доступ последовательный. Повторное чтение того же файла — копирование из памяти, на порядки быстрее. Отсюда эффект «холодного» и «прогретого» сервера после перезапуска.

Запись. write копирует данные в кэш, помечает страницы «грязными» и сразу возвращает управление. Сбросом на диск занимается ядро в фоне — через несколько секунд или при нехватке памяти. Это быстро, но если пропадёт питание или упадёт ядро, грязные страницы потеряются, хотя программа думала, что записала.

fsync. Вызов fsync(fd) ждёт, пока данные и метаданные файла дойдут до устройства. Именно его делают базы данных при коммите: транзакция считается подтверждённой, только когда запись в журнале (WAL) пережила бы сбой. fsync стоит от долей миллисекунды на SSD с конденсаторами до десятков миллисекунд на HDD, поэтому базы группируют коммиты (group commit), а настройки вроде synchronous_commit = off в PostgreSQL или acks/flush в брокерах — сознательный обмен надёжности на скорость.

Обход кэша. Базы, которые кэшируют данные сами (InnoDB, Oracle), часто открывают файлы с O_DIRECT, чтобы не держать два кэша. PostgreSQL, наоборот, опирается на page cache, поэтому shared_buffers ставят около четверти памяти. Kafka почти полностью полагается на page cache и последовательную запись.

Пример

$ free -h
              total   used   free  shared  buff/cache  available
Mem:           31Gi   6.2Gi  0.4Gi   0.1Gi       24Gi       24Gi

$ time cat big.log > /dev/null    # первый раз — с диска
real 0m4.812s
$ time cat big.log > /dev/null    # второй — из page cache
real 0m0.391s

Что хотят услышать

  • Page cache: кэш файлов в свободной памяти, readahead.
  • Запись отложенная, write не гарантирует сохранность.
  • fsync и его цена, связь с коммитом в базах.
  • «Свободной памяти мало» на сервере — норма.

Частые ошибки

  • Считать, что после write или закрытия файла данные уже на диске.
  • Добавлять памяти серверу из-за маленького free, глядя не на ту колонку.

Что спросят дальше

Связанные вопросы

Закрытая бета

Подсказки на собеседовании — в реальном времени

sotto слышит вопрос интервьюера и показывает короткий ответ и план, как в блоке «Коротко». Оставьте контакт — пришлём доступ и промокод.

  • Промокод на 2 часа — сверх пробных 30 минут
  • Программа для Windows — скачать сразу, войти — через Яндекс ID или VK ID
  • Прямая связь с разработчиком: что улучшить — сделаем