Как работает ZFS — часть 1: vdev

в 2:28, , рубрики: illumos, vdev, zfs, метки: , ,

Vdev, или Virtual Device — это базовая единица, на которой строится массив данных ZFS (zpool). Для работы ZFS необходим как минимум один vdev — виртуальное устройство, которое позволяет случайный доступ к информации на уровне блоков.

Обычно, в качестве таких блоков используются целые диски или iScsi/FC LUNы (raw-disk vdev), но можно также использовать разделы дисков или файлы. Целые диски предпочтительнее, так как для них ZFS использует onboard write cache, в результате чего производительность записи может значительно улучшиться. Я пока не буду объяснять разницу между logical vdev и leaf vdev — это топик для отдельной статьи про то как работает RaidzN и zfs mirror.

src.illumos.org/source/xref/illumos-gate/usr/src/uts/common/fs/zfs/vdev_disk.c#314

if (vd->vdev_wholedisk == 1) 
{
	int wce = 1;
	/*
	 * If we own the whole disk, try to enable disk write caching.
	 * We ignore errors because it's OK if we can't do it.
	 */
	(void) ldi_ioctl(dvd->vd_lh, DKIOCSETWCE, (intptr_t)&wce, FKIOCTL, kcred, NULL);
}

В описании структуры виртуального устройства можно посмотреть какие параметры используются для чего, но я не собираюсь давать объяснение каждому — цель статьи — описание собственно того как ZFS хранит и пишет данные.

Каждый vdev разделен на логические разделы (metaslab), которые создаются автоматически при добавлении диска в ZFS Pool. Количество таких разделов зависит от размера vdev — примерно 200 разделов, но реально может быть и <128, и >256.

src.illumos.org/source/xref/illumos-gate/usr/src/uts/common/fs/zfs/vdev.c#1552

void
vdev_metaslab_set_size(vdev_t *vd)
{
	/*
	 * Aim for roughly 200 metaslabs per vdev.
	 */
	vd->vdev_ms_shift = highbit(vd->vdev_asize / 200);
	vd->vdev_ms_shift = MAX(vd->vdev_ms_shift, SPA_MAXBLOCKSHIFT);
}

Далее, внутри каждого metaslab'a есть так называемая карта места — space map. Она, как и большинство структур в ZFS, сделана на базе AVL-дерева, и позволяет эффективно работать с блоками занятого и свободного места независимо от размера блока и его места внутри metaslab'a.

Вот небольшой пример того, как ZFS видит эту структуру, используя zdb -mmm <poolname> — тут показаны 20 сегментов первого metaslab'a первого vdev'a в моем массиве:

Metaslabs:
        vdev          0
        metaslabs   323   offset                spacemap          free
        ---------------   -------------------   ---------------   -------------
        metaslab      0   offset            0   spacemap     34   free    84.8M
                          segments         72   maxsize   24.9M   freepct   16%
....
            [    73]    A  range: 001f00ba00-001ffffa00  size: ff4000
            [    74]    A  range: 001eff8200-001f00ae00  size: 012c00
            [    75]    A  range: 001b091200-001b130200  size: 09f000
            [    76]    A  range: 0012a84e00-0013a84e00  size: 1000000
            [    77]    A  range: 0013a84e00-00141bb800  size: 736a00
            [    78]    A  range: 0006b88000-0007707200  size: b7f200
            [    79] FREE: txg 9153385, pass 1
            [    80]    F  range: 0000282000-0000283200  size: 001200
            [    81]    F  range: 0000b47200-0000b48600  size: 001400
            [    82] ALLOC: txg 9252698, pass 1
            [    83]    A  range: 0000282000-0000283a00  size: 001a00
            [    84]    A  range: 0000b47200-0000b48e00  size: 001c00
            [    85]    A  range: 00008e6e00-00008e8a00  size: 001c00
            [    86] FREE: txg 9279297, pass 2
            [    87]    F  range: 00008e9000-00008e9c00  size: 000c00
            [    88] FREE: txg 9328122, pass 2
            [    89]    F  range: 0010a7be00-0010a7c800  size: 000a00
            [    90] FREE: txg 9444756, pass 1
            [    91]    F  range: 0009b14200-000ab14200  size: 1000000
            [    92]    F  range: 000ab14200-000b402000  size: 8ede00
....

Здесь заметно, что карта не сортирует сегменты по адресам блоков, размерам, и не пытается дефрагментировать свободное место на разделе. К сожалению, в ZFS полностью отсутствует возможность дефрагментации чего-либо вообще — это один из минусов использования бинарных деревьев и Copy-on-write принципа. Дефрагментировать ZFS — очень нетривиальная задача, и пока остается невозможной для большинства случаев.

Минимальный размер блока в space map — 512 байт (0х200), максимальный — 16МБ (0х1000000). Свободное место в space map не учитывается для блоков, которые никогда не были использованы, но учитывается для блоков которые когда-то были записаны, а потом удалены.

При записи в какой-либо конкретный metaslab составляется еще одна «карта» свободного места, free map, из которой берутся подходящие участки полностью свободного места, в которое можно записать данные последовательно, не тратя время на disk seek. В предыдущем примере видно, что всего у меня 84МБ «free» места в данном разделе, но самый большой непрерывный свободный участок — 24.9МБ «maxfree».

Один из последних шагов в ZFS write pipeline — это собственно определить, куда записать новые данные, что (надеюсь) будет топиком следующего поста про metaslab allocator.

Поскольку это первая статья в серии, приветствуются комментарии про состав — нужно ли больше примеров исходного кода для нетривиальных параграфов, как воспринимается сам текст, и т. д.

Автор: gatech

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js