Każda kolekcja danych jest podzielona na segmenty. Każdy segment ma niezależne przechowywanie wektorów, przechowywanie ładunku oraz indeks.

Dane w segmentach zazwyczaj się nie pokrywają. Jednak przechowywanie tego samego punktu w różnych segmentach nie powoduje problemów, ponieważ wyszukiwanie zawiera mechanizm deduplikacji.

Segmenty obejmują przechowywanie wektorów, przechowywanie ładunku, indeks wektorów, indeks ładunku oraz maper ID do przechowywania relacji między wewnętrznymi i zewnętrznymi ID.

Segmenty mogą być "dodawalne" lub "niemożliwe do dodania", w zależności od rodzaju przechowywania i indeksu używanego. Można swobodnie dodawać, usuwać i wyszukiwać dane w "dodawalnych" segmentach, podczas gdy "niemożliwe do dodania" segmenty mogą jedynie odczytywać i usuwać dane.

Konfiguracja segmentów w kolekcji może być inna i niezależna od siebie, ale wymagany jest co najmniej jeden "dodawalny" segment.

Przechowywanie wektorowe

W zależności od potrzeb aplikacji, Qdrant może używać jednej z następujących opcji przechowywania danych. Wybór musi uwzględniać prędkość wyszukiwania i użycie pamięci RAM.

Przechowywanie w pamięci - Przechowuje wszystkie wektory w pamięci RAM, co zapewnia największą szybkość, ponieważ dostęp do dysku jest wymagany jedynie podczas trwałości.

Przechowywanie w memmap - Tworzy wirtualną przestrzeń adresową związana z plikiem na dysku. Zmapowane pliki nie są bezpośrednio wczytywane do pamięci RAM, lecz dostęp do nich odbywa się za pomocą buforowania stron. Ten sposób pozwala na elastyczne wykorzystanie dostępnej pamięci. Przy wystarczającej ilości pamięci RAM, prędkość jest prawie tak szybka jak przechowywanie w pamięci.

Konfiguracja przechowywania w memmap

Istnieją dwa sposoby konfigurowania użycia memmap (również znane jako przechowywanie na dysku):

  • Ustaw opcję on_disk dla wektorów w interfejsie API tworzenia kolekcji:

Tylko dotyczy wersji v1.2.0 i nowszych

PUT /collections/{nazwa_kolekcji}

{
    "wektory": {
      "rozmiar": 768,
      "odległość": "Cosine",
      "on_disk": true
    }
}

Spowoduje to utworzenie kolekcji, w której wszystkie wektory natychmiast przechowywane są w pamięci memmap. Jest to zalecane podejście, gdy instancja Qdrant używa szybkich dysków i musi obsługiwać duże kolekcje.

  • Ustaw opcję memmap_threshold_kb. Ta opcja ustawia próg konwertowania segmentów na przechowywanie memmap.

Istnieją dwa sposoby zaimplementowania tego:

  1. Ustaw próg globalnie w pliku konfiguracyjnym. Nazwa parametru to memmap_threshold_kb.
  2. Ustaw próg indywidualnie dla każdej kolekcji podczas tworzenia lub aktualizacji.
PUT /collections/{nazwa_kolekcji}

{
    "wektory": {
      "rozmiar": 768,
      "odległość": "Cosine"
    },
    "konfiguracja_optymalizatorów": {
        "memmap_threshold": 20000
    }
}

Zasada ustalania parametru progu memmap jest prosta:

  • Jeśli scenariusz użycia jest zrównoważony – ustaw próg memmap taki sam jak indexing_threshold (domyślnie jest to 20000). W takim przypadku optymalizator nie przeprowadzi żadnych dodatkowych przebiegów i zoptymalizuje wszystkie progi naraz.
  • Jeśli obciążenie zapisem jest duże, a pamięć RAM jest niska – ustaw próg memmap niższy niż indexing_threshold, na przykład 10000. W takim przypadku optymalizator najpierw przekonwertuje segmenty na przechowywanie memmap, a następnie zastosuje indeksowanie.

Dodatkowo, można używać przechowywania memmap nie tylko dla wektorów, ale także dla indeksów HNSW. Aby włączyć tę funkcję, ustaw parametr hnsw_config.on_disk na true podczas tworzenia kolekcji.

PUT /collections/{nazwa_kolekcji}

{
    "wektory": {
      "rozmiar": 768,
      "odległość": "Cosine"
    },
    "konfiguracja_optymalizatorów": {
        "memmap_threshold": 20000
    },
    "konfiguracja_hnsw": {
        "on_disk": true
    }
}

Przechowywanie ładunku

Qdrant obsługuje dwa rodzaje przechowywania ładunku: InMemory oraz OnDisk.

Przechowywanie ładunku InMemory organizuje dane ładunku w taki sam sposób, jak wektory w pamięci. Dane ładunku są ładowane do pamięci podczas uruchamiania usługi, podczas gdy dysk i RocksDB są wykorzystywane jedynie do celów trwałości. Ten rodzaj przechowywania jest bardzo szybki, ale może wymagać dużej ilości miejsca w pamięci do przechowywania wszystkich danych, zwłaszcza jeśli ładunek obejmuje duże wartości (takie jak streszczenia tekstów czy nawet obrazy).

Dla dużych wartości ładunku zaleca się stosowanie przechowywania ładunku OnDisk. Ten rodzaj przechowywania bezpośrednio odczytuje i zapisuje ładunek do RocksDB, nie wymagając dużej ilości pamięci do przechowywania. Wady to jednak opóźnienie dostępu. Jeśli potrzebujesz zapytań wektorów na podstawie warunków ładunku, sprawdzanie wartości przechowywanych na dysku może potrwać zbyt długo. W takim przypadku zalecamy utworzenie indeksu ładunku dla każdego pola używanego do filtrowania warunków, aby uniknąć dostępu do dysku. Po utworzeniu indeksu pola, Qdrant zawsze będzie przechowywał wszystkie wartości indeksowanego pola w pamięci, niezależnie od rodzaju przechowywania ładunku.

Rodzaj przechowywania ładunku można określić przez plik konfiguracyjny lub poprzez użycie parametru kolekcji on_disk_payload podczas tworzenia kolekcji.

Kontrola wersji

Aby zapewnić integralność danych, Qdrant wykonuje wszystkie zmiany danych w dwóch etapach. Najpierw dane są zapisywane do dziennika zapisów (WAL), który sortuje i przypisuje kolejne numery do wszystkich operacji.

Gdy zmiany są dodane do WAL, nie są utracone nawet w przypadku awarii zasilania. Następnie zmiany trafiają do segmentu. Każdy segment przechowuje najnowszą wersję zmian zastosowanych do niego, a także wersję każdego pojedynczego punktu. Jeśli numer sekwencyjny nowej zmiany jest mniejszy niż bieżąca wersja punktu, aktualizator zignoruje tę zmianę. Ten mechanizm pozwala Qdrantowi skutecznie odzyskać przechowywanie z WAL w przypadku awaryjnego zamykania.