ОС · Persistence · 13 мин
Flash и SSD
Почему SSD — это совсем другое железо
В прошлой главе про HDD мы разбирали диск с крутящимися пластинами и механической головкой. Там главная боль — seek (подвести головку) и rotation (дождаться нужного сектора). Поэтому случайный доступ у HDD в десятки раз медленнее последовательного.
SSD (Solid State Drive — «твердотельный накопитель») устроен иначе: никаких движущихся частей, только микросхемы памяти. Память называется NAND flash (флеш-память). Раз нет головки и нет вращения — нет и seek. Любая ячейка доступна почти за одинаковое время.
Бытовая аналогия. HDD — это виниловая пластинка с иглой: чтобы заиграть нужную песню, иглу надо физически подвести к дорожке. SSD — это плейлист в телефоне: любой трек открывается мгновенно, неважно, первый он или сотый.
Казалось бы — заменили механику на электронику, стало быстро, и всё. Но у флеша есть три неочевидных особенности, которые меняют вообще всё поведение накопителя. Из-за них SSD внутри гораздо хитрее, чем HDD. Разберём их.
HDD SSD
┌───────────────┐ ┌───────────────────┐
│ ((( o ))) │ │ [chip][chip][chip] │
│ пластина+игла │ │ [chip][chip][chip] │
│ крутится 7200 │ │ нет движущихся │
│ об/мин │ │ частей │
└───────────────┘ └───────────────────┘
seek + rotation любая ячейка ~ равно
случайн. << послед. случайн. ≈ послед. (чтение)Слева HDD с механикой и неравномерным доступом, справа SSD из микросхем, где любое место читается примерно за одно и то же время.
Три правила флеша: страницы, блоки и стирание
Вот главное, что нужно понять про NAND flash. Внутри есть две единицы размера:
- Страница (page) — небольшая порция, обычно 4–16 КБ. Это единица чтения и записи.
- Блок (block) — большая группа страниц, обычно 128–256 страниц (то есть несколько мегабайт). Это единица стирания (erase).
А теперь три правила, которые ломают привычную картину «диск — это массив ячеек, пиши куда хочешь»:
- Читать можно по страницам — быстро, в любом порядке.
- Писать можно по страницам, НО только в страницу, которая перед этим была стёрта (находится в «чистом» состоянии).
- Стирать можно только целым блоком — нельзя стереть одну страницу.
То есть у каждой страницы три возможных состояния: СТЁРТАЯ (чистая, готова
принять запись), ЗАПИСАННАЯ (в ней лежат данные) и НЕВАЛИДНАЯ (данные
устарели, но физически ещё занимают место — стереть-то можно только весь блок).
БЛОК (единица стирания) = много страниц
┌─────────────────────────────────────────────┐
│ page0 page1 page2 page3 ... page127 │ ← единица чтения/записи
└─────────────────────────────────────────────┘
Чтение: page2 ──────────────► быстро, любой порядок
Запись: page2 ──► только если page2 СТЁРТАЯ
Стирание: ───► нельзя стереть page2 одну
стирается ВЕСЬ блок целикомЗдесь видно несоответствие масштабов: пишем мелко (страница), а стираем крупно (блок). Именно из этого вырастают все проблемы дальше.
Главная засада: нельзя перезаписать страницу
В HDD перезапись тривиальна: навёл головку, наложил новые данные поверх старых. Во флеше так нельзя. Чтобы записать в уже занятую страницу, её сперва надо стереть. А стереть одну страницу нельзя — только весь блок из сотни страниц.
Представьте: в блоке лежат данные 128 страниц, а вы хотите изменить всего одну. Наивный путь был бы кошмаром:
Хочу изменить page2 в блоке, где остальные 127 страниц заняты полезным:
наивно:
1. прочитать все 128 страниц в память (буфер)
2. в буфере поменять page2
3. стереть ВЕСЬ блок
4. записать обратно все 128 страниц
→ ради 4 КБ изменений мы перелопатили несколько МБ!Вот это и есть write amplification (усиление записи) — когда чтобы записать чуть-чуть логических данных, накопителю приходится физически перетасовать гораздо больше. Считается как «сколько байт реально записано на флеш ÷ сколько байт просил записать хост». Чем выше — тем хуже: и медленнее, и быстрее изнашивается флеш.
Аналогия: блокнот, написанный ручкой без ластика. Стереть можно только целую страницу целиком. Хотите поправить одно слово в середине — придётся переписать всю страницу заново.
На практике никто не делает наивный «read-modify-erase-write» на каждую запись. Вместо этого SSD пишет новые данные в свободное место, а старую копию просто помечает невалидной — и разгребает мусор потом, пачкой. Кто этим занимается — дальше.
FTL: слой, который всё это прячет
Если бы операционная система знала про страницы, блоки и стирание, драйверы файловых систем (см. файлы и директории) пришлось бы переписывать под флеш. Этого не случилось — потому что внутри SSD есть FTL (Flash Translation Layer), «слой трансляции флеша».
FTL — это маленький контроллер с прошивкой внутри SSD. Снаружи он прикидывается обычным блочным диском: «дай мне логический блок №12345 — я запишу/прочитаю». А внутри он сам решает, на какую физическую страницу флеша это положить, и ведёт таблицу соответствия логический адрес → физический адрес (mapping table).
ОС думает, что пишет в "сектор 12345"
│
▼
┌───────────────────────┐
│ FTL │ (прошивка контроллера SSD)
│ таблица отображения: │
│ LBA 12345 → phys p17 │
│ LBA 12346 → phys p3 │
│ ... │
│ + сборка мусора │
│ + wear leveling │
└───────────────────────┘
│
▼
реальные блоки/страницы NAND flashОС видит ровный массив секторов; FTL под капотом раскидывает их по физическим страницам и держит карту соответствия.
Главный приём FTL — log-structured запись (запись «журналом»): новые данные всегда кладутся в очередную свободную стёртую страницу, по порядку, а в таблице обновляется указатель. Старая физическая страница помечается невалидной. Так мы избегаем дорогого «стереть блок ради одной страницы» на каждую запись.
Запись данных X в LBA 100, потом перезапись X' в тот же LBA 100:
шаг 1: пишем X в чистую page5, таблица: LBA100 → page5
шаг 2: НЕ стираем page5! пишем X' в чистую page6
таблица: LBA100 → page6 (page5 → НЕВАЛИДНА)
page5 [X невалид] page6 [X' валид] page7 [чисто] ...Запись всегда идёт в чистое место — быстро. А невалидные страницы копятся, и их надо когда-то освобождать.
Сборка мусора и wear leveling
Свободные стёртые страницы кончаются — их надо возвращать в оборот. Этим занимается garbage collection (сборка мусора) внутри FTL.
GC находит блок, где много невалидных страниц, переносит из него ещё живые (валидные) страницы в другое место, а затем стирает весь блок целиком — и тот снова готов к записи.
Блок A почти мусорный (V = валид, X = невалид, . = пусто):
[V][X][X][V][X][X][X][V] ← 3 валидных, 5 невалидных
GC:
1. скопировать 3 валидные в другой блок ← вот это и есть лишняя
2. стереть блок A целиком → [.][.][.][.][.][.][.][.] физическая запись
(write amplification!)Заметьте: шаг копирования валидных страниц — это та самая лишняя физическая запись, источник write amplification. Чем активнее перезаписывают SSD и чем он полнее, тем больше приходится копировать.
Вторая забота FTL — wear leveling (выравнивание износа). Беда флеша в том, что каждую ячейку можно стереть-перезаписать ограниченное число раз (от нескольких тысяч до десятков тысяч циклов, зависит от типа). После исчерпания ячейка перестаёт надёжно держать заряд.
Если бы FTL всегда писал в одни и те же физические блоки (например, туда, куда маппится часто меняющийся файл), эти блоки умерли бы быстро, а остальные остались бы как новенькие. Wear leveling специально размазывает запись равномерно по всем блокам — даже «холодные» данные, которые лежат и не меняются, периодически переносит, чтобы освободить мало изношенные блоки под горячую запись.
без wear leveling: с wear leveling:
блок0 ████████ (умер) блок0 ███
блок1 █ блок1 ███
блок2 █ блок2 ███ ← износ равномерный,
блок3 █ блок3 ███ ресурс всего SSD большеСлева горячий блок изнашивается в одиночку и выходит из строя; справа нагрузка размазана, и накопитель живёт дольше.
TRIM: дай SSD знать, что данные больше не нужны
Есть тонкая проблема на стыке файловой системы и FTL. Когда вы удаляете файл, файловая система (см. файлы и директории) просто помечает его блоки свободными в своих структурах — но самому SSD об этом не сообщает. С точки зрения FTL те страницы всё ещё «валидные данные», и при сборке мусора он будет честно их копировать. Зря.
Команда TRIM решает это: ОС явно говорит SSD «логические блоки с такими-то адресами больше не содержат нужных данных». Теперь FTL может пометить соответствующие физические страницы невалидными и не копировать их при GC.
Без TRIM: С TRIM:
ОС удалила файл ОС удалила файл → шлёт TRIM(LBA...)
SSD не знает → считает валидным SSD помечает страницы невалидными
GC копирует мусор (лишняя запись) GC пропускает их → меньше write amplif.Итог TRIM: меньше write amplification, меньше износ, выше скорость записи на заполненном диске.
Почему чтение быстрое, а запись — сложная
Соберём всё вместе. Случайное чтение на SSD почти такое же быстрое, как последовательное: головки нет, любую страницу достаём напрямую. Это огромный выигрыш над HDD, где случайное чтение упиралось в seek.
А вот запись сложнее, чем кажется снаружи. Из-за «стирать только блоками» SSD не перезаписывает на месте, а ведёт лог, копит невалидные страницы и периодически запускает сборку мусора со стиранием блоков. Поэтому:
- запись «рывками» (то быстро в кэш/свободные страницы, то проседает, когда включился GC);
- на почти полном SSD запись медленнее (мало свободных блоков, GC копирует больше);
- мелкая случайная запись хуже крупной последовательной (больше работы FTL).
HDD против SSD: сводная таблица
| Параметр | HDD | SSD |
|---|---|---|
| Принцип | пластины + головка | NAND flash, микросхемы |
| Движущиеся части | да | нет |
| Случайное чтение | медленно (seek+rotation) | быстро, ≈ последовательного |
| Последовательное чтение | хорошо | очень хорошо |
| Перезапись на месте | да, просто | нет: нужно стирать блок |
| Единица стирания | сектор перезаписывается | целый блок (сотни страниц) |
| Особый эффект | convoy/seek | write amplification |
| Износ | механический, со временем | ограничено циклов на ячейку |
| Спец-механизмы | планировщик I/O | FTL, GC, wear leveling, TRIM |
| Цена за ГБ | ниже | выше |
| Латентность | миллисекунды | микросекунды |
Таблица показывает: SSD выигрывает по скорости и латентности, но платит сложностью внутри (FTL) и ограниченным ресурсом перезаписи.
Какая операция во flash-памяти возможна только на уровне целого блока, а не отдельной страницы?
Что такое write amplification?
Что делает FTL (Flash Translation Layer)?
Зачем нужен wear leveling?
Что сообщает SSD команда TRIM?
Почему у SSD случайное чтение почти такое же быстрое, как последовательное?
Что спрашивают на собесе
- Почему во флеше нельзя просто перезаписать страницу и при чём тут стирание целого блока.
- Что такое write amplification, откуда оно берётся и как TRIM и запас свободного места его уменьшают.
- Зачем нужен FTL и какую таблицу он ведёт; что такое log-structured запись внутри SSD.
- Что делает wear leveling и почему без него SSD умрёт раньше времени.
- Почему у SSD случайное чтение почти равно последовательному, а запись ведёт себя «рывками» и проседает на заполненном диске.
- Что именно делает TRIM и на каком стыке (файловая система ↔ контроллер) он работает.