MAATRIX / Блог / Как работает TRIM и что происходит с SSD, когда о нём забыли

Как работает TRIM и что происходит с SSD, когда о нём забыли

MAATRIX

Вы удаляете файл, df показывает больше свободного места — и кажется, что диск про эти данные "забыл". На деле SSD в этот момент вообще ничего не узнал: файловая система переписала свои собственные метаданные, а сама флеш-память как хранила старые байты, так и хранит. Разница между "ФС думает, что место свободно" и "SSD знает, что место свободно" — это ровно то, чем занимается команда TRIM, и если она не работает, диск годами копит долг, который однажды выливается в заметно просевшую скорость записи.

Что на самом деле происходит при удалении файла

Файловая система — ext4, XFS, NTFS, любая — устроена так, что "удаление" файла почти никогда не означает физическое стирание данных. Происходит куда более дешёвая операция: ФС находит запись об этом файле в своих внутренних структурах (inode, таблица размещения, extent-дерево — в зависимости от ФС), помечает соответствующие блоки как свободные в битовой карте или дереве свободного пространства, и на этом всё. Сами байты, которые составляли файл, остаются лежать в тех же логических блочных адресах (LBA), на которые указывала ФС до удаления.

Это осознанное и правильное архитектурное решение — не баг и не лень разработчиков файловых систем. Физическая перезапись всех блоков нулями при каждом удалении была бы дорогой операцией, а главное — она никому особо не нужна: следующая запись в эти же блоки всё равно перезапишет старое содержимое новым. Именно поэтому восстановление удалённых файлов часто работает: пока поверх старых блоков не легла новая запись, данные физически на месте, просто ФС о них "забыла" в своих метаданных.

Для жёсткого диска на этом история и заканчивается — головке всё равно, свободен блок логически или занят, она просто перезаписывает магнитную поверхность по адресу, который ей дали. А вот у SSD с этого момента начинаются проблемы, которых у HDD никогда не было.

Почему SSD не может просто взять и переписать ячейку

Ключевая особенность флеш-памяти NAND в том, что запись и стирание — это принципиально разные операции с разной "зернистостью". Записать данные можно постранично: страница флеша — это несколько килобайт, и контроллер может записать в пустую страницу новые данные напрямую. А вот стереть — то есть привести ячейки в состояние, из которого в них снова можно писать, — можно только целым блоком (block), который состоит из десятков или сотен страниц сразу.

Отсюда фундаментальное ограничение: контроллер SSD физически не может взять одну "грязную" страницу — ту, где раньше лежали данные удалённого файла, — и молча перезаписать в ней новое значение. Сначала нужно стереть весь блок, в который эта страница входит, а стереть блок целиком — значит потерять содержимое всех остальных страниц этого блока, включая те, где может лежать всё ещё актуальный, нужный вам файл.

Поэтому на практике при поступлении новой записи в LBA, который физически указывает на уже занятую страницу, контроллер поступает иначе: он не трогает старую страницу вообще, а записывает новые данные в другую, уже стёртую и свободную страницу из своего пула, обновляет таблицу трансляции адресов (FTL, flash translation layer) так, чтобы логический адрес теперь указывал на новое физическое место, а старую страницу помечает как "невалидную" — то есть данные там больше не актуальны, но физически они всё ещё занимают место и требуют стирания блока, прежде чем это место снова станет пригодным для записи.

Вот тут и разница между "невалидной" страницей и "свободной". Невалидная страница — это данные, которые контроллер сам понял, что устарели, потому что в этот же LBA прилетела новая запись. А страница, где лежит давно удалённый файл, в глазах контроллера остаётся абсолютно валидной и живой — потому что никто и никогда явно не сообщал ему, что эти данные больше не нужны. С точки зрения SSD файл, который вы удалили неделю назад, всё ещё "существует" и его нельзя трогать при сборке мусора.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Что делает команда TRIM и как она меняет картину

TRIM (в спецификации ATA) и его аналог для NVMe — Deallocate/Dataset Management — это команда, которую операционная система посылает контроллеру диска явно: "логические блоки с такого-то по такой-то больше не содержат нужных данных, можешь их не хранить и не переносить при сборке мусора". Это единственный канал, по которому SSD вообще может узнать о разнице между "занято по мнению ФС" и "занято физически" — сам контроллер видеть внутренности файловой системы не может, для него диск — это просто массив пронумерованных блоков без какого-либо смысла.

Получив TRIM на диапазон блоков, контроллер помечает соответствующие страницы как невалидные — точно так же, как это происходило бы при перезаписи, только без реальной записи новых данных. Дальше в игру вступает garbage collection — фоновый процесс, который работает внутри SSD постоянно, независимо от того, простаивает диск или под нагрузкой. GC выбирает блоки с наибольшей долей невалидных страниц, переносит из них оставшиеся валидные данные в новый, заранее стёртый блок, а затем стирает освободившийся блок целиком и кладёт его в пул готовых к записи.

Смысл TRIM в том, что он расширяет "кругозор" сборщика мусора. Без TRIM единственный способ для контроллера понять, что страница невалидна — дождаться, пока в тот же LBA придёт новая запись поверх. Если файл давно удалён и в этот адрес больше никто ничего не пишет, страница будет считаться "живой" бесконечно — до следующей полной перезаписи всего раздела. С TRIM контроллер узнаёт об этом сразу после удаления файла, может включить эти страницы в план сборки мусора заранее и стереть соответствующие блоки в фоне, пока диск не загружен полезной работой, а не в тот момент, когда приложению срочно понадобилась запись.

Важно подчеркнуть: TRIM не ускоряет запись впрямую и не "очищает" диск в том смысле, в каком это делает rm. Это чисто информационная команда — стирание происходит по решению контроллера и его расписанию, а не мгновенно. Но без этой информации у контроллера в принципе нет возможности заранее подготовить свободное место — только реагировать по факту записи.

Что происходит на SSD, если TRIM не работает

Без TRIM невалидные (с точки зрения ОС) страницы для контроллера продолжают выглядеть как валидные данные до тех пор, пока их логический адрес не будет перезаписан повторно. Практический эффект — постепенно накапливающийся разрыв между тем, сколько места реально свободно с точки зрения пользователя, и тем, сколько стёртых и готовых к записи блоков реально доступно контроллеру.

Первое, что растёт в такой ситуации — write amplification, отношение объёма данных, реально записанных в NAND, к объёму данных, которые попросило записать приложение. Причина прямая: если свободных, уже стёртых блоков не хватает, контроллер вынужден в момент записи выбирать частично занятый блок, переносить из него ещё актуальные (с его точки зрения) страницы в другое место, стирать блок и только потом писать новые данные — всё ради одной "полезной" записи от приложения. Подробно о том, как это устроено и почему множитель может расти существенно, разобрано в статье про write amplification на SSD.

Второе следствие — предсказуемая деградация именно скорости записи со временем, а не разовое падение производительности. Пока диск свежий, у контроллера есть большой запас чистых стёртых блоков, и он пишет "как в буфер" — быстро, без накладных расходов на стирание в реальном времени. По мере того как весь адресуемый объём диска хотя бы раз был записан, а TRIM не давал контроллеру знать, какая часть из этого уже неактуальна, свободных стёртых блоков остаётся всё меньше, и всё большая доля операций записи вынуждена включать синхронное стирание блока прямо в момент запроса, а не заранее в фоне. Именно это ощущается как "диск со временем стал писать медленнее, хотя формально места вагон".

Третье следствие — рост нагрузки на ресурс диска: каждый лишний перенос валидных страниц и лишнее стирание блока — это реальные циклы program/erase, которые физически изнашивают ячейки флеша (подробно о механике этого износа — в статье о том, как SSD умирает медленно из-за wear leveling). Работающий TRIM не отменяет этот износ полностью, но убирает ту его часть, что возникает не из-за реальных нужд приложения, а из-за того, что контроллер годами таскал по диску данные давно удалённых файлов, считая их живыми.

Конкретных цифр вроде "во сколько раз падает скорость" здесь намеренно нет — это сильно зависит от модели контроллера, объёма spare area и паттерна записи, и усреднённые проценты без замера на своём железе будут вводить в заблуждение. Ориентируйтесь на направление эффекта, а конкретную картину смотрите на своей нагрузке через SMART и реальные бенчмарки записи.

Как проверить и включить TRIM на практике в Linux

Первый шаг — убедиться, что диск и вся цепочка ниже него вообще поддерживают TRIM. Для этого есть простая проверка:

lsblk --discard

Ненулевые значения в столбцах DISC-GRAN (гранулярность) и DISC-MAX (максимальный размер операции discard) означают, что диск поддерживает TRIM. Нули в обоих столбцах — либо диск реально не поддерживает discard, либо (что чаще встречается на VPS) поддержку не прокинул виртуальный контроллер диска гипервизора.

Дальше — два разных способа сообщать SSD о свободных блоках, и путать их не стоит:

  • Continuous discard — монтирование раздела с опцией discard в /etc/fstab (для ext4/XFS) или встроенный autotrim у ZFS. TRIM-команда отправляется практически сразу после каждого удаления файла, малыми порциями.
  • Periodic (batched) discard — команда fstrim запускается по расписанию (штатно — таймер fstrim.timer в systemd, по умолчанию раз в неделю на большинстве дистрибутивов) и разом отправляет TRIM на все накопившиеся с прошлого раза свободные блоки.
# ручной прогон TRIM на конкретной точке монтирования
fstrim -v /

# проверить, включён ли штатный периодический таймер
systemctl status fstrim.timer
systemctl enable --now fstrim.timer

На практике для большинства серверных нагрузок periodic discard через fstrim.timer — более безопасный выбор по умолчанию: он не добавляет накладных расходов на каждую операцию удаления и не создаёт риска, что TRIM большого диапазона заблокирует диск в неудачный момент. У continuous discard (discard в fstab) обратная сторона: TRIM большого диапазона за один раз может ощутимо просесть по задержке на некоторых контроллерах и прошивках — именно такой случай, когда периодический TRIM всего раздела разом подвесил базу данных на секунды, разобран отдельно в статье про фоновый TRIM. Так что если на сервере крутится чувствительная к задержкам нагрузка вроде СУБД, стоит либо оставаться на fstrim.timer с разумной частотой, либо тестировать discard=async (для ext4 в свежих ядрах — отложенный неблокирующий discard) вместо синхронного discard.

TRIM в VPS, RAID, LUKS и других слоях — где он может потеряться

Отдельная и частая на практике проблема — TRIM теряется где-то на пути от файловой системы до физических ячеек флеша, а не потому, что вы забыли включить fstrim.timer. Каждый слой абстракции между ФС и диском должен явно поддерживать проброс discard-команд дальше, иначе цепочка обрывается.

На VPS первая точка, где TRIM может не дойти до физического SSD — виртуальный диск. Тип контроллера, который гипервизор эмулирует для гостевой ОС, должен поддерживать discard: для QEMU/KVM это virtio-scsi (умеет TRIM из коробки) или virtio-blk с явно включённой опцией discard=on в конфигурации диска — старые образы дисков или контроллеры вроде IDE/старого virtio-blk без этой опции discard просто не пропустят. Если вы арендуете VPS, самый надёжный способ проверить, а не гадать по документации хостинга — тот же lsblk --discard внутри самой машины: если гранулярность и максимальный размер ненулевые, значит вся цепочка гипервизор → гостевая ОС TRIM пропускает.

Дальше в цепочке могут стоять и другие слои:

СлойЧто нужно для проброса TRIM
LVM (логические тома)Опция issue_discards = 1 в /etc/lvm/lvm.conf, либо discard при монтировании ФС поверх тома
LUKS (шифрование диска)Флаг --allow-discards при открытии тома (cryptsetup luksOpen --allow-discards); по умолчанию отключён из соображений безопасности, так как проброс TRIM может частично раскрывать паттерн занятости диска
Программный RAID (mdadm)Поддерживается для RAID уровней 0/1/10 в современных ядрах, для RAID 5/6 исторически была отдельная опасность до соответствующих доработок ядра — стоит явно проверять на своей версии
ZFSСвойство autotrim=on на пуле плюс ручной zpool trim <pool> при необходимости

Если у вас есть хотя бы один из этих слоёв, включённой опции discard на верхнем уровне монтирования недостаточно — нужно проверить проброс на каждой ступени. Частая ошибка — включить discard в fstab, увидеть, что fstrim -v отрабатывает без ошибок, и решить, что всё в порядке, хотя команда на самом деле гасится на уровне LUKS без --allow-discards и до физического диска не доходит. Проверять стоит не факт отсутствия ошибок, а реальный DISC-GRAN/DISC-MAX у самого нижнего в цепочке блочного устройства.

Отдельно стоит отметить SSD, которые в принципе не поддерживают TRIM — обычно старые или очень бюджетные модели без соответствующей прошивки. На таком диске штатное решение — периодический secure erase через утилиту производителя в окно, когда диск можно вывести из эксплуатации целиком: средствами ОС компенсировать отсутствие TRIM тут нечем.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Нужно ли включать и discard в fstab, и fstrim.timer одновременно?

Обычно нет смысла — это два способа делать одно и то же, и совмещать их избыточно. Для большинства серверных сценариев достаточно fstrim.timer; discard в fstab имеет смысл добавлять отдельно, только если вы сознательно выбрали continuous discard вместо периодического и понимаете компромисс по задержкам.

Опасен ли TRIM для данных — можно ли восстановить файл после него?

Да, в этом и разница с обычным удалением: пока TRIM не отправлен, данные физически на месте и восстановимы штатными средствами (пока их не перезаписали). После TRIM контроллер вправе стереть эти страницы в любой момент, и восстановление становится ненадёжным или невозможным — рассчитывать на данные после TRIM не стоит.

Работает ли TRIM на обычном HDD?

Нет, у HDD нет ограничения "стереть блок целиком перед записью" — головка может перезаписать сектор напрямую, поэтому команда TRIM для них просто не имеет смысла и не поддерживается.

Как понять, что диску на сервере реально не хватает TRIM прямо сейчас?

Прямых счётчиков "утраченного" TRIM обычно нет, но косвенный признак — сравнение производительности записи на пустом и почти заполненном разделе на одном и том же диске: заметная разница в пользу пустого раздела при выключенном или неработающем TRIM — повод проверить всю цепочку проброса discard.

Может ли TRIM сам по себе испортить данные, которые я не удалял?

Нет — TRIM применяется только к тем блокам, которые файловая система сама пометила свободными в своих метаданных. Если файл не удалён и не перезаписан, его блоки в TRIM не попадают.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →