Производительность Ceph — различия между версиями

Материал из YourcmcWiki
Перейти к: навигация, поиск
Строка 160: Строка 160:
 
Вот простой эксперимент: «кластер» из 2 SSD. Делаю strace одной из 4-х osd на одной из двух SSD во время запуска теста fio в 1 поток на 60 секунд (на эту осд пришлось по perf counter-ам 2676 запроса):
 
Вот простой эксперимент: «кластер» из 2 SSD. Делаю strace одной из 4-х osd на одной из двух SSD во время запуска теста fio в 1 поток на 60 секунд (на эту осд пришлось по perf counter-ам 2676 запроса):
 
<pre>
 
<pre>
grep -P 'pwrite.*= \d+$' osd0-trace.txt | wc
+
root@m2:~# grep -P 'pwrite.*= \d+$' osd0-trace.txt | wc
 
   6743  62638  975705
 
   6743  62638  975705
grep -P 'sync.*= \d+$' osd0-trace.txt | wc
+
root@m2:~# grep -P 'sync.*= \d+$' osd0-trace.txt | wc
 
   26912  175625 1412944
 
   26912  175625 1412944
 +
root@m2:~# grep -P 'pwrite.*= 4096$' osd0-trace.txt |wc
 +
  1651  15673  233742
 +
root@m2:~# grep -P 'pwrite.*= 8192$' osd0-trace.txt |wc
 +
  4921  45369  715623
 +
root@m2:~# grep -P 'pwrite.*= 12288$' osd0-trace.txt |wc
 +
    167    1560  25721
 +
root@m2:~# grep -P 'pwrite.*= 16384$' osd0-trace.txt |wc
 +
      4      36    619
 
</pre>
 
</pre>
  
 
То есть эта тварь на 2676 запроса записи сделала 6743 записи (ещё ладно), но при этом 26912 (!!!) синков!
 
То есть эта тварь на 2676 запроса записи сделала 6743 записи (ещё ладно), но при этом 26912 (!!!) синков!
 +
 +
Из общего числа 1651 запись была размером 4кб, 4921 — 8кб, 167 — 12кб и 4 — 16кб. Итого Write Amplification = 12.
  
 
== DPDK и SPDK ==
 
== DPDK и SPDK ==

Версия 17:12, 12 ноября 2018

Бенчмаркинг

Основные направления тестирования:

  • Линейное чтение/запись (большими блоками)
  • Пиковая производительность высоко-параллельного случайного чтения/записи мелкими блоками
  • Задержка однопоточного случайного чтения мелкими блоками (4-8 Кб)
  • Задержка однопоточной транзакционной записи мелкими блоками (4-8 Кб) — обычно последовательной, как в журнал СУБД, но в один поток это обычно слабо отличается от случайной

Задержки часто важнее простой пиковой производительности случайного чтения/записи, так как далеко не каждое приложение может загрузить диск при большом параллелизме / глубокой очереди (32-128 запросов).

Ceph — это SDS, его задержки всегда выше, чем у устройств при прямом доступе, и от этого никуда не денешься. В интернете есть доклад Nick Fisk «Low-Latency Ceph», в его исполнении Low latency это 0.7ms, то есть на лучший результат рассчитывать особенно не приходится. 0.7ms — это всего лишь примерно ~1500 iops в 1 поток.

Сначала прогоните fio на голом диске:

  • Перед тестированием отключите кэш записи диска: hdparm -W 0 /dev/sdX (SATA-диски через SATA или HBA), sdparm --set WCE=0 /dev/sdX (SAS-диски). Не совсем ясно, почему, но эта операция на серверных SSD может увеличить IOPS-ы на 2 порядка. Также см.ниже #Картина маслом «Тормозящий кэш».
  • Линейное чтение: fio -ioengine=libaio -direct=1 -invalidate=1 -name=test -bs=4M -iodepth=32 -rw=read -runtime=60 -filename=/dev/sdX
  • Линейная запись: fio -ioengine=libaio -direct=1 -invalidate=1 -name=test -bs=4M -iodepth=32 -rw=write -runtime=60 -filename=/dev/sdX
  • Задержка случайного чтения: fio -ioengine=libaio -sync=1 -direct=1 -invalidate=1 -name=test -bs=4k -iodepth=1 -rw=randread -runtime=60 -filename=/dev/sdX
  • Пиковые IOPS случайного чтения: fio -ioengine=libaio -direct=1 -invalidate=1 -name=test -bs=4k -iodepth=128 -rw=randread -runtime=60 -filename=/dev/sdX
  • Задержка записи в журнал: fio -ioengine=libaio -sync=1 -direct=1 -invalidate=1 -name=test -bs=4k -iodepth=1 -rw=write -runtime=60 -filename=/dev/sdX
  • Задержка случайной записи: fio -ioengine=libaio -sync=1 -direct=1 -invalidate=1 -name=test -bs=4k -iodepth=1 -rw=randwrite -runtime=60 -filename=/dev/sdX
  • Пиковые IOPS случайной записи: fio -ioengine=libaio -direct=1 -invalidate=1 -name=test -bs=4k -iodepth=128 -rw=randread -runtime=60 -filename=/dev/sdX

«А почему так мало…» — см.ниже.

Как тестировать Ceph после сборки:

  • rados bench лучше не использовать — он создаёт для тестирования очень мало объектов (в 1 поток всего 2, в 128 — несколько сотен). Из-за этого, например, на заполненном HDD результаты будут сильно оптимистичнее, так как снимается необходимость постоянного поиска метаданных в RocksDB.
  • fio в RBD: fio -ioengine=rbd -direct=1 -invalidate=1 -name=test -bs=4k -iodepth=128 -rw=randwrite -pool=rpool_hdd -runtime=60 -rbdname=testimg
    • лучше запускать с другого узла — результат будет в 1.5 раза лучше, видимо, из-за отсутствия переключения контекстов между ceph OSD и fio. можно запускать два теста параллельно, bluestore любит параллелизм — суммарный результат вполне может оказаться ещё в 2 раза лучше.
    • для мазохистов — параметр iodepth=128 поменять на iodepth=1. цифра будет раз в 20 хуже и она будет отражать то, сколько примерно TPS сможет выполнить ваша OLTP СУБД изнутри Ceph.
  • Встроенной утилитой rbd bench --io-size 4096 --io-threads 64 --io-total 10G --io-pattern rand --io-type write rpool_hdd/testimg
  • Можно тестировать и fio изнутри виртуалки, rbd драйвер нормально создаёт параллельную нагрузку — проверено.
  • Производительность может отличаться на заполненном и незаполненном RBD-образе. Но отличия небольшие, думать, что там будет разница в несколько раз — не нужно.
  • При тестировании случайной записи в ceph в один поток (fsync/fdatasync/sync/iodepth=1/rados bench -t 1) вы фактически всё время тестируете ОДИН диск. То есть, всё время тестируются разные диски, но в каждый отдельный момент времени запрос идёт только к одной placement group (тройке-четвёрке-пятёрке дисков).
  • Соответственно, вы не увидите 100 % утилизации дисков на хостах при тестировании в один поток, однопоточная нагрузка не может полностью загрузить кластер.

Производительность случайной записи

Warning Warning: Сначала плохая новость!

Важная особенность Ceph — вся запись, даже та, для которой никто этого явно не просит, ведётся транзакционно. То есть, никакая операция записи не завершается, пока она не записана в журналы всех OSD и не сделан fsync() диска. Так сделано, чтобы предотвращать RAID WRITE HOLE-подобные ситуации рассинхронизации данных между репликами при отключении питания, потере сети и т.п…

Сама запись на устройство и репликация с других OSD происходит отложенно и асинхронно, но какая разница — она все равно создаёт дополнительную нагрузку на устройства и притормаживает последующие запросы.

Всё это приводит к тому, что типичная настольная SSD-шка в Ceph выдаёт неприлично низкие IOPS-ы — обычно от 500 до 2000. И это при том, что при обычном тестировании почти любая SSD выдаёт > 20000 iops. Даже самый паршивый китайский noname выдаёт не менее 10000 iops. NVMe легко выжимает 150000 и больше. Но стоит начать использовать fsync… и та же NVMe выдаёт 600 iops (на 2.5 порядка меньше).

Note.svg Старый FileStore на плохих дисках быстрее BlueStore где-то в 1.5 раза. На хороших FileStore менее латентный, то есть, даёт больше iops в один поток (меньше задержки). Но это не значит, что его надо использовать — по остальным параметрам FileStore устарел. Например, снапшоты там работают со скоростью снапшотов LVM (очень медленно), нет частичной перезаписи объектов в EC-пулах, нет контрольных сумм (вследствие чего вообще нельзя использовать size=2). Также CPU filestore жрёт сильнее и пиковые параллельные IOPS-ы у него хуже bluestore.

В общем, чтобы понять, сколько у вас будет IOPS-ов на запись в Ceph, диски под него нужно тестировать с опцией fio fsync=1. Или fdatasync=1, если тестируете поверх ФС. Или можно sync=1 iodepth=1, эффект обычно почти тот же, что от fsync=1. Разница между опциями (см. man fio):

  • fsync=1 синхронизирует данные и метаданные тестируемого файла после каждой операции записи. Именно так работает BlueStore. Именно поэтому мы будем использовать именно эту опцию.
  • fdatasync=1 синхронизирует только данные (но не метаданные) тестируемого файла после каждой операции записи. Соответственно, от fsync=1 это отличается, только если тестируется файл в ФС, а не блочное устройство.
    Note.svg fdatasync=1 надо использовать, когда на диске уже есть ФС, а прогнать тест хочется. Результаты будут достаточно корректными.
  • sync=1 использует синхронный ввод/вывод, то есть, каждая операция начинается после завершения предыдущей. Но штука в том, что почти все движки fio при этом открывают файл с O_SYNC. А вот O_SYNC уже означает, что каждая операция записи внутри сопровождается аналогом fsync. Но при этом если iodepth > 1, то в очередь диска до синхронизации «пролезает» несколько операций и IOPS-ы растут, тест перестаёт быть эквивалентным записи с fsync=1.

Конденсаторы!

Нас спасёт такое чудо инженерной мысли, как SSD с конденсаторами (точнее, обычно суперконденсаторами — ионисторами). Которые на M.2 SSD, кстати, прекрасно видны невооружённым глазом:

Micron 5100 sata m2.jpg

Конденсаторы работают фактически как встроенный в SSD ИБП и позволяют SSD успеть сбросить кэш во флеш-память при потере питания. Таким образом кэш становится «энергонезависимым» — и таким образом SSD может просто игнорировать запросы fsync, так как точно знает, что данные из кэша в любом случае доедут до постоянной памяти.

При этом IOPS-ы транзакционной записи становятся равны IOPS-ами нетранзакционной.

Конденсаторы в официальных описаниях SSD-шек обычно называются «enhanced/advanced power loss protection». Этой характеристикой обладают, как правило, только «серверные» SSD, да и то не все. Например, в Intel DC S3100 конденсаторов нет, а в Intel DC S4600 есть.

Note.svg Это и является главным отличием серверных SSD от настольных. Обычному пользователю транзакции нужны редко — а вот на серверах живут СУБД, которым транзакции как раз нужны позарез.

То есть, под Ceph следует закупать только SSD с конденсаторами. Даже если рассматривать NVMe — NVMe без конденсаторов хуже, чем SATA с оными.

И ещё один вариант — Intel Optane. Это тоже SSD, но они основаны не на Flash памяти (не NAND и не NOR), а вообще на другой технологии, называющейся 3D XPoint. Хз, как она работает, но заявляются 550000 iops при полном отсутствии необходимости в стирании блоков, кэше и конденсаторах. Но а) в применении к Ceph — нужно проверять — не факт, что Ceph вообще сможет выжать из них их iops-ы б) вариант дорогой, раза в 3 дороже типичной SSD (1500$ за 960 гб, 500$ за 240 гб).

Контроллеры

  • SATA — это нормально, SAS не обязателен от слова «совсем». SATA за счёт того, что «не умничает», достаточно быстрая и точно лучше, чем старые RAID контроллеры.
  • Разница в IOPS между RAID и HBA/SATA может быть колоссальна. В производительность не самого нового RAID контроллера легко упереться. Плохо даже не то, что на 1 диск вы получите 48000 iops вместо 60000, хуже то, что при подключении 8 дисков вы получите 6000 iops на каждый диск вместо 60000, так как 48000 поделятся на всех. Также в RAID режиме увеличивается задержка в 1 поток.
  • Так что свой RAID контроллер либо переключите в режим passthrough (если он умеет), либо перепрошейте, чтобы умел, либо выкиньте в помойку и купите HBA («RAID без RAID-функционала», например, LSI 9300-8i). Это актуально для всех видов программных хранилок — Ceph, ZFS и т. п.
  • Если не выкинули RAID — отключайте все кэши контроллера, чтобы уменьшить влияние прослойки и не страдать при разряде батарейки / перемещении диска в другой сервер. Наверное, в теории можно выжить и с включенным кэшем, но это стрельба себе в ногу.
  • У HBA тоже есть предел IOPS. К примеру, у LSI 9211-8i это ~280000 iops на весь контроллер.
  • При подключении через SATA или HBA контроллер не забывайте для SATA дисков сделать hdparm -W 0 /dev/sdX, для SAS — sdparm --set WCE=0 /dev/sdX.
  • Для SAS и NVMe можно попробовать включить blk-mq. Для SATA обычно бесполезно или почти бесполезно.
  • Фактическая глубина очереди, используемая Ceph OSD при случайной записи, редко больше 10 (посмотреть можно при работе утилитой iostat -xmt 1).

Оценка производительности кластера

  • Оценка производительности кластера просто по спецификациям входящих в него SSD не совсем верна, причём в обе стороны:
    • Bluestore, видимо, за счёт параллелизма, выдаёт чуть больше iops-ов даже на SSD без конденсаторов, чем просто те же ssd могут выдать с fsync — я лично смог добиться от тестового пула на 3-х Intel SSDSC2KW256G8 8000 iops (случайная запись), хотя сами ssd с fsync выдают примерно 5500
    • И обратно, даже если SSD мегабыстрая, цеф — это огромный оверхед, он жрёт проц и никаких 220000 iops из одной SSD не выжмет. См. ниже #Пример теста от Micron — там в суперкрутом сетапе у них вышло всего 8750 iops в пересчёте на 1 NVMe (но это у них без SPDK/DPDK).
  • Правильное применение для всяких оптанов и супербыстрых NVMe это, видимо, журналы, но какое на практике увеличение iops-ов при добавлении условно оптана к тем же обычным серверным ssd — опять же, сходу не ясно.
  • Увеличение IOPS-ов Ceph обычно сопровождается увеличением жора CPU. CPU нужны хорошие. :)
    • Примечание: лучше больше гигагерцев, но меньше сокетов (и возможно ядер, если они неравноправные).
  • Однопоточные IOPS-ы целиком и полностью зависят от задержек. Важно всё — диски, процессор, контроллер, сетевая карта, коммутатор, Infiniband и SPDK/DPDK по возможности. Да и сам Bluestore латентнее, чем Filestore… хотя, с другой стороны, разница — условных 280 или 350 iops на дисках, которые сами по себе могут на порядок-два больше, что, имхо, непринципиально.
  • Лайфхак для ускорения однопоточной нагрузки: mdadm RAID 0 из RBD-образов внутри самой виртуалки.
  • Лайфхак для очень быстрых дисков: несколько OSD на одном диске.
  • Гипотетический монстр производительности в вакууме: мощные процы, Intel NVMe, Infiniband или Intel 25-40GbE, SPDK/DPDK.

Картина маслом «Тормозящий кэш»

Дано: 3 компа с 3x 7200rpm SATA HDD (в одном 4x HDD, но не суть важно), с 1 SSD (десктопным) под систему и ceph-mon и с 1 SSD (старым, но серверным, 25000 iops) под журналы. Не самая быстрая 10-гигабитная сеть — флуд пингом средний RTT (задержка) 0.098ms. Развёрнут Ceph + OpenNebula с KVM. Диски под Ceph отформатированы в Bluestore утилитой ceph-volume (то есть используется LVM). Диски виртуалок лежат в обычном реплицированном ceph pool с size=3.

Создаём Debian-виртуалку (настройки диска kvm по умолчанию — bus=virtio, cache=none), ставим fio, запускаем в ней тест на задержку транзакционной случайной записи: fio -ioengine=libaio -size=10G -sync=1 -direct=1 -name=test -bs=4k -iodepth=1 -rw=randwrite -runtime=60 -filename=./testfile (или можно не случайной, тогда rw=write, но результат идентичный).

  1. Настройки по умолчанию — все кэши дисков включены (везде hdparm -W 1, в /sys/block/*/queue/write_cache везде write back) — Ж О П А, iops=59, avg lat = 16.88ms
  2. Отключаю кэш записи SSD с журналами: hdparm -W 0 /dev/sdb — остаётся Ж О П А, iops=58, avg lat = 16.99ms
  3. Всем LVM-девайсам отключаю кэш записи: for i in /sys/block/dm-*; do echo write through > $i/queue/write_cache; done` — А Ф И Г Е Т Ь, iops=584, avg lat = 1.7ms
  4. Обратно включаю кэш SSDшке с журналами: hdparm -W 1 /dev/sdb — остаётся iops=582, avg lat = 1.7ms
  5. Откручиваю все отключения кэшей LVM: for i in /sys/block/dm-*; do echo write back > $i/queue/write_cache; done — обратно жопа, 57 iops, avg lat = 17.2ms
  6. Опять отключаю кэш журнальным LVM-девайсам: for i in `ls /dev/ceph-journals/lvol*`; do j=readlink $i; echo write through > /sys/block/${j##../}/queue/write_cache; done — никакого улучшения, всё та же жопа (но с ними это точно безопасно, так как они с конденсаторами :))
  7. Отключаю кэш HDD LVM-разделам (for i in `ls /dev/ceph-*/osd-block*`; do j=readlink $i; echo write through > /sys/block/${j##../}/queue/write_cache; done) — бинго, iops=603, avg lat = 1.65ms
  8. Ага. Простите. Обнаруживаю, что просто писать куда-то write through небезопасно без hdparm -W 0 /dev/sd*, так как https://www.kernel.org/doc/Documentation/block/queue-sysfs.txt - Writing to this file can change the kernels view of the device, but it doesn’t alter the device state. ок, добавляю for i in /dev/sd?; do hdparm -W 0 $i; done (отключаю все кэши) — результат похуже, iops=405, avg lat = 2.47ms — но это всё равно лучше, чем изначальная жопа.

Виртуалку, в которой тестировал — даже не перезапускал между тестами.

Картина маслом — «тормозящий кэш» (c).

Note.svg Мораль — отключайте кэш записи всем устройствам

Почему вообще Bluestore такой медленный?

Ведь старались-старались, уходили от двойной записи и журналирования журнала в filestore. И опять то же самое…

Речь о random iops. Вкратце: потому что писано всё кривым **ем.

Все мы знаем, что 1 7200rpm HDD может выдать примерно 100—120 iops. Дальше нам говорят — ну, там типа журналирование.

Ну ок, как мы рассуждаем — ну, типа, есть журнал, есть диск. Значит типа вроде как синхронно записало в журнал, потом асинхронно постепенно перенесло на диск. Значит, берём 100, умножаем на число дисков в кластере, делим на фактор репликации (3), делим на 1.5-2 (данные+журнал), мы же держим в уме, что наверняка там всё асинхронно и оптимизировано… Получаем, скажем, 100 * 9 дисков / 2 / 3 = 150 iops. Запускаем тест на собранном кластере — ОЙ. 30 iops. Как так-то?

А просто всё на самом деле довольно криво устроено.

Журнала собственного нет, вместо него RocksDB. Она как бы keyvalue база, но она же LSM, она же так и работает — типа до лимита пишет в память+журнал, потом когда упирается в лимит — делает compaction по уровням. По сути как бы БД-журнал. Вот они её как журнал и юзают.

В той же RocksDB метаданные, и их дохрена, ибо виртуальные клоны и всякое такое. Плюс чексуммы. Плюс идея, что надо побыстрее закоммитить запрос в минимальном виде и ответить «ок». Плюс два варианта записи — прямой и отложенный.

В итоге алгоритм записи примерно такой:

  1. Пришла транзакция — писнулась в журнал. Sync.
  2. Если запись мелкая, она откладывается и на время «откладывания» записывается в ту же rocksdb в отдельное место. Sync.
  3. Упёрлись в лимит сброса отложенных транзакций. Прочитали порцию из rocksdb, смержили, отправили в диск. С*ка опять sync, чтобы понимать, когда уже можно будет обновить метаданные.
  4. Обновляем метаданные. Sync.
  5. Если запись крупная, то она сразу отправляется на диск. Потом Sync.))). и потом тоже обновляем метаданные. Sync.

И ещё всё это приправлено тредами, блокировками…

То есть получается, что есть как бы журнал, есть данные и есть метаданные. И ещё если мелкие записи то есть очередь отложенной записи (в той же бд, но отдельная), её сначала надо писать, а потом сбрасывать и очищать. В итоге получается на 1 входящую транзакцию штук 5 реальных транзакций.

Без отложенной записи (на SSD это дефолт) поэтому всё и легче — остаётся только журнал, данные и метаданные — это хотя бы 3 транзакции, а не 5. А когда нет SSD, то казалось бы журнал хоть немного должен помогать, авотх*й, потому что seek-ов то меньше, но зато транзакций не 3, а 5.

Смысл видимо именно в отсутствии собственного журнала. Если бы он был и они работали как традиционная СУБД, то по идее, они могли бы делать примерно так:

  • приняли операцию
  • BEGIN
  • UPDATE метаданные
  • UPDATE данные
  • COMMIT = ОДНА синхронная запись в журнал
  • ответили клиенту «ок»

Ну а потом журнал просто потихоньку чистим. А у них так не получается — у них rocksdb, данные и метаданные в логически разных местах, а журналирование — фактически отдельная транзакция.

Вот простой эксперимент: «кластер» из 2 SSD. Делаю strace одной из 4-х osd на одной из двух SSD во время запуска теста fio в 1 поток на 60 секунд (на эту осд пришлось по perf counter-ам 2676 запроса):

root@m2:~# grep -P 'pwrite.*= \d+$' osd0-trace.txt | wc
   6743   62638  975705
root@m2:~# grep -P 'sync.*= \d+$' osd0-trace.txt | wc
  26912  175625 1412944
root@m2:~# grep -P 'pwrite.*= 4096$' osd0-trace.txt |wc
   1651   15673  233742
root@m2:~# grep -P 'pwrite.*= 8192$' osd0-trace.txt |wc
   4921   45369  715623
root@m2:~# grep -P 'pwrite.*= 12288$' osd0-trace.txt |wc
    167    1560   25721
root@m2:~# grep -P 'pwrite.*= 16384$' osd0-trace.txt |wc
      4      36     619

То есть эта тварь на 2676 запроса записи сделала 6743 записи (ещё ладно), но при этом 26912 (!!!) синков!

Из общего числа 1651 запись была размером 4кб, 4921 — 8кб, 167 — 12кб и 4 — 16кб. Итого Write Amplification = 12.

DPDK и SPDK

  • DPDK включается через ms_type=async+dpdk
  • SPDK включается для NVMe-шек передачей в качестве пути девайса spdk:<серийный номер pcie устройства>

Краткий экскурс в устройство SSD и флеш-памяти

Особенность флеш-памяти (NAND/NOR) заключается в том, что пишется она мелкими блоками (обычно 512 байт), но перед тем, как писать — блок нужно стереть. А вот стирать она умеет только крупные блоки («erase unit»), размером обычно 2-4 мегабайта, и стирание довольно медленное по отношению к записи. Кроме того, каждый erase unit ограничен числом стираний. После нескольких тысяч стираний (типичное значение для MLC) он физически выходит из строя. В более дешёвых чипах и MLC, TLC лимит стираний меньше, в более дорогих и SLC — больше. Таким образом, при «тупом» подходе перезапись флеш-памяти, во-первых, была бы очень медленной, а во-вторых, она бы быстро выходила из строя.

Но почему тогда SSD быстрые? А потому, что внутри SSD на самом деле есть очень мощный и умный контроллер (1-2 гигагерца, примерно как процессоры мобильников), и на нём выполняется нечто, называемое Flash Translation Layer — прошивка, которая переназначает каждый мелкий логический сектор в произвольное место диска. FTL всё время поддерживает некоторое количество свободных стёртых блоков, каждая случайная запись на самом деле идёт в новое место диска — в стёртую область. Поэтому запись быстрая. Одновременно FTL делает дефрагментацию свободного места и Wear Leveling (распределение износа), направляя запись и перемещая данные так, чтобы все блоки диска стирались примерно одинаковое количество раз. Кроме того, во всех SSD некоторый % реального места зарезервирован под Wear Leveling («overprovision»), а в хороших серверных SSD этот процент весьма большой — например, в Micron 5100 Max это +60 % ёмкости.

Именно из наличия FTL вытекает и проблема с энергонезависимостью и «power loss protection»-ом. Карты отображения секторов — это метаданные, которые при сбросе кэша тоже нужно сбрасывать в постоянную память, и именно этот сброс и вносит торможение в работу настольных SSD с fsync.

Бонус: USB-флешки

А почему тогда USB-флешки такие медленные? Случайная запись на флешку 512-байтными (или 4 Кб) блоками обычно идёт со скоростью 2-3 iops. А флеш-память там ровно та же, что в SSD — ну, может, более дешёвые вариации, но разница же не на порядки. Ответ кроется в том, что на флешках тоже есть FTL (и даже Wear Leveling), но по сравнению с SSD-шным он маленький и тупой. У него слабый процессор и мало памяти. Из-за малого объёма RAM контроллеру флешки, в отличие от контроллера SSD, негде хранить полную таблицу сопоставления виртуальных и реальных секторов — поэтому отображаются не сектора, а крупные блоки где-то по мегабайту или больше, а при записи есть лимит на количество «открытых» блоков. Как это происходит:

  • Допустим, вы пишете в сектор X.
  • Контроллер отображает блок, которому принадлежит этот сектор, на реальный блок, и «открывает» его — выделяет пустой блок, запоминает, что он «дочерний» для открытого и записывает туда один изменённый вами сектор.
  • Таким макаром можно открыть максимум N разных блоков; число N обычно очень маленькое — от 3 до 6.
  • Дальше если вы пишете следующий сектор из уже открытого блока — он просто записывается в его дочерний блок (что быстро).
  • Если же следующий записываемый сектор принадлежит другому блоку — какой-то из открытых блоков приходится закрывать и «сливать» содержимое дочернего блока с оригинальным.

Для копирования больших файлов на флешку, отформатированную в любую из стандартных файловых систем, двух блоков достаточно: в один открытый блок пишутся данные, во второй — метаданные записываемого файла. Запись последовательная, всё быстро. А вот при случайной записи вы перестаёте попадать в уже «открытые» блоки и каждая операция записи превращается в полное стирание. Тут-то и начинаются тормоза…

Пример теста от Micron

Пример самолётного сетапа от Micron с процами по полляма (2x Xeon Gold), 100-гбит сетью и 10x NVMe (с конденсаторами, ага) в каждом узле, 4 узла, репликация 2x: https://www.micron.com/resource-details/30c00464-e089-479c-8469-5ecb02cfe06f

Всего 350000 iops на запись в пике на весь кластер, при 100 % загрузке CPU. Казалось бы, довольно много, но если поделить 350000/40 osd — получится 8750 иопс на 1 osd. С учётом репликации на диски нагрузка двойная, выходит, 17500 иопс. Ок, журналы тоже удваивают нагрузку, итого — 35000 iops на запись смог выжать ceph из одной NVMe… которая сама по спеке может 260000 иопс в одиночку. Вот такой вот overhead.

UPD: Поправка: в чём микрон неправ — они не использовали SPDK и DPDK. Есть большая вероятность, что в их случае выигрыш мог быть в несколько раз.

Модели

  • Micron серий 5100/5200
  • HGST SN260