Write amplification: почему SSD пишет в разы больше, чем вы ему отдали
Вы записали на диск гигабайт данных, а SMART-счётчик «байт записано на NAND» вырос на два-три гигабайта. Ресурс SSD, рассчитанный по паспортному TBW, кончается заметно раньше, чем предполагала нагрузка на бумаге, а случайная запись под вечер вдруг проседает по скорости без видимой причины. За всем этим стоит одна и та же механика — write amplification, коэффициент разрыва между тем, что попросила записать операционная система, и тем, что контроллер SSD реально высыпал на флеш-память. Разберём, откуда берётся этот разрыв, почему его нельзя обнулить и что реально можно сделать, чтобы держать его в узде.
Содержание
- Страница и блок: почему SSD не может просто переписать один сектор
- Garbage collection: зачем контроллеру переносить чужие данные
- Write amplification: что это и как считается
- Что раздувает WAF: паттерн нагрузки и заполненность диска
- Как высокий WAF сокращает реальный срок службы SSD
- Как WAF снижает производительность записи, а не только ресурс
- Практика: over-provisioning, TRIM и как держать WAF в узде
Страница и блок: почему SSD не может просто переписать один сектор
Флеш-память NAND устроена не так, как магнитный диск, где головка может перезаписать произвольный сектор на месте. У NAND-флеша есть два уровня организации:
- Страница (page) — минимальная единица чтения и записи, обычно несколько килобайт (у современных TLC/QLC-чипов это порядка 4–16 КБ, конкретное значение зависит от модели и поколения памяти).
- Блок (block) — минимальная единица стирания, объединяет много страниц подряд — от нескольких десятков до нескольких сотен на блок, опять же в зависимости от чипа.
Ключевое физическое ограничение NAND: записать данные можно только в *пустую* страницу — ту, что либо никогда не использовалась, либо была стёрта. А стереть можно только целый блок целиком, ни одну отдельную страницу в нём стереть нельзя.
Отсюда прямое следствие: когда ОС просит SSD «изменить» уже записанные данные (перезаписать файл, обновить строку в базе, дописать в лог), контроллер физически не может переписать старую страницу на месте. Вместо этого он:
- Находит свободную (стёртую) страницу где-то в другом блоке.
- Записывает туда новые данные.
- Помечает старую страницу как невалидную (данные там больше не актуальны, но физически они всё ещё лежат на чипе).
- Обновляет таблицу трансляции адресов (FTL, flash translation layer), чтобы логический адрес теперь указывал на новую страницу.
Так СSD «эмулирует» перезапись на месте, оставаясь верным своей физической природе — писать можно только в чистое, а чистить можно только блоками.
Garbage collection: зачем контроллеру переносить чужие данные
Пока диск свежий и на нём много пустых блоков, схема выше работает без побочных эффектов — контроллер просто берёт очередную чистую страницу. Проблема начинается, когда пустые блоки заканчиваются, а невалидные (мусорные) страницы накапливаются внутри уже использованных блоков вперемешку с валидными.
В этот момент контроллеру нужно физически освободить блок целиком, чтобы было куда писать дальше. Но стереть блок можно только целиком — а в нём вперемешку лежат и мусорные страницы (можно смело стирать), и валидные, актуальные данные (терять нельзя). Поэтому перед стиранием блока контроллер обязан:
- Прочитать все ещё валидные страницы из выбранного «грязного» блока.
- Переписать их в другое, уже подготовленное место (в новый или частично свободный блок).
- Только после этого стереть исходный блок целиком, получив пул чистых страниц.
Этот процесс называется garbage collection (GC) — сборка мусора, по прямой аналогии с управлением памятью в языках программирования, только на уровне флеш-чипов. Именно здесь и рождается write amplification в чистом виде: чтобы освободить место под 1 МБ новых данных от хоста, контроллер может быть вынужден заодно переписать ещё N мегабайт чужих, никак не связанных с этой операцией данных — просто потому, что они физически оказались соседями по блоку с мусором.
Важный нюанс: garbage collection работает в фоне и не привязана жёстко к моменту прихода записи от ОС — контроллер может откладывать её, объединять несколько операций, использовать SLC-кэш как буфер. Но рано или поздно эта работа должна быть сделана, и она всегда добавляет реальных записей на NAND сверх того, что просила ОС.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверWrite amplification: что это и как считается
Write amplification factor (WAF) — это отношение объёма данных, реально записанных на флеш-чипы, к объёму данных, которые хост (ОС, приложение) попросил записать за тот же период:
WAF = объём записи на NAND / объём записи от хоста
По определению WAF не может быть меньше 1 — контроллер физически не может записать на чипы меньше байт, чем ему передала ОС для сохранения (за вычетом сжатия на тех контроллерах, что его умеют — но это отдельная и не универсальная тема). WAF, равный ровно 1, — идеальный, но практически недостижимый случай: он означал бы, что каждая запись хоста ложится в уже пустую страницу и никогда не требует переноса чужих данных.
На практике WAF почти всегда больше 1, и его конкретное значение зависит от служебных операций самого контроллера (обновление FTL-таблиц, метаданных wear leveling), паттерна нагрузки — насколько запросы на запись случайны или последовательны, заполненности диска, поддержки и своевременности TRIM и размера spare area (over-provisioning) у конкретной модели SSD.
Точное числовое значение WAF для конкретного диска под конкретной нагрузкой — это то, что имеет смысл измерять на месте (через SMART-атрибуты, если производитель их публикует, или через нагрузочное тестирование), а не брать из общих таблиц: оно слишком сильно зависит от прошивки контроллера, объёма spare area и характера ввода-вывода, чтобы был смысл называть здесь усреднённые цифры.
Что раздувает WAF: паттерн нагрузки и заполненность диска
Два фактора влияют на WAF сильнее прочих.
Случайная запись мелкими блоками против последовательной. Когда данные пишутся последовательно большими кусками, контроллеру проще заполнять блоки целиком «начисто» — в идеале блок либо весь валиден, либо весь мусор, и GC почти не нужен. Когда запись случайная и мелкая (характерно для баз данных, логов с ротацией, файлов виртуальных дисков), валидные и невалидные страницы перемешиваются внутри блоков куда сильнее — GC приходится переносить больше «выживших» данных на каждый освобождаемый блок.
Заполненность диска. Чем меньше на SSD свободного логического пространства, тем меньше у контроллера пустых блоков, которые можно использовать без немедленной сборки мусора. На почти заполненном диске почти каждая новая запись сразу требует GC — свободных чистых блоков просто не остаётся про запас. На диске с большим запасом свободного места контроллер может дольше откладывать GC, накапливать мусорные блоки с высокой долей невалидных страниц (такие блоки выгоднее всего чистить — переносить почти нечего) и в целом работать эффективнее.
Как высокий WAF сокращает реальный срок службы SSD
Каждая ячейка NAND-флеша выдерживает ограниченное число циклов программирования и стирания (P/E cycles) — после этого она физически деградирует и перестаёт надёжно хранить заряд. Именно на этом ограничении строится вся арифметика ресурса SSD: паспортный показатель TBW (terabytes written) или DWPD (drive writes per day) — это оценка производителя, сколько данных от хоста диск способен принять за жизненный цикл, прежде чем P/E-циклы у ячеек будут исчерпаны.
Но P/E-циклы тратятся не на запись от хоста напрямую, а на реальную запись на NAND. Если контроллер физически переписывает на флеш заметно больше данных, чем попросила ОС, то и P/E-циклы расходуются быстрее — на ту же полезную нагрузку от хоста диск тратит кратно больше своего физического ресурса, чем при WAF, близком к идеалу.
Паспортный TBW производитель считает, закладывая какой-то средний WAF для типовой нагрузки. Если ваш реальный паттерн записи (много случайной мелкой записи, диск почти полон, TRIM не работает) даёт WAF заметно выше того, что закладывал производитель, — реальный «пробег» диска в терабайтах хостовой записи до исчерпания ресурса будет меньше паспортного, даже если каждая отдельная запись целиком укладывается в спецификацию. Поэтому мониторить стоит не только «сколько байт записала ОС», но и показатели износа, которые видит сам диск — про то, какие атрибуты SMART реально коррелируют с приближающимся отказом, в отдельном разборе: SMART: какие показатели реально предсказывают смерть диска.
Механику постепенной деградации ячеек и то, как контроллер распределяет износ между блоками (wear leveling), стоит рассматривать в связке с write amplification — это две стороны одного процесса старения SSD: как SSD умирает медленно: wear leveling простыми словами. А чтобы предметно прикинуть, сколько TBW уйдёт на конкретную нагрузку, пригодится расчёт из статьи ресурс SSD: сколько TBW уйдёт на вашу нагрузку.
Как WAF снижает производительность записи, а не только ресурс
Write amplification — это не только вопрос долголетия диска, но и прямая причина просадок скорости записи под нагрузкой. Дело в том, что garbage collection не бесплатна с точки зрения времени: чтение валидных страниц из «грязного» блока, их перезапись в новое место и стирание освобождённого блока — это реальные операции ввода-вывода на тех же NAND-каналах, которые в это же время нужны для обслуживания новых запросов от хоста.
Если GC не успевает работать в фоне достаточно заранее (например, при устойчиво высокой интенсивности случайной записи и малом запасе свободных блоков), контроллер оказывается вынужден выполнять её синхронно, прямо в момент прихода запроса на запись от хоста — и тогда операция записи от приложения ждёт, пока освободится место. Внешне это выглядит как непредсказуемые всплески задержки именно тогда, когда диск давно не простаивал: SLC-кэш (если он есть) исчерпан, свободных чистых блоков не осталось, GC работает «в реальном времени», а не заранее.
Похожий по характеру эффект — когда служебная активность самого SSD блокирует полезный ввод-вывод — проявляется и вокруг других механизмов обслуживания диска, не только garbage collection. Разбор конкретного случая, где периодическая фоновая операция на всём разделе на 15 секунд останавливала базу данных, показывает ту же логику на соседнем механизме: база замирала на 15 секунд — разбор фонового TRIM на всём разделе. Скорость деградации под нагрузкой сильно зависит от модели контроллера, объёма DRAM-кэша и spare area — универсальных цифр «на сколько именно просядет запись» здесь нет, это предмет измерения на конкретном железе, а не табличное значение.
Практика: over-provisioning, TRIM и как держать WAF в узде
Полностью убрать write amplification нельзя — это следствие физики NAND, а не недоработка прошивки. Но снизить его до разумного уровня вполне реально несколькими практическими способами.
Over-provisioning — оставить диску запас невидимого для ОС пространства. Часть физической ёмкости SSD с завода не размечена под пользовательские данные и служит контроллеру резервом чистых блоков (spare area) — именно она позволяет GC работать заранее и без спешки. Пользователь может дополнительно увеличить этот резерв вручную, просто не размечая под файловую систему часть диска:
# пример: диск на 1 ТБ, под раздел отдаём не весь объём,
# а, скажем, 90% ёмкости — остаток контроллер использует
# как дополнительный резерв чистых блоков
parted /dev/nvme0n1 mkpart primary 0% 90%
Чем интенсивнее случайная запись на диске (СУБД, очереди сообщений, VM-хранилища), тем заметнее эффект от такого запаса: чем больше недоразмеченного пространства, тем ниже давление на GC. Конкретный процент, который стоит закладывать, зависит от профиля нагрузки и модели диска — единого правильного числа для всех случаев нет.
TRIM — сообщать контроллеру, какие страницы больше не нужны. Когда файловая система удаляет файл, сами данные физически остаются на NAND — с точки зрения контроллера страницы всё ещё выглядят валидными, пока ему явно не сказали обратное. Команда TRIM как раз и передаёт контроллеру список логических блоков, которые ОС больше не считает занятыми, — это позволяет GC сразу считать такие страницы мусором, а не тратить цикл на их «спасение» при следующей сборке мусора.
Проверить и включить периодический TRIM на Linux:
# ручной прогон TRIM по всем смонтированным ФС, поддерживающим discard
fstrim -av
# включить еженедельный systemd-таймер (обычно уже есть в дистрибутиве)
systemctl enable --now fstrim.timer
systemctl status fstrim.timer
Постоянный (discard в опциях монтирования) и периодический (fstrim.timer) режимы TRIM — разные по нагрузке на диск сценарии, и для большинства серверных задач периодический прогон предпочтительнее: он не добавляет TRIM-операцию в каждую отдельную транзакцию удаления, а выполняет её пакетно по расписанию.
Не доводить диск до заполнения под завязку. Даже без ручного over-provisioning свободное логическое пространство файловой системы работает похожим образом — чем ближе диск к 100% занятости, тем меньше у контроллера пустых блоков для манёвра. Держать разумный запас свободного места на томах с активной случайной записью — простая и рабочая практика.
Учитывать профиль нагрузки при выборе диска. Для write-heavy сценариев (базы данных, очереди, частое логирование) стоит смотреть на модели SSD с изначально большим spare area и заявленным DWPD — они спроектированы под высокую интенсивность случайной записи, а не на пиковую последовательную скорость в рекламных цифрах.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Можно ли вообще свести write amplification к единице?
Нет, это противоречило бы физике NAND — стирание только блоками, запись только в чистые страницы неизбежно требует переноса части данных при сборке мусора. Цель — не обнулить WAF, а держать его на приемлемом для вашей нагрузки уровне за счёт over-provisioning, TRIM и разумной заполненности диска.
TRIM решает проблему write amplification полностью?
Нет, TRIM снижает WAF, но не убирает его причину. TRIM избавляет контроллер от необходимости «спасать» уже удалённые данные при GC, но сама механика переноса валидных страниц между блоками остаётся — TRIM просто уменьшает объём того, что приходится переносить.
Как узнать реальный write amplification своего диска?
Часть SSD публикуют через SMART отдельные атрибуты «данных записано хостом» и «данных записано на NAND» (у разных производителей называются и нумеруются по-разному). Если оба значения доступны — WAF считается их прямым делением. Если нет, эффект можно оценить косвенно — по скорости расхода TBW-ресурса относительно объёма реальной хостовой записи за тот же период.
Стоит ли переживать про write amplification на десктопном SSD с лёгкой нагрузкой?
Для типичного пользовательского сценария (браузер, документы, редкие крупные файлы) объём записи в принципе низкий, и даже повышенный WAF не успевает выбрать сколько-нибудь заметную долю ресурса диска за реалистичный срок службы оборудования. Тема становится практически значимой там, где запись интенсивная и постоянная — на серверах баз данных, очередей сообщений, логирования с высокой частотой.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →