Opanuj optymalizację zapytań Neo4j dla szybszej i wydajniejszej pracy grafowej bazy danych. Poznaj najlepsze praktyki Cypher, strategie indeksowania, techniki profilowania i zaawansowane metody optymalizacji.
Grafowe bazy danych: Optymalizacja zapytań w Neo4j – Kompleksowy przewodnik
Grafowe bazy danych, w szczególności Neo4j, stają się coraz bardziej popularne do zarządzania i analizowania połączonych danych. Jednak wraz ze wzrostem zbiorów danych, kluczowa staje się wydajna realizacja zapytań. Ten przewodnik stanowi kompleksowy przegląd technik optymalizacji zapytań w Neo4j, umożliwiając budowanie wysoko wydajnych aplikacji grafowych.
Zrozumienie znaczenia optymalizacji zapytań
Bez odpowiedniej optymalizacji zapytań, zapytania Neo4j mogą stać się powolne i zasobochłonne, wpływając na wydajność i skalowalność aplikacji. Optymalizacja obejmuje połączenie zrozumienia wykonywania zapytań Cypher, wykorzystania strategii indeksowania oraz stosowania narzędzi do profilowania wydajności. Celem jest zminimalizowanie czasu wykonania i zużycia zasobów przy jednoczesnym zapewnieniu dokładnych wyników.
Dlaczego optymalizacja zapytań ma znaczenie
- Poprawiona wydajność: Szybsze wykonywanie zapytań prowadzi do lepszej responsywności aplikacji i bardziej pozytywnego doświadczenia użytkownika.
- Zmniejszone zużycie zasobów: Zoptymalizowane zapytania zużywają mniej cykli procesora, pamięci i operacji wejścia/wyjścia na dysku, co zmniejsza koszty infrastruktury.
- Zwiększona skalowalność: Wydajne zapytania pozwalają Twojej bazie danych Neo4j obsługiwać większe zbiory danych i większe obciążenie zapytań bez degradacji wydajności.
- Lepsza współbieżność: Zoptymalizowane zapytania minimalizują konflikty blokad i rywalizację o zasoby, poprawiając współbieżność i przepustowość.
Podstawy języka zapytań Cypher
Cypher to deklaratywny język zapytań Neo4j, zaprojektowany do wyrażania wzorców grafowych i relacji. Zrozumienie Cyphera jest pierwszym krokiem do skutecznej optymalizacji zapytań.
Podstawowa składnia Cypher
Oto krótki przegląd podstawowych elementów składni Cypher:
- Węzły: Reprezentują encje w grafie. Ujęte w nawiasy okrągłe:
(node)
. - Relacje: Reprezentują połączenia między węzłami. Ujęte w nawiasy kwadratowe i połączone myślnikami i strzałkami:
-[relationship]->
lub<-[relationship]-
lub-[relationship]-
. - Etykiety: Kategoryzują węzły. Dodawane po zmiennej węzła:
(node:Label)
. - Właściwości: Pary klucz-wartość powiązane z węzłami i relacjami:
{property: 'value'}
. - Słowa kluczowe: Takie jak
MATCH
,WHERE
,RETURN
,CREATE
,DELETE
,SET
,MERGE
, itp.
Najczęstsze klauzule Cypher
- MATCH: Używana do znajdowania wzorców w grafie.
MATCH (a:Person)-[:FRIENDS_WITH]->(b:Person) WHERE a.name = 'Alice' RETURN b
- WHERE: Filtruje wyniki na podstawie warunków.
MATCH (n:Product) WHERE n.price > 100 RETURN n
- RETURN: Określa, jakie dane mają zostać zwrócone z zapytania.
MATCH (n:City) RETURN n.name, n.population
- CREATE: Tworzy nowe węzły i relacje.
CREATE (n:Person {name: 'Bob', age: 30})
- DELETE: Usuwa węzły i relacje.
MATCH (n:OldNode) DELETE n
- SET: Aktualizuje właściwości węzłów i relacji.
MATCH (n:Product {name: 'Laptop'}) SET n.price = 1200
- MERGE: Znajduje istniejący węzeł lub relację albo tworzy nowy, jeśli nie istnieje. Przydatne do operacji idempotentnych.
MERGE (n:Country {name: 'Germany'})
- WITH: Pozwala na łączenie wielu klauzul
MATCH
i przekazywanie wyników pośrednich.MATCH (a:Person)-[:FRIENDS_WITH]->(b:Person) WITH a, count(b) AS friendsCount WHERE friendsCount > 5 RETURN a.name, friendsCount
- ORDER BY: Sortuje wyniki.
MATCH (n:Movie) RETURN n ORDER BY n.title
- LIMIT: Ogranicza liczbę zwracanych wyników.
MATCH (n:User) RETURN n LIMIT 10
- SKIP: Pomija określoną liczbę wyników.
MATCH (n:Product) RETURN n SKIP 5 LIMIT 10
- UNION/UNION ALL: Łączy wyniki wielu zapytań.
MATCH (n:Movie) WHERE n.genre = 'Action' RETURN n.title UNION ALL MATCH (n:Movie) WHERE n.genre = 'Comedy' RETURN n.title
- CALL: Wykonuje procedury składowane lub funkcje zdefiniowane przez użytkownika.
CALL db.index.fulltext.createNodeIndex("PersonNameIndex", ["Person"], ["name"])
Plan wykonania zapytania w Neo4j
Zrozumienie, w jaki sposób Neo4j wykonuje zapytania, jest kluczowe dla optymalizacji. Neo4j używa planu wykonania zapytania, aby określić optymalny sposób pobierania i przetwarzania danych. Możesz wyświetlić plan wykonania za pomocą poleceń EXPLAIN
i PROFILE
.
EXPLAIN a PROFILE
- EXPLAIN: Pokazuje logiczny plan wykonania bez faktycznego uruchamiania zapytania. Pomaga zrozumieć kroki, które Neo4j podejmie, aby wykonać zapytanie.
- PROFILE: Wykonuje zapytanie i dostarcza szczegółowych statystyk dotyczących planu wykonania, w tym liczbę przetworzonych wierszy, trafień w bazę danych i czas wykonania dla każdego kroku. Jest to nieocenione przy identyfikowaniu wąskich gardeł wydajności.
Interpretacja planu wykonania
Plan wykonania składa się z serii operatorów, z których każdy wykonuje określone zadanie. Typowe operatory to:
- NodeByLabelScan: Skanuje wszystkie węzły z określoną etykietą.
- IndexSeek: Używa indeksu do znalezienia węzłów na podstawie wartości właściwości.
- Expand(All): Przechodzi przez relacje w celu znalezienia połączonych węzłów.
- Filter: Stosuje warunek filtrujący do wyników.
- Projection: Wybiera określone właściwości z wyników.
- Sort: Porządkuje wyniki.
- Limit: Ogranicza liczbę wyników.
Analiza planu wykonania może ujawnić nieefektywne operacje, takie jak pełne skanowanie węzłów lub niepotrzebne filtrowanie, które można zoptymalizować.
Przykład: Analiza planu wykonania
Rozważmy następujące zapytanie Cypher:
EXPLAIN MATCH (p:Person {name: 'Alice'})-[:FRIENDS_WITH]->(f:Person) RETURN f.name
Wynik EXPLAIN
może pokazać NodeByLabelScan
, a następnie Expand(All)
. Oznacza to, że Neo4j skanuje wszystkie węzły Person
, aby znaleźć 'Alice', zanim przejdzie przez relacje FRIENDS_WITH
. Bez indeksu na właściwości name
jest to nieefektywne.
PROFILE MATCH (p:Person {name: 'Alice'})-[:FRIENDS_WITH]->(f:Person) RETURN f.name
Uruchomienie PROFILE
dostarczy statystyk wykonania, ujawniając liczbę trafień w bazę danych i czas poświęcony na każdą operację, co dodatkowo potwierdzi wąskie gardło.
Strategie indeksowania
Indeksy są kluczowe dla optymalizacji wydajności zapytań, pozwalając Neo4j na szybkie lokalizowanie węzłów i relacji na podstawie wartości właściwości. Bez indeksów Neo4j często ucieka się do pełnych skanów, które są powolne dla dużych zbiorów danych.
Typy indeksów w Neo4j
- Indeksy B-drzewa (B-tree Indexes): Standardowy typ indeksu, odpowiedni do zapytań o równość i zakres. Tworzony automatycznie dla ograniczeń unikalności lub ręcznie za pomocą polecenia
CREATE INDEX
. - Indeksy pełnotekstowe (Fulltext Indexes): Zaprojektowane do wyszukiwania danych tekstowych za pomocą słów kluczowych i fraz. Tworzone za pomocą procedury
db.index.fulltext.createNodeIndex
lubdb.index.fulltext.createRelationshipIndex
. - Indeksy punktowe (Point Indexes): Zoptymalizowane pod kątem danych przestrzennych, umożliwiające wydajne zapytania oparte na współrzędnych geograficznych. Tworzone za pomocą procedury
db.index.point.createNodeIndex
lubdb.index.point.createRelationshipIndex
. - Indeksy zakresowe (Range Indexes): Specjalnie zoptymalizowane pod kątem zapytań zakresowych, oferujące poprawę wydajności w porównaniu z indeksami B-drzewa dla niektórych obciążeń. Dostępne w Neo4j 5.7 i nowszych.
Tworzenie i zarządzanie indeksami
Możesz tworzyć indeksy za pomocą poleceń Cypher:
Indeks B-drzewa:
CREATE INDEX PersonName FOR (n:Person) ON (n.name)
Indeks złożony:
CREATE INDEX PersonNameAge FOR (n:Person) ON (n.name, n.age)
Indeks pełnotekstowy:
CALL db.index.fulltext.createNodeIndex("PersonNameIndex", ["Person"], ["name"])
Indeks punktowy:
CALL db.index.point.createNodeIndex("LocationIndex", ["Venue"], ["latitude", "longitude"], {spatial.wgs-84: true})
Możesz wyświetlić istniejące indeksy za pomocą polecenia SHOW INDEXES
:
SHOW INDEXES
I usuwać indeksy za pomocą polecenia DROP INDEX
:
DROP INDEX PersonName
Najlepsze praktyki dotyczące indeksowania
- Indeksuj często odpytywane właściwości: Zidentyfikuj właściwości używane w klauzulach
WHERE
i wzorcachMATCH
. - Używaj indeksów złożonych dla wielu właściwości: Jeśli często odpytujesz o wiele właściwości jednocześnie, utwórz indeks złożony.
- Unikaj nadmiernego indeksowania: Zbyt wiele indeksów może spowolnić operacje zapisu. Indeksuj tylko te właściwości, które są faktycznie używane w zapytaniach.
- Weź pod uwagę kardynalność właściwości: Indeksy są bardziej efektywne dla właściwości o wysokiej kardynalności (tj. wielu unikalnych wartościach).
- Monitoruj użycie indeksów: Użyj polecenia
PROFILE
, aby sprawdzić, czy indeksy są używane przez Twoje zapytania. - Okresowo przebudowuj indeksy: Z biegiem czasu indeksy mogą ulec fragmentacji. Ich przebudowa może poprawić wydajność.
Przykład: Indeksowanie dla wydajności
Rozważmy graf sieci społecznościowej z węzłami Person
i relacjami FRIENDS_WITH
. Jeśli często wyszukujesz znajomych określonej osoby po imieniu, utworzenie indeksu na właściwości name
węzła Person
może znacznie poprawić wydajność.
CREATE INDEX PersonName FOR (n:Person) ON (n.name)
Po utworzeniu indeksu, następujące zapytanie wykona się znacznie szybciej:
MATCH (p:Person {name: 'Alice'})-[:FRIENDS_WITH]->(f:Person) RETURN f.name
Użycie PROFILE
przed i po utworzeniu indeksu zademonstruje poprawę wydajności.
Techniki optymalizacji zapytań Cypher
Oprócz indeksowania, istnieje kilka technik optymalizacji zapytań Cypher, które mogą poprawić wydajność.
1. Używanie poprawnego wzorca MATCH
Kolejność elementów we wzorcu MATCH
może znacząco wpłynąć na wydajność. Zacznij od najbardziej selektywnych kryteriów, aby zmniejszyć liczbę węzłów i relacji, które muszą zostać przetworzone.
Niewydajne:
MATCH (a)-[:RELATED_TO]->(b:Product) WHERE b.category = 'Electronics' AND a.city = 'London' RETURN a, b
Zoptymalizowane:
MATCH (b:Product {category: 'Electronics'})<-[:RELATED_TO]-(a {city: 'London'}) RETURN a, b
W wersji zoptymalizowanej zaczynamy od węzła Product
z właściwością category
, co prawdopodobnie będzie bardziej selektywne niż skanowanie wszystkich węzłów, a następnie filtrowanie według miasta.
2. Minimalizowanie transferu danych
Unikaj zwracania niepotrzebnych danych. Wybieraj tylko te właściwości, których potrzebujesz w klauzuli RETURN
.
Niewydajne:
MATCH (n:User {country: 'USA'}) RETURN n
Zoptymalizowane:
MATCH (n:User {country: 'USA'}) RETURN n.name, n.email
Zwracanie tylko właściwości name
i email
zmniejsza ilość przesyłanych danych, poprawiając wydajność.
3. Używanie WITH dla wyników pośrednich
Klauzula WITH
pozwala na łączenie wielu klauzul MATCH
i przekazywanie wyników pośrednich. Może to być przydatne do dzielenia złożonych zapytań na mniejsze, bardziej zarządzalne kroki.
Przykład: Znajdź wszystkie produkty, które są często kupowane razem.
MATCH (o:Order)-[:CONTAINS]->(p:Product)
WITH o, collect(p) AS products
WHERE size(products) > 1
UNWIND products AS product1
UNWIND products AS product2
WHERE id(product1) < id(product2)
WITH product1, product2, count(*) AS co_purchases
ORDER BY co_purchases DESC
LIMIT 10
RETURN product1.name, product2.name, co_purchases
Klauzula WITH
pozwala nam zebrać produkty w każdym zamówieniu, odfiltrować zamówienia z więcej niż jednym produktem, a następnie znaleźć wspólne zakupy między różnymi produktami.
4. Wykorzystanie zapytań sparametryzowanych
Zapytania sparametryzowane zapobiegają atakom typu Cypher injection i poprawiają wydajność, pozwalając Neo4j na ponowne wykorzystanie planu wykonania zapytania. Używaj parametrów zamiast osadzania wartości bezpośrednio w ciągu zapytania.
Przykład (używając sterowników Neo4j):
session.run("MATCH (n:Person {name: $name}) RETURN n", {name: 'Alice'})
Tutaj $name
jest parametrem przekazywanym do zapytania. Pozwala to Neo4j na buforowanie planu wykonania zapytania i ponowne jego użycie dla różnych wartości name
.
5. Unikanie iloczynów kartezjańskich
Iloczyny kartezjańskie występują, gdy masz wiele niezależnych klauzul MATCH
w zapytaniu. Może to prowadzić do generowania dużej liczby niepotrzebnych kombinacji, co może znacznie spowolnić wykonanie zapytania. Upewnij się, że Twoje klauzule MATCH
są ze sobą powiązane.
Niewydajne:
MATCH (a:Person {city: 'London'})
MATCH (b:Product {category: 'Electronics'})
RETURN a, b
Zoptymalizowane (jeśli istnieje relacja między Person a Product):
MATCH (a:Person {city: 'London'})-[:PURCHASED]->(b:Product {category: 'Electronics'})
RETURN a, b
W wersji zoptymalizowanej używamy relacji (PURCHASED
) do połączenia węzłów Person
i Product
, unikając iloczynu kartezjańskiego.
6. Używanie procedur i funkcji APOC
Biblioteka APOC (Awesome Procedures On Cypher) dostarcza zbiór użytecznych procedur i funkcji, które mogą rozszerzyć możliwości Cyphera i poprawić wydajność. APOC zawiera funkcjonalności do importu/eksportu danych, refaktoryzacji grafu i wiele więcej.
Przykład: Użycie apoc.periodic.iterate
do przetwarzania wsadowego
CALL apoc.periodic.iterate(
"MATCH (n:OldNode) RETURN n",
"CREATE (newNode:NewNode) SET newNode = n.properties WITH n DELETE n",
{batchSize: 1000, parallel: true}
)
Ten przykład demonstruje użycie apoc.periodic.iterate
do migracji danych z OldNode
do NewNode
w partiach. Jest to znacznie bardziej wydajne niż przetwarzanie wszystkich węzłów w jednej transakcji.
7. Rozważ konfigurację bazy danych
Konfiguracja Neo4j również może wpływać na wydajność zapytań. Kluczowe konfiguracje to:
- Rozmiar sterty (Heap Size): Przydziel wystarczającą ilość pamięci sterty dla Neo4j. Użyj ustawienia
dbms.memory.heap.max_size
. - Pamięć podręczna stron (Page Cache): Pamięć podręczna stron przechowuje często używane dane w pamięci. Zwiększ jej rozmiar (
dbms.memory.pagecache.size
) dla lepszej wydajności. - Logowanie transakcji: Dostosuj ustawienia logowania transakcji, aby zrównoważyć wydajność i trwałość danych.
Zaawansowane techniki optymalizacji
Dla złożonych aplikacji grafowych mogą być konieczne bardziej zaawansowane techniki optymalizacji.
1. Modelowanie danych grafowych
Sposób modelowania danych grafowych może mieć znaczący wpływ na wydajność zapytań. Rozważ następujące zasady:
- Wybierz odpowiednie typy węzłów i relacji: Zaprojektuj schemat grafu tak, aby odzwierciedlał relacje i encje w Twojej domenie danych.
- Używaj etykiet efektywnie: Używaj etykiet do kategoryzacji węzłów i relacji. Pozwala to Neo4j na szybkie filtrowanie węzłów na podstawie ich typu.
- Unikaj nadmiernego użycia właściwości: Chociaż właściwości są użyteczne, ich nadmierne użycie może spowolnić wydajność zapytań. Rozważ użycie relacji do reprezentowania danych, które są często odpytywane.
- Denormalizuj dane: W niektórych przypadkach denormalizacja danych może poprawić wydajność zapytań poprzez zmniejszenie potrzeby stosowania złączeń. Bądź jednak świadomy redundancji i spójności danych.
2. Używanie procedur składowanych i funkcji zdefiniowanych przez użytkownika
Procedury składowane i funkcje zdefiniowane przez użytkownika (UDF) pozwalają na hermetyzację złożonej logiki i wykonywanie jej bezpośrednio w bazie danych Neo4j. Może to poprawić wydajność poprzez zmniejszenie narzutu sieciowego i umożliwienie Neo4j optymalizacji wykonania kodu.
Przykład (tworzenie UDF w Javie):
@Procedure(name = "custom.distance", mode = Mode.READ)
@Description("Calculates the distance between two points on Earth.")
public Double distance(@Name("lat1") Double lat1, @Name("lon1") Double lon1,
@Name("lat2") Double lat2, @Name("lon2") Double lon2) {
// Implementation of the distance calculation
return calculateDistance(lat1, lon1, lat2, lon2);
}
Następnie możesz wywołać UDF z poziomu Cyphera:
RETURN custom.distance(34.0522, -118.2437, 40.7128, -74.0060) AS distance
3. Wykorzystanie algorytmów grafowych
Neo4j zapewnia wbudowane wsparcie dla różnych algorytmów grafowych, takich jak PageRank, najkrótsza ścieżka i wykrywanie społeczności. Algorytmy te mogą być używane do analizy relacji i wydobywania wniosków z danych grafowych.
Przykład: Obliczanie PageRank
CALL algo.pageRank.stream('Person', 'FRIENDS_WITH', {iterations:20, dampingFactor:0.85})
YIELD nodeId, score
RETURN nodeId, score
ORDER BY score DESC
LIMIT 10
4. Monitorowanie i strojenie wydajności
Ciągle monitoruj wydajność swojej bazy danych Neo4j i identyfikuj obszary do poprawy. Użyj następujących narzędzi i technik:
- Neo4j Browser: Zapewnia graficzny interfejs do wykonywania zapytań i analizy wydajności.
- Neo4j Bloom: Narzędzie do eksploracji grafów, które pozwala wizualizować i wchodzić w interakcje z danymi grafowymi.
- Monitorowanie Neo4j: Monitoruj kluczowe metryki, takie jak czas wykonania zapytania, użycie procesora, zużycie pamięci i operacje wejścia/wyjścia na dysku.
- Logi Neo4j: Analizuj logi Neo4j w poszukiwaniu błędów i ostrzeżeń.
- Regularnie przeglądaj i optymalizuj zapytania: Identyfikuj wolne zapytania i stosuj techniki optymalizacji opisane w tym przewodniku.
Przykłady z życia wzięte
Przeanalizujmy kilka rzeczywistych przykładów optymalizacji zapytań w Neo4j.
1. Silnik rekomendacji w e-commerce
Platforma e-commerce używa Neo4j do budowy silnika rekomendacji. Graf składa się z węzłów User
, Product
i relacji PURCHASED
. Platforma chce polecać produkty, które są często kupowane razem.
Zapytanie początkowe (wolne):
MATCH (u:User)-[:PURCHASED]->(p1:Product), (u)-[:PURCHASED]->(p2:Product)
WHERE p1 <> p2
RETURN p1.name, p2.name, count(*) AS co_purchases
ORDER BY co_purchases DESC
LIMIT 10
Zapytanie zoptymalizowane (szybkie):
MATCH (o:Order)-[:CONTAINS]->(p:Product)
WITH o, collect(p) AS products
WHERE size(products) > 1
UNWIND products AS product1
UNWIND products AS product2
WHERE id(product1) < id(product2)
WITH product1, product2, count(*) AS co_purchases
ORDER BY co_purchases DESC
LIMIT 10
RETURN product1.name, product2.name, co_purchases
W zapytaniu zoptymalizowanym używamy klauzuli WITH
, aby zebrać produkty w każdym zamówieniu, a następnie znaleźć wspólne zakupy między różnymi produktami. Jest to znacznie bardziej wydajne niż początkowe zapytanie, które tworzy iloczyn kartezjański między wszystkimi zakupionymi produktami.
2. Analiza sieci społecznościowej
Sieć społecznościowa używa Neo4j do analizy połączeń między użytkownikami. Graf składa się z węzłów Person
i relacji FRIENDS_WITH
. Platforma chce znaleźć influencerów w sieci.
Zapytanie początkowe (wolne):
MATCH (p:Person)-[:FRIENDS_WITH]->(f:Person)
RETURN p.name, count(f) AS friends_count
ORDER BY friends_count DESC
LIMIT 10
Zapytanie zoptymalizowane (szybkie):
MATCH (p:Person)
RETURN p.name, size((p)-[:FRIENDS_WITH]->()) AS friends_count
ORDER BY friends_count DESC
LIMIT 10
W zapytaniu zoptymalizowanym używamy funkcji size()
do bezpośredniego zliczania liczby znajomych. Jest to bardziej wydajne niż początkowe zapytanie, które wymaga przejścia przez wszystkie relacje FRIENDS_WITH
.
Dodatkowo, utworzenie indeksu na etykiecie Person
przyspieszy początkowe wyszukiwanie węzłów:
CREATE INDEX PersonLabel FOR (p:Person) ON (p)
3. Wyszukiwanie w grafie wiedzy
Graf wiedzy używa Neo4j do przechowywania informacji o różnych encjach i ich relacjach. Platforma chce zapewnić interfejs wyszukiwania do znajdowania powiązanych encji.
Zapytanie początkowe (wolne):
MATCH (e1)-[:RELATED_TO*]->(e2)
WHERE e1.name = 'Neo4j'
RETURN e2.name
Zapytanie zoptymalizowane (szybkie):
MATCH (e1 {name: 'Neo4j'})-[:RELATED_TO*1..3]->(e2)
RETURN e2.name
W zapytaniu zoptymalizowanym określamy głębokość przechodzenia przez relacje (*1..3
), co ogranicza liczbę relacji, które muszą zostać przeanalizowane. Jest to bardziej wydajne niż początkowe zapytanie, które przechodzi przez wszystkie możliwe relacje.
Ponadto, użycie indeksu pełnotekstowego na właściwości `name` może przyspieszyć początkowe wyszukiwanie węzłów:
CALL db.index.fulltext.createNodeIndex("EntityNameIndex", ["Entity"], ["name"])
Wnioski
Optymalizacja zapytań w Neo4j jest niezbędna do budowania wysoko wydajnych aplikacji grafowych. Poprzez zrozumienie wykonywania zapytań Cypher, wykorzystanie strategii indeksowania, stosowanie narzędzi do profilowania wydajności i stosowanie różnych technik optymalizacyjnych, można znacznie poprawić szybkość i efektywność zapytań. Pamiętaj, aby stale monitorować wydajność swojej bazy danych i dostosowywać strategie optymalizacji w miarę ewolucji danych i obciążeń zapytań. Ten przewodnik stanowi solidną podstawę do opanowania optymalizacji zapytań w Neo4j i budowania skalowalnych i wydajnych aplikacji grafowych.
Wdrażając te techniki, możesz zapewnić, że Twoja grafowa baza danych Neo4j będzie działać z optymalną wydajnością i stanowić cenne zasoby dla Twojej organizacji.