Pull to refresh

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

Open source *
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  - тут показаны 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.

Поскольку это первая статья в серии, приветствуются комментарии про состав - нужно ли больше примеров исходного кода для нетривиальных параграфов, как воспринимается сам текст, и т. д.
Tags:
Hubs:
Total votes 42: ↑39 and ↓3 +36
Views 28K
Comments Comments 38