bcache i dm-cache (LVM cache)

12 sierpień 2015

Jednym z głównych wąskich gardeł każdego komputera jest czas dostępu do danych. Tradycyjne dyski twarde rzadko kiedy przewyższają 200MiB/s sekwencyjnego odczytu i są kompletnie żenujące w przypadku losowego I/O, są jednak bardzo przystępne cenowo względem oferowanej przestrzeni. Na przeciwnym biegunie znajdują się dyski SSD, ze stałym czasem dostępu do każdego fragmentu danych i horrendalnymi cenami za każdy gigabajt. Gdzieś po środku można znaleźć dyski hybrydowe, które w uproszczeniu wykorzystują mały dysk SSD w charakterze pamięci podręcznej dużego rotacyjnego dysku.

Zwykle są to sprzętowe rozwiązania, na temat których ciężko mi się wypowiedzieć, bo ich ceny są co najmniej niekorzystne. Są też dobre wieści: Linux od wydania 3.10 oferuje aż dwie software'owe opcje umożliwiające osiągnięcie tego samego efektu: bcache i dm-cache (albo: LVM cache).

bcache

Bcache (od block cache) potrzebuje do działania dwóch urządzeń: podstawowego, którego dane trafią do pamięci podręcznej oraz drugiego, które posłuży za tą pamięć. Z racji tego, że bcache operuje na urządzeniach blokowych, w charakterze backing device i caching device mogą służyć całe dyski, partycje, macierze czy urządzenia sieciowe.

Najprostszą konfiguracją jest spięcie całego HDD i SSD za pomocą bcache, a następnie utworzenia pożądanych partycji na urządzeniu bcache. Efekt końcowy będzie bardzo podobny do fizycznego hybrydowego dysku, z wyłączeniem dwóch użytych portów SATA. Większą elastyczność daje podział na partycje przed zastosowaniem bcache, co umożliwia zmianę metody buforowania.

Kopiując jednak sprzętową konfigurację mojego serwera i laptopa, załóżmy, że na dysku SSD (/dev/sda) utworzona została partycja systemowa sda1 oraz druga sda2, która zostanie użyta jako cache, natomiast dysk rotacyjny (/dev/sdb) w całości zostanie poświęcony na dane.

Na dobry początek należy zainstalować pakiet bcache-tools. Użytkownicy większości dystrybucji znajdą go w oficjalnych repozytoriach, z wyjątkiem Archa, który posiada odpowiedni PKGBUILD w AUR. (Przypuszczam, że głównym powodem tego stanu rzeczy jest fakt, że aktywność w repozytorium git projektu jest raczej znikoma.)

Zanim zacznie się instruować bcache które urządzenie do czego służy, należy zapisać metadane, żeby były one w ogóle rozpoznawalne przez kernel jako urządzenia bcache'a. Służy do tego polecenie make-bcache i przełączniki -B od backing device oraz -C od caching device:

make-bcache -B /dev/sdb
make-bcache -C /dev/sda2

Po wykonaniu powyższych poleceń można zauważyć pojawienie się nowego urządzenia /dev/bcache0 oraz katalogu /dev/bcache. Wspomniane urządzenie to nic innego jak dysk na dane, ale jeszcze bez skonfigurowanej pamięci podręcznej. W informacji zwróconej przez make-bcache -C można wyczytać nadane UUID, które musimy przekazać do bcache. (Można je także odczytać przy pomocy bcache-super-show.)

echo 47f20a96-4a63-42e2-a27a-ed5dc78604d3 > /sys/block/bcache0/bcache/attach

I to tyle. Wystarczy jeszcze utworzyć dowolny system plików na wypadkowym urządzeniu (tj. /dev/bcache0) i zamontować go w systemie.

Chociaż różnica w wydajności jest zauważalna gołym okiem (zwłaszcza po zmianie metody buforowania na writeback, którą lepiej sobie odpuścić jeśli nie posiada się kopii zapasowych i/lub stałego źródła zasilania) do uzyskania dokładnych statystyk na temat wykorzystania obu urządzeń można przyjrzeć się zawartości plików w /sys/block/bcache0/ albo posłużyć się gotowym skryptem.

dm-cache (lvmcache)

Nieco młodszym rozwiązaniem jest podsystem dm-cache, z którego można skorzystać za pomocą LVM. W tym przypadku oba urządzenia blokowe muszą znajdować się w jednej grupie woluminów. Załóżmy więc, że analogicznie do części poświęconej bcache, dysponujemy pustą partycją /dev/sda2 utworzoną na dysku SSD oraz całym dyskiem HDD /dev/sdb, a ponadto nie mamy praktycznej wiedzy dot. LVM i chcemy o tym przeczytać w tym wpisie.

  • Należy zatem zacząć od zainicjalizowania ww. urządzeń i utworzenia grupy woluminów:
pvcreate /dev/sda2 /dev/sdb
vgcreate vg0 /dev/sda2 /dev/sdb
  • Następnie potrzebne jest utworzenie wolumenu na dane, które w przyszłości będą trafiały do pamięci podręcznej na dysku SSD:
lvcreate -n data -L 250G vg0 /dev/sdb
  • Kolejnym krokiem jest utworzenie wolumenu na pamięć podręczną:
lvcreate -n data_cache -L 20G vg0 /dev/sda2
  • …oraz na metadane cache'u, który powinien być tysiąckrotnie mniejszy od wolumenu na cache, ale mieć co najmniej 8MB:
lvcreate -n data_cache_meta -L 20M vg0 /dev/sda2
  • Po poprzednim kroku wynik polecenia lvs -a vg0 powinien wyglądać podobnie do:
LV               VG   Attr       LSize   Pool Origin
data_cache       vg   -wi-a-----  20.00g
data_cache_meta  vg   -wi-a-----  20.00g
data             vg   -wi-a----- 250.00g
  • Pozostaje zakończyć dzieło i poinformować LVM, który wolumen jest czym:
lvconvert --type cache-pool --poolmetadata vg0/data_cache_meta vg0/data_cache
lvconvert --type cache --cachepool vg0/data_cache vg0/data

Analogicznie do bcache, można pokusić się o zmianę sposobu buforowania z writethrough na writeback:

lvconvert --cachemode writeback vg0/data_cache

Ponadto od lvm2 2.02.112 istnieje prosta metoda na podejrzenie statystyk związanych z pamięcią podręczną, na przykład:

# lvs -o cache_read_hits,cache_read_misses vg1/data
  CacheReadHits    CacheReadMisses
           1871198          1902686

Nie da się zaprzeczyć, że taka lista kroków może kogoś przestraszyć. Chciałem jednak zademonstrować o ile bardziej elastycznym pomysłem jest użycie tzw. lvmcache od zwykłego bcache. Tak naprawdę identyczny efekt można wykonać w co najmniej dwóch krokach mniej, ale zainteresowanych odsyłam do źródeł na końcu wpisu.

Podsumowanie

Czy można zatem jednoznacznie wskazać lepszą metodę na użycie dysku SSD jako cache HDD? Nie do końca. Na pierwszy rzut oka łatwo stwierdzić, że użytkownikom domowym bardziej przypadnie do gustu bcache. Należy jednak pamiętać, że nie jest on zbyt odporny na częste przełączanie się miedzy wersjami kernela (vide linux i linux-lts w Archu) – organoleptycznie sprawdziłem, że czyni to bcache bardzo nieszczęśliwym i jednocześnie chętnym na posłanie danych do /dev/null, co zresztą okazało się sędzią, katem, stryczkiem, trumną i gwoździem dla mojego Arch Linuksa w pracy. Ponadto sam podsystem w kernelu jest rozwijany głównie przez jednego programistę, który zajmował się także bcache-tools nim postanowił tego zaprzestać. Nie nastraja to zbyt optymistycznie.

Abstrahując nawet od liczby programistów za bcache i dm-cache/LVM, takie porównanie nasuwa obraz porównywania zwykłych partycji do LVM – kompletnie inny poziom możliwości. Nie znaczy to jednak, że LVM wychodzi z areny niezwyciężony. Co jeśli chce się objąć jednym dyskiem SSD kilka partycji dysku HDD? Ani bcache, ani dm-cache tego nie umożliwią – należałoby utworzyć wolumeny logiczne LVM na partycji /dev/bcache0. Czyli tak jak zwykle w Linuksie, wszystko zależy od tego, czego właściwie potrzebujemy.

Materiały źródłowe