Magazyny danych typu klucz-wartość (KVS) to szybko rozwijający się segment świata baz danych NoSQL, który ostatnio zyskuje na popularności. Ponieważ posiadają najprostszy model danych – dowolny
Ciągi bajtów dla kluczy, w połączeniu z ciągiem bajtów dla odpowiadającej im wartości, można szybko włączyć do aplikacji o bardzo uproszczonych potrzebach w zakresie pamięci masowej. Ich interfejsy API są zazwyczaj równie proste, oferując jedynie bardzo prymitywne operacje i generując je z minimalnym narzutem.
Chociaż obecnie istnieje wiele zorientowanych na architekturę klient-serwer systemów KVS, takich jak memcache i redis, niniejszy test wykorzystuje wyłącznie wbudowane systemy KVS – tj. systemy KVS działające w całości w procesie aplikacji, zamiast wymagać komunikacji z procesem serwera bazy danych. Należy pamiętać, że systemy KVS tego typu nie są niczym nowym, należą do najstarszej rodziny oprogramowania do przechowywania danych. Na przykład BerkeleyDB wywodzi się z biblioteki pamięci masowej używanej w systemach Unix od lat 1980. XX wieku. Systemy KVS nie są ograniczone do użytku lokalnego (niesieciowego) – BerkeleyDB, LMDB i TokuDB są w pełni transakcyjne i od wielu lat są wykorzystywane jako podstawowe mechanizmy pamięci masowej dla serwerów takich jak MySQL i OpenLDAP. W rzeczywistości, w każdym innym magazynie danych wyższego poziomu, SQL, NoSQL czy innym, znajduje się wbudowany system KVS wykonujący faktyczną pracę.
Obecnie istnieje wiele popularnych aplikacji wykorzystujących KVS; np. kryptowaluty takie jak BitCoin używają KVS do przechowywania swojego blockchaina, który jest kompletnym rejestrem wszystkich transakcji. Wymagania projektowe prowadzące do wykorzystania KVS mogą obejmować bardzo wysokie wskaźniki transakcyjne (np. w przypadku dużych witryn e-commerce), bardzo niskie opóźnienia (np. w przypadku traderów o wysokiej częstotliwości), bardzo mały rozmiar (np. w przypadku telefonów komórkowych i innych ograniczonych środowisk) lub kombinację wszystkich trzech.
Testy przeprowadzane w tym miejscu pochodzą z kodu testowego pierwotnie napisanego w Google przez autorów LevelDB, a następnie przeniesionego do wielu innych silników baz danych przez Symas Corp [ 2 ] . Scenariusz testowy opiera się na testach testowych przeprowadzonych przez zespół RocksDB z Facebooka [ 3 ] oraz rozszerzonej wersji tych testów obejmującej więcej silników baz danych i przypadków użycia przeprowadzonych przez Symas Corp.
Test składa się z dwóch faz: fazy ładowania masowego, gdzie rekordy są ładowane w kolejności sekwencyjnej, a następnie fazy odczytu i zapisu, gdzie rekordy są udostępniane w kolejności losowej.
Wszystkie testowane silniki baz danych posiadają wbudowane magazyny klucz/wartość, które przechowują klucze w kolejności posortowanej, umożliwiając w ten sposób uporządkowane przeszukiwanie kluczy. W związku z tym wszystkie wykorzystują warianty struktury danych drzewa wyszukiwania. Zbiór danych testowych został dobrany tak, aby był około 5 razy większy niż pamięć RAM na serwerach testowych, aby pokazać wydajność przestrzeni dyskowej i wejścia/wyjścia każdego silnika bazy danych.
Chociaż podstawowe koncepcje są ze sobą powiązane, wszystkie silniki baz danych stosują bardzo zróżnicowane podejścia do implementacji. Większość z nich stara się zarządzać własnymi pamięciami podręcznymi danych, aby zminimalizować liczbę fizycznych operacji wejścia/wyjścia potrzebnych dla danego obciążenia. Każde podejście wiąże się z kompromisem między przestrzenią a czasem: wykorzystanie określonej ilości miejsca może zaoszczędzić określoną ilość czasu w danych okolicznościach, ale te oszczędności nieuchronnie zwrócą się w innym aspekcie:
RocksDB stawia na prostotę kosztem wydajności – wymaga najbardziej złożonego strojenia, a ponad 40 parametrów musi być ustawionych optymalnie, aby zapewnić najwyższą wydajność. TokuDB i BerkeleyDB podążają tą samą drogą pod względem złożoności konfiguracji. LevelDB i LMDB dążą do prostoty konfiguracji, a LMDB nie wymaga żadnego strojenia.
RocksDB, LevelDB i TokuDB koncentrują się na wydajności zapisu; ich projekty, odpowiednio, Log Structured Merge Tree (LSM) i Fractal Tree (FT), w dużym stopniu opierają się na konfigurowanych przez użytkownika buforach zapisu, które agregują dane w pamięci przed ich zapisaniem na dysku. Pozwala to na sekwencyjne zapisywanie danych, a tym samym na osiągnięcie najwyższej prędkości dostępnej dla danego nośnika. Wadą jest to, że idealna kolejność zapisu nie jest idealna dla odczytu, a wydajność odczytu jest znacznie gorsza. BerkeleyDB i LMDB to bardziej tradycyjne projekty B+tree, koncentrujące się bardziej na wydajności odczytu niż zapisu.
WiredTiger stara się pokryć wszystkie potrzeby, oferując w swojej bibliotece zarówno silniki LSM, jak i B+tree. Ta elastyczność wymaga również umiarkowanego poziomu złożoności konfiguracji.
RocksDB i LevelDB oferują funkcje strip, dążąc do maksymalnej wydajności zapisu. BerkeleyDB, LMDB, TokuDB i WiredTiger koncentrują się na niezawodności, oferując pełne transakcje ACID. W związku z tym cztery ostatnie silniki można łatwo dostosować do serwerów SQL, serwerów LDAP i innych aplikacji zorientowanych na transakcje, podczas gdy dwa pierwsze są mniej odpowiednie do takich zastosowań.
Wszystkie silniki, z wyjątkiem LMDB, używają własnych, jawnie zarządzanych buforów zapisu i pamięci podręcznej danych. LMDB korzysta wyłącznie z pamięci podręcznej stron systemu operacyjnego. W związku z tym wszystkie silniki, oprócz LMDB, mają znaczny narzut procesora ze względu na zarządzanie tymi buforami i wielokrotne kopiowanie danych między nimi. Na przykład, podczas pobierania rekordu z dysku, który nie był wcześniej buforowany, BerkeleyDB wykonuje co najmniej jedną kopię z pamięci do pamięci — najpierw dane są odczytywane przez system operacyjny do pamięci podręcznej stron, a następnie kopiowane do pamięci podręcznej stron BerkeleyDB. Na koniec dane są kopiowane z pamięci podręcznej BerkeleyDB do bufora dostarczonego przez użytkownika. LMDB jest systemem zerowej kopii; dane trafiają bezpośrednio z urządzenia pamięci masowej do pamięci podręcznej stron systemu operacyjnego, a następnie są przekazywane z powrotem użytkownikowi. Silniki takie jak TokuDB wykonują jeszcze więcej kopii, ponieważ dane na dysku przechodzą wielokrotne transformacje formatu, zanim zostaną zwrócone użytkownikowi. Każdy z tych etapów zużywa pamięć, więc architektura pamięci i kopii ma bezpośredni wpływ na liczbę rekordów, które mogą zmieścić się jednocześnie w pamięci RAM, a tym samym na to, jak duży zestaw roboczy może być obsługiwany z pełną prędkością. Ponieważ baza danych LMDB nie tworzy własnych kopii, może obsługiwać największy zestaw danych dla dowolnego rozmiaru pamięci RAM.
Niektóre silniki baz danych są zaprojektowane tak, aby były bardziej optymalne pod kątem operacji zapisu, a inne zoptymalizowane pod kątem odczytu. Faza ładowania masowego pokazuje ich najlepszą prędkość zapisu. Faza losowego odczytu i zapisu pokazuje ich najgorszą prędkość zapisu, gdzie pojedynczy wątek wykonuje zapis do losowo wybranych rekordów, podczas gdy wątki XX wykonują współbieżne odczyty do innych losowo wybranych rekordów. Pokazuje również, jak każdy silnik radzi sobie z odczytem w mocno obciążonym środowisku.
Jak wykazały testy Symas, względna wydajność każdego silnika bazy danych jest silnie uzależniona od rozmiaru używanych rekordów. Wybrany tutaj rozmiar rekordu stanowi kompromis między większymi rekordami (w których sprawdzają się tradycyjne silniki oparte na Btree) a mniejszymi rekordami (w których sprawdzają się projekty oparte na LSM).




Amazon