Wróć do katalogu
Season 17 12 Odcinki 45 min 2026

Apache Cassandra with Python

Edycja 2026. Techniczna seria podcastów zgłębiająca rozproszoną architekturę Apache Cassandra oraz sposoby interakcji z nią za pomocą DataStax Python Driver. Obejmuje modelowanie danych, execution profiles, LWTs, zapytania asynchroniczne i cqlengine Object Mapper.

Bazy danych Obliczenia rozproszone
Apache Cassandra with Python
Teraz odtwarzane
Click play to start
0:00
0:00
1
Szersza perspektywa
Wprowadzenie do Apache Cassandra. Dowiedz się, dlaczego aplikacje o globalnej skali wybierają tę rozproszoną bazę danych NoSQL i czym różni się ona od tradycyjnych systemów relacyjnych.
4m 21s
2
Consistent Hashing i The Ring
Zanurz się w architekturę Cassandra. Omawiamy consistent hashing, token ring oraz sposób, w jaki dane są partycjonowane między wieloma węzłami bez użycia master server.
3m 24s
3
Query Driven Data Modeling
Zapomnij o wszystkim, co wiesz o relacyjnych bazach danych. Dowiedz się, dlaczego query-driven data modeling w Cassandrze wymaga denormalizacji oraz jaka jest kluczowa różnica między partition keys a clustering keys.
2m 58s
4
Łączenie się za pomocą Pythona
Rozpocznij pracę z DataStax Python Driver. Dowiedz się, jak utworzyć instancję Cluster, połączyć się z Session i nawiązać komunikację z węzłami Cassandra.
3m 42s
5
Execution Profiles
Zarządzaj płynnie złożonymi obciążeniami, korzystając z Execution Profiles. Dowiedz się, jak skonfigurować load balancing, timeouts i consistency levels dla każdego zapytania, bez zaśmiecania konfiguracji klastra.
4m 01s
6
Prepared Statements
Dowiedz się, jak wykonywać polecenia CQL z poziomu Pythona. Omawiamy simple statements oraz kluczowe korzyści wydajnościowe płynące z używania Prepared Statements dla często powtarzających się zapytań.
3m 22s
7
Stronicowanie dużych zapytań
Nigdy więcej nie zawieszaj aplikacji przez ładowanie ogromnego zbioru danych do pamięci. Odkryj, jak sterownik Pythona automatycznie stronicuje wyniki dużych zapytań i jak zarządzać fetch sizes.
3m 37s
8
Wysokoprzepustowe zapytania asynchroniczne
Zmaksymalizuj przepustowość swojej aplikacji. Dowiedz się, jak używać execute_async, ResponseFutures oraz callbacks, aby uruchamiać współbieżne żądania do Cassandry.
4m 11s
9
Lightweight Transactions
Bezpiecznie wdrażaj operacje compare-and-set. Dowiedz się, jak działają Lightweight Transactions (LWTs) w Cassandrze i jak sprawdzać specjalistyczną kolumnę applied w wynikach w Pythonie.
3m 35s
10
Modele Object Mapper
Unikaj surowych ciągów znaków CQL i modeluj swoje dane za pomocą klas Pythona. Dowiedz się, jak używać cqlengine do definiowania tabel, określania primary keys i synchronizowania schematu.
4m 00s
11
Tworzenie zapytań za pomocą cqlengine
Płynnie pobieraj i filtruj dane za pomocą obiektów QuerySet w cqlengine Object Mapper. Omawiamy operatory filtrowania, immutability oraz ograniczenia dotyczące sortowania.
4m 04s
12
Vector Search dla AI
Zabezpiecz swoje umiejętności na przyszłość dzięki Vector Search w Cassandrze 5.0. Odkryj, jak przechowywać i odpytywać wielowymiarowe wektory, aby napędzać nowoczesne aplikacje sztucznej inteligencji i uczenia maszynowego.
3m 58s

Odcinki

1

Szersza perspektywa

4m 21s

Wprowadzenie do Apache Cassandra. Dowiedz się, dlaczego aplikacje o globalnej skali wybierają tę rozproszoną bazę danych NoSQL i czym różni się ona od tradycyjnych systemów relacyjnych.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Apache Cassandra z Pythonem, odcinek 1 z 12. Relacyjne bazy danych odbijają się od ściany, gdy próbujesz zrobić ich deploy na wielu kontynentach. W efekcie walczysz z latency, downtime'em lub kruchą architekturą, w której awaria pojedynczego serwera kładzie twoje operacje zapisu. Ogromna, globalna skala wymaga zupełnie innego paradygmatu bazy danych. Tym paradygmatem jest Apache Cassandra. To rozproszona baza danych NoSQL typu open-source, zbudowana z myślą o ogromnej skali. Kiedy inżynierowie po raz pierwszy patrzą na Cassandrę, często przynoszą ze sobą bagaż przyzwyczajeń z systemów relacyjnych. Szukają primary node'a, który obsługuje zapisy, oraz replik read-only, które za nim podążają. Musisz natychmiast porzucić ten model mentalny. Cassandra nie używa architektury primary-replica. Działa w całości w oparciu o architekturę masterless i multi-primary. Każdy pojedynczy node w klastrze jest identyczny. Dowolny node może przyjąć read request i dowolny node może przyjąć write request. Aby zrozumieć, dlaczego to tak działa, spójrz na jej historię. Cassandra została pierwotnie stworzona z połączenia dwóch wielkich przełomów badawczych. Po pierwsze, zaczerpnęła w pełni rozproszony, masterlessowy design sieci z Amazon Dynamo. To on dyktuje, jak node'y się komunikują, odkrywają nawzajem i replikują dane w sieci. Po drugie, zaadoptowała log-structured storage engine z Google Bigtable, który obsługuje to, jak dane są fizycznie zapisywane na dysku. W efekcie powstał system zaprojektowany specjalnie z myślą o ciągłej dostępności w wielu datacenter. Wyobraź sobie globalną sieć społecznościową. Masz aktywnych użytkowników w Tokio, Londynie i Nowym Jorku jednocześnie. Jeśli użytkownik w Londynie zaktualizuje swój profil, ta operacja zapisu musi wykonać się natychmiast. Routowanie tego requestu przez Atlantyk do pojedynczej, centralnej bazy danych jest zbyt wolne. Z Cassandrą, użytkownik pisze do lokalnego node'a w londyńskim datacenter. Ten node akceptuje zapis lokalnie i natychmiast bierze na siebie odpowiedzialność za zreplikowanie go do Tokio i Nowego Jorku w tle. I tu jest kluczowa sprawa. Ponieważ każdy node działa jako primary, nie ma tu single point of failure. Cassandra układa swoje node'y w logiczny pierścień. Kiedy dane wchodzą do systemu, matematyczny hash dokładnie określa, które node'y w pierścieniu są właścicielami tego konkretnego fragmentu danych. Jeśli poważna awaria odetnie całe londyńskie datacenter, sieć społecznościowa nie pada. Tokio i Nowy Jork nadal przyjmują odczyty i zapisy bez żadnych przerw. Kiedy Londyn wraca do życia, pozostałe datacenter automatycznie synchronizują brakujące dane do przywróconych node'ów. Osiągasz prawdziwą globalną dostępność z zerowym downtime'em. Ten masterlessowy design oznacza również, że skalowanie jest przewidywalne i liniowe. Jeśli potrzebujesz więcej storage'u lub większej przepustowości zapisu, po prostu wpinasz kolejny node do pierścienia. Klaster automatycznie wykrywa nowy sprzęt i redystrybuuje dane, aby zbalansować load na aktywnych maszynach. Cassandra zmusza cię do zamiany wygody tradycyjnych zapytań bazodanowych na coś znacznie trudniejszego do zbudowania w dużej skali: absolutną gwarancję, że niezależnie od tego, jaki sprzęt zawiedzie, twoja baza danych pozostanie online, a twoje operacje się powiodą. Jeśli chcesz wesprzeć podcast, możesz wyszukać DevStoriesEU w serwisie Patreon. To wszystko w tym odcinku. Dzięki za wysłuchanie i buduj dalej!
2

Consistent Hashing i The Ring

3m 24s

Zanurz się w architekturę Cassandra. Omawiamy consistent hashing, token ring oraz sposób, w jaki dane są partycjonowane między wieloma węzłami bez użycia master server.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Apache Cassandra z Pythonem, odcinek 2 z 12. Naiwny hashing danych całkowicie zawodzi w momencie, gdy dodajesz nowy serwer do klastra bazy danych. Gdy liczba serwerów się zmienia, prawie każdy fragment danych musi zostać przeniesiony w nowe miejsce. Rozwiązaniem tego chaosu przy skalowaniu jest consistent hashing i The Ring. Standardowym sposobem dystrybucji danych na wielu serwerach jest hashing modulo. Jeśli masz osiem node'ów, bierzesz partition key, taki jak user ID, hashujesz go i dzielisz wynik przez osiem. Reszta z dzielenia mówi ci, który node dostanie profil użytkownika. Działa to idealnie, dopóki twój storage się nie zapełni i nie podłączysz dziewiątego node'a. Teraz dzielisz przez dziewięć. Reszty się zmieniają. Prawie każdy profil użytkownika w twoim systemie nagle ląduje na innym serwerze, wywołując ogromną burzę przenoszenia danych, która potrafi położyć cały klaster. Cassandra całkowicie tego unika. Używa consistent hashingu, aby przewidywalnie dystrybuować dane w klastrze, bez polegania na żadnym centralnym koordynatorze. Zamiast prostego obliczania modulo, Cassandra mapuje zarówno dane, jak i node'y do stałej, ciągłej przestrzeni w kształcie okręgu, nazywanej token ringiem. Domyślnie Cassandra używa funkcji hashującej, która generuje ogromny zakres możliwych liczb. Najniższa możliwa wartość hasha łączy się bezpośrednio z najwyższą, tworząc zamknięty krąg. Każdemu fizycznemu node'owi w klastrze przypisany jest konkretny numer, czyli token, gdzieś na tym ringu. Kiedy dodajesz profil użytkownika, Cassandra hashuje user ID, żeby wygenerować token. Aby dowiedzieć się, który node jest właścicielem tego profilu, system lokalizuje token danych na ringu i porusza się zgodnie z ruchem wskazówek zegara. Pierwszy napotkany node zostaje właścicielem. Wróćmy myślami do naszego klastra z ośmioma node'ami. Jeśli dodamy dziewiąty fizyczny node, dostaje on pojedynczy nowy token na ringu, lądując między dwoma istniejącymi node'ami. Ponieważ własność danych jest ustalana idąc zgodnie z ruchem wskazówek zegara, ten nowy, dziewiąty node przejmuje tylko konkretny wycinek danych od swojego bezpośredniego sąsiada zgodnie z ruchem wskazówek zegara. Pozostałe siedem node'ów nie robi nic. Ich dane pozostają całkowicie nietknięte. I tu jest kluczowa sprawa. Przypisanie dokładnie jednego tokena do jednego fizycznego node'a stwarza problemy operacyjne. Trudno jest idealnie zbalansować dane, a kiedy dodajesz nowy node, tylko jeden sąsiadujący serwer jest odpowiedzialny za przekazanie danych. Ten pojedynczy sąsiad dostaje potężnie w kość pod dużym obciążeniem. Żeby to naprawić, Cassandra używa virtual nodes, czyli vnode'ów. Zamiast dawać fizycznemu serwerowi jeden ogromny, ciągły kawałek ringu, vnode'y tną ring na wiele mniejszych zakresów. Pojedynczemu fizycznemu node'owi przypisuje się setki różnych tokenów rozrzuconych losowo po ringu. Kiedy dodajesz ten dziewiąty fizyczny node używając vnode'ów, przejmuje on wiele małych kawałków ringu od wszystkich istniejących serwerów naraz. Teraz, zamiast jednego sąsiada odwalającego całą czarną robotę, cały klaster równomiernie dzieli się pracą przy streamowaniu danych do nowej maszyny. Consistent hashing i virtual nodes oddzielają rozmieszczenie danych od surowej liczby fizycznych serwerów, pozwalając klastrowi płynnie się skalować i działać przewidywalnie bez żadnego centralnego mastera, który dyktowałby, gdzie mają trafiać dane. To tyle w tym odcinku. Do usłyszenia następnym razem!
3

Query Driven Data Modeling

2m 58s

Zapomnij o wszystkim, co wiesz o relacyjnych bazach danych. Dowiedz się, dlaczego query-driven data modeling w Cassandrze wymaga denormalizacji oraz jaka jest kluczowa różnica między partition keys a clustering keys.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Apache Cassandra z Pythonem, odcinek 3 z 12. W relacyjnej bazie danych najpierw budujesz tabele, definiujesz relacje, a potem piszesz swoje query. W Cassandrze, jeśli nie znasz swoich dokładnych query przed startem, twoja baza danych polegnie. To jest główna zasada Query Driven Data Modeling. Wielu developerów przychodzi do Cassandry z silnymi nawykami relacyjnymi. Naturalnie szukasz sposobów na normalizację danych, ustawienie foreign keys i unikanie duplikacji. Musisz całkowicie porzucić to podejście. Cassandra nie obsługuje joinów. Jeśli spróbujesz znormalizować dane w wielu tabelach, skończysz na robieniu joinów w kodzie aplikacji, co niszczy korzyści wydajnościowe, dla których w ogóle wybrałeś Cassandrę. Cassandra wymaga Query Driven Data Modeling. Zaczynasz od zmapowania dokładnych pytań, które twoja aplikacja musi zadać bazie danych. W tym modelu jedno query to zazwyczaj jedna tabela. Jeśli potrzebujesz dostępu do tych samych danych na trzy różne sposoby, tworzysz trzy różne tabele trzymające te same dane. To prowadzi nas do denormalizacji. Duplikowanie danych nie jest tu błędem; to fundamentalna strategia. Miejsce na dysku jest tanie, ale rozproszone odczyty przez sieć są drogie. Zapisując dane razem, dokładnie w kształcie twojego read query, Cassandra może je pobrać w jednej operacji bez przeszukiwania całego klastra. Żeby to zadziałało, musisz zrozumieć primary key. To nie tylko unikalny identyfikator. Kontroluje on dokładnie, gdzie i jak twoje dane są przechowywane na dysku. Primary key ma dwie odrębne części: partition key i clustering key. Partition key dyktuje, który fizyczny node w twoim klastrze trzyma dane. Wszystkie wiersze dzielące ten sam partition key są przechowywane razem na tym samym nodzie. Clustering key określa kolejność sortowania tych wierszy na dysku wewnątrz tej konkretnej partycji. Weźmy scenariusz publikacji magazynów z dokumentacji. Załóżmy, że twoja aplikacja musi pobrać wszystkie magazyny wydane przez konkretnego wydawcę. Twoim partition key musi być nazwa wydawcy. Kiedy przychodzi query, Cassandra hashuje nazwę wydawcy, identyfikuje dokładny node trzymający dane tego wydawcy i idzie prosto do niego. Żeby naturalnie uporządkować wyniki, możesz użyć daty publikacji jako swojego clustering key. Teraz nie tylko wszystkie magazyny tego wydawcy są zgrupowane na jednym nodzie, ale są też fizycznie przechowywane w kolejności chronologicznej. Baza danych po prostu streamuje z powrotem wstępnie posortowane dane. Oto kluczowy wniosek. Wymieniasz złożoność zapisu na ogromną prędkość odczytu. Zapisujesz te same dane do wielu tabel, żeby zaspokoić różne widoki aplikacji, ale kiedy użytkownik prosi o te dane, wracają one w milisekundach, ponieważ baza danych nie wykonuje żadnych obliczeń, żeby je złożyć. Dzięki za wysłuchanie. Trzymajcie się wszyscy.
4

Łączenie się za pomocą Pythona

3m 42s

Rozpocznij pracę z DataStax Python Driver. Dowiedz się, jak utworzyć instancję Cluster, połączyć się z Session i nawiązać komunikację z węzłami Cassandra.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Apache Cassandra z Pythonem, odcinek 4 z 12. Kiedy łączysz się z tradycyjną bazą danych, kierujesz swoją aplikację na pojedynczy adres hosta. Kiedy łączysz się z rozproszoną bazą danych, możesz zakładać, że musisz ręcznie śledzić dziesiątki adresów serwerów w plikach konfiguracyjnych. Wcale nie musisz, ponieważ twój driver bazy danych działa tak naprawdę jak inteligentny router. Dzisiaj przyjrzymy się łączeniu z poziomu Pythona. Wyobraź sobie, że uruchamia się twój mikroserwis w Pythonie. Musi nawiązać komunikację z lokalnym, trzywęzłowym klastrem Cassandry. Na początek importujesz klasę Cluster z modułu cassandra dot cluster. Inicjalizujesz tę klasę, przekazując jej listę adresów IP, znanych jako contact points. Jeśli twoje węzły używają niestandardowego portu, możesz też podać tutaj argument port; w przeciwnym razie domyślna wartość to 9042. Deweloperzy często mylą ten krok z budowaniem standardowego connection stringa dla pojedynczego DSN, gdzie musisz jawnie wylistować dokładnie to, z czym chcesz gadać. W przypadku Cassandry nie musisz wymieniać każdego pojedynczego węzła w swojej infrastrukturze. Jeśli masz ogromny, trzydziestowęzłowy klaster, przekazanie zaledwie dwóch lub trzech adresów IP jako contact points jest w zupełności wystarczające. I tu jest kluczowa sprawa. Kiedy driver Pythona startuje, uderza do jednego z tych contact points, żeby wykonać bootstrap. Odpytuje tabele systemowe, aby pobrać aktualną topologię klastra. Robiąc to, automatycznie odkrywa adresy IP reszty węzłów. Driver dynamicznie utrzymuje tę mapę sieci w tle. Jeśli później zrobisz scale out i dodasz węzły, driver wykryje zmianę i automatycznie dostosuje routing, bez konieczności restartu aplikacji. Kiedy już utworzysz instancję obiektu Cluster z tymi początkowymi contact points, wywołujesz jego metodę connect. Ta akcja zwraca obiekt Session. Obiekt Session obsługuje właściwy connection pooling do węzłów, które właśnie odkrył. Wywołując connect, możesz opcjonalnie przekazać nazwę keyspace. Keyspace działa jak namespace dla twoich danych. Jeśli go podasz, driver ustawi go jako domyślny dla wszystkich przyszłych operacji na tym obiekcie Session. Ponieważ Session zarządza pod spodem złożonymi connection pools, jest zaprojektowany tak, aby był long-lived i thread-safe. Zazwyczaj tworzysz jeden obiekt Session przy starcie aplikacji i używasz go wielokrotnie. Teraz druga kwestia, czyli łączenie się z zarządzaną usługą w chmurze, taką jak DataStax Astra. Astra działa inaczej i nie wystawia surowych adresów IP dla contact points. Zamiast tego pobierasz secure connect bundle. Jest to plik zip zawierający wymagane certyfikaty i szczegóły połączenia mutual TLS. W swoim kodzie w Pythonie pomijasz listę adresów IP. Zamiast tego, przekazujesz słownik z konfiguracją chmurową do obiektu Cluster. Ten słownik zawiera klucz o nazwie secure connect bundle, który wskazuje na lokalną ścieżkę do twojego pliku zip. Łączysz to z obiektem plaintext authentication provider, skonfigurowanym z twoim client ID i secret. Wywołanie connect zwraca wtedy standardowy obiekt Session, działający dokładnie tak samo, jak w konfiguracji z lokalnym klastrem. Kluczowy wniosek jest taki, że niezależnie od tego, czy przekażesz kilka lokalnych adresów IP, czy cloud secure bundle, driver Pythona bierze twój początkowy entry point, mapuje rozproszoną sieć bazy danych i całkowicie abstrahuje logikę routingu od kodu twojej aplikacji. To wszystko w tym odcinku. Dzięki za wysłuchanie i twórz dalej!
5

Execution Profiles

4m 01s

Zarządzaj płynnie złożonymi obciążeniami, korzystając z Execution Profiles. Dowiedz się, jak skonfigurować load balancing, timeouts i consistency levels dla każdego zapytania, bez zaśmiecania konfiguracji klastra.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Apache Cassandra z Pythonem, odcinek 5 z 12. Wraz z rozwojem twojej aplikacji, stosowanie jednego uniwersalnego timeoutu lub consistency level do wszystkich zapytań do bazy danych staje się ogromnym wąskim gardłem operacyjnym. W efekcie twoje background jobs przedwcześnie kończą się błędem, albo twój UI zawiesza się w oczekiwaniu na ciężki read. Execution Profiles to mechanizm, który rozwiązuje ten problem. Wielu developerów myli konfigurację execution z legacy setupem na poziomie klastra. W starszych paradygmatach definiowałeś globalne parametry bezpośrednio na obiekcie cluster, co oznaczało, że każde zapytanie współdzieliło dokładnie ten sam timeout. Execution profiles zastępują ten sztywny schemat. Pozwalają ci na jednoczesne utrzymanie wielu odrębnych konfiguracji w ramach jednej aktywnej sesji. Weźmy pod uwagę aplikację webową typu multi-tenant. Twój front-end wymaga ścisłego, jednosekundowego timeoutu, aby zapewnić płynność działania stron. Tymczasem twoje backgroundowe taski raportujące potrzebują trzydziestu sekund, aby bezpiecznie zagregować duże partycje. Execution profile to samodzielny, nazwany pakiet ustawień requestów, precyzyjnie dostosowany do tych różnych workloadów. Aby to zbudować, zaczynasz od utworzenia instancji obiektu execution profile. Dla taska raportującego, tworzysz instancję profilu i ustawiasz parametr request timeout na trzydzieści sekund. Możesz pójść o krok dalej i podpiąć pod ten obiekt konkretną load balancing policy, na przykład routując te ciężkie analityczne ready wyłącznie do dedykowanego analitycznego data center. Następnie tworzysz drugi, odrębny obiekt profilu dla swojego UI, przypisując mu jednosekundowy timeout i lokalną load balancing policy. Możesz również dorzucić do tych profili odrębne retry policies lub consistency levels, w zależności od tego, czego wymaga zapytanie. Oto kluczowa sprawa. Rejestrujesz te profile raz, podczas początkowego setupu klastra, zamiast budować je podczas samej egzekucji zapytania. Podczas tworzenia instancji klastra, przekazujesz dictionary do argumentu execution profiles. Ten dictionary mapuje proste nazwy typu string na obiekty profilu, które właśnie stworzyłeś. Driver zarządza tymi konfiguracjami pod spodem. Zawsze istnieje wbudowany profil fallbackowy, który możesz nadpisać, mapując konfigurację na konkretną stałą o nazwie execution profile default. Jeśli wykonasz zapytanie bez jawnego podania nazwy profilu, driver automatycznie zastosuje te domyślne ustawienia. Kiedy faktycznie musisz odpalić zapytanie, używasz standardowych metod session execute lub execute async. Obok twojego query stringa lub prepared statement, przekazujesz argument execution profile, używając prostej nazwy typu string, którą zarejestrowałeś wcześniej. Driver przechwytuje tę nazwę, pobiera powiązane z nią ustawienia i stosuje je do tego konkretnego requestu. Zapytanie z twojego UI jest ściśle ograniczone do jednej sekundy, a background job działa komfortowo przez trzydzieści sekund. Oba zapytania wykonują się współbieżnie, zmultipleksowane w ramach dokładnie tej samej sesji i dokładnie tej samej connection pool. Przeniesienie twojej konfiguracji do execution profiles oddziela zachowanie twojego zapytania od fizycznego połączenia z bazą danych. Pozwala to pojedynczej aplikacji dynamicznie kształtować jej ruch do bazy danych w oparciu o specyficzne potrzeby każdej funkcji, bez konieczności nawiązywania oddzielnych sesji. Dzięki za spotkanie. Mam nadzieję, że dowiedziałeś się czegoś nowego.
6

Prepared Statements

3m 22s

Dowiedz się, jak wykonywać polecenia CQL z poziomu Pythona. Omawiamy simple statements oraz kluczowe korzyści wydajnościowe płynące z używania Prepared Statements dla często powtarzających się zapytań.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Apache Cassandra z Pythonem, odcinek 6 z 12. Jeśli używasz prostego formatowania stringów dla zapytań o wysokiej przepustowości, zmuszasz bazę danych do marnowania cennych cykli CPU na ponowne parsowanie tej samej struktury tysiące razy na sekundę. Aby zatrzymać to marnotrawstwo i znacznie poprawić wydajność aplikacji, użyj Prepared Statements. Kiedy łączysz się z Cassandrą, najbardziej bezpośrednim sposobem interakcji z bazą danych jest przekazanie stringa do metody session dot execute. Przekazujesz jej zapytanie, a ona zwraca result set. Domyślnie każdy wiersz w tym wyniku wraca jako pythonowy namedtuple. Oznacza to, że możesz uzyskać dostęp do wartości kolumn używając prostej dot notation, jak row dot name albo row dot age. To czyste rozwiązanie i dobrze sprawdza się przy jednorazowych operacjach. Ale wysyłanie surowego stringa jest bardzo nieefektywne dla zapytań, które uruchamiasz bez przerwy. Możesz myśleć, że już robisz to dobrze, jeśli używasz parametrów pozycyjnych. Jeśli przekażesz query string zawierający znaczniki percent-s razem z sekwencją wartości, pythonowy driver bezpiecznie sformatuje to zapytanie. Zapobiega to atakom typu injection, ale architektonicznie nic to nie zmienia w kwestii wydajności. Nadal za każdym razem wysyłasz cały query string przez sieć do Cassandry. Cassandra nadal musi odebrać tekst, sparsować składnię i obliczyć query plan od zera. Oto kluczowa sprawa. Nie musisz parsować tej samej struktury dwa razy. I tu wkracza metoda session dot prepare. Zamiast od razu wykonywać zapytanie, przekazujesz swój query string do metody prepare. W tym stringu zastępujesz dynamiczne wartości znakami zapytania. Driver wysyła ten szablon do Cassandry. Cassandra parsuje go, waliduje, oblicza najwydajniejszy execution plan, a następnie generuje unikalne ID dla tego konkretnego statementu. Odsyła to ID z powrotem do twojej aplikacji w Pythonie. Od tego momentu, za każdym razem, gdy musisz uruchomić to zapytanie, twoja aplikacja nie wysyła stringa. Binduje twoje konkretne zmienne do obiektu Prepared Statement i wysyła tylko unikalne ID razem z surowymi bajtami wartości. Pomyśl o usłudze uwierzytelniania o dużym ruchu. Musisz wyszukać użytkownika po jego ID podczas logowania. Zapytanie to select star from users where user id equals question mark. Jeśli twoja usługa obsługuje dziesięć tysięcy logowań na sekundę, wysyłanie pełnego query stringa dziesięć tysięcy razy marnuje przepustowość sieci i CPU bazy danych. Przygotowując statement raz, podczas startu aplikacji, znacznie zmniejszasz network payload. Cassandra widzi ID, natychmiast wyciąga wstępnie obliczony execution plan i od razu pobiera dane. Prepared Statements przenoszą ciężar parsowania zapytań z powtarzającego się podatku per request na jednorazowy koszt setupu. To tyle w tym odcinku. Do usłyszenia następnym razem!
7

Stronicowanie dużych zapytań

3m 37s

Nigdy więcej nie zawieszaj aplikacji przez ładowanie ogromnego zbioru danych do pamięci. Odkryj, jak sterownik Pythona automatycznie stronicuje wyniki dużych zapytań i jak zarządzać fetch sizes.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Apache Cassandra z Pythonem, odcinek 7 z 12. Odpalasz proste zapytanie select na ogromnej tabeli i nagle twoja aplikacja crashuje z błędem out-of-memory. A przynajmniej tak by było, gdyby twój driver bazy danych próbował załadować wszystko naraz. Zamiast tego, driver płynnie obsługuje to w tle. Dzisiaj porozmawiamy o pagingu dużych zapytań. Kiedy wykonujesz zapytanie używając drivera Cassandra dla Pythona, nie próbuje on zaciągnąć milionów wierszy do pamięci twojej aplikacji. Domyślnie driver automatycznie robi paging wyników, pobierając dokładnie 5000 wierszy na raz. Zwrócony result set działa jak standardowy iterator w Pythonie. Kiedy iterujesz po wierszach w pętli, driver w sposób transparentny uderza do bazy danych, żeby pobrać kolejny batch tuż przed tym, jak skończą ci się dane. Twój kod wygląda dokładnie jak standardowa pętla, ale pod spodem driver gwarantuje bezpieczeństwo pamięci, trzymając w niej tylko jedną stronę danych w danym momencie. Nie jesteś skazany na domyślne 5000 wierszy. Możesz to kontrolować, ustawiając właściwość fetch size na obiekcie statement, zanim przekażesz go do metody execute. Jeśli zmniejszysz fetch size do 100, driver będzie trzymał mniej danych w pamięci, ale będzie robił częstsze round tripy sieciowe do bazy danych. Nie myl tego mechanizmu z tradycyjną paginacją w SQL-u, używającą poleceń limit i offset. Paginacja typu offset w SQL-u wymusza na bazie danych skanowanie i odrzucanie wierszy przed zwróceniem twoich danych, co drastycznie zwalnia, im głębiej robisz paging. Cassandra używa podejścia opartego na kursorach. Driver używa wewnętrznego markera, żeby śledzić dokładną fizyczną lokalizację w bazie danych, w której zakończył się ostatni odczyt. Automatyczny paging jest idealny do przetwarzania danych w tle, ale nie sprawdza się przy budowaniu bezstanowych aplikacji webowych. Wyobraź sobie endpoint webowy, który dostarcza do interfejsu użytkownika stale przewijaną listę tysięcy logów audytowych. Nie możesz trzymać otwartego połączenia z bazą danych i aktywnego iteratora na serwerze, czekając aż użytkownik przescrolluje stronę. Potrzebujesz sposobu, żeby zatrzymać zapytanie, zamknąć request i wznowić go później. I tu pojawia się kluczowa sprawa. Driver udostępnia na obiekcie result set właściwość o nazwie paging state. Jest to nieprzezroczysty byte string, który reprezentuje dokładną pozycję kursora dla twojego zapytania. Żeby zbudować bezstanowe API, wykonujesz zapytanie, pobierasz pojedynczą stronę logów audytowych i wyciągasz ten paging state. Konwertujesz bajty na zwykły hex string i wysyłasz go na frontend razem z danymi. Kiedy użytkownik przescrolluje interfejs na sam dół, frontend odsyła ten sam hex string z powrotem na twój serwer. Twój backend dekoduje ten string i przekazuje go do metody execute jako parametr paging state. Cassandra natychmiast wznawia zapytanie dokładnie w miejscu, w którym skończyła. Użycie paging state pozwala ci odciąć czas życia pamięci twojej aplikacji od ogromnej skali tabel w bazie danych. Dzięki temu możesz streamować nieskończone ilości danych do klientów, używając ściśle ograniczonej ilości pamięci RAM na serwerze. To wszystko w tym odcinku. Dzięki za wysłuchanie i koduj dalej!
8

Wysokoprzepustowe zapytania asynchroniczne

4m 11s

Zmaksymalizuj przepustowość swojej aplikacji. Dowiedz się, jak używać execute_async, ResponseFutures oraz callbacks, aby uruchamiać współbieżne żądania do Cassandry.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Apache Cassandra z Pythonem, odcinek 8 z 12. Oczekiwanie na odpowiedź bazy danych przed wysłaniem kolejnego requestu to najprostszy sposób na zdławienie własnej aplikacji. Jeśli twój skrypt w Pythonie czeka bezczynnie podczas komunikacji sieciowej, tracisz ogromną wydajność zapisu. Aby to naprawić, potrzebujesz asynchronicznych zapytań o wysokiej przepustowości. Kiedy używasz standardowej metody execute na sesji Cassandry, twój kod się blokuje. Wysyła zapytanie, czeka aż baza danych je przetworzy, i czeka aż sieć przyniesie odpowiedź. Dla pipeline'u przetwarzającego dane, ten czas bezczynności jest zabójczy dla twojej całkowitej przepustowości. Aby odblokować prawdziwą prędkość, driver DataStax dla Pythona udostępnia metodę execute async. Najpierw musimy wyjaśnić pewne częste nieporozumienie. Słysząc słowo async w Pythonie, prawdopodobnie myślisz o module asyncio ze standardowej biblioteki i słowach kluczowych async await. To nie jest to. Driver Cassandry opiera się na własnym, lekkim event loopie działającym w wątku w tle. Wywołując execute async, korzystasz z customowego mechanizmu drivera, a nie z wbudowanych asynchronicznych funkcji Pythona. Kiedy przekazujesz zapytanie do execute async, nie czeka ono na odpowiedź z bazy danych. Natychmiast zwraca kontrolę do twojego programu. To, co ci zwraca, to obiekt o nazwie ResponseFuture. Ten obiekt to promise. Reprezentuje operację na bazie danych, która została wysłana do klastra, ale jeszcze się nie zakończyła. Ponieważ twój main thread już nie czeka, potrzebujesz sposobu, aby dowiedzieć się, kiedy zapytanie faktycznie się zakończy lub czy zakończy się błędem. Zarządzasz tym, podpinając callbacki bezpośrednio do obiektu ResponseFuture. Najpierw definiujesz funkcję success, która przyjmuje result set jako argument. Następnie definiujesz funkcję error, która przyjmuje wyjątek jako argument. Na koniec łączysz obie te funkcje z future'em za pomocą metody add callbacks. Kiedy baza danych odpowie, wątek w tle odbiera pakiet sieciowy i automatycznie odpala odpowiednią funkcję. I tu jest kluczowa sprawa. Ten mechanizm pozwala ci na pipelinowanie setek operacji jednocześnie. Wyobraź sobie pipeline IoT odbierający setki odczytów temperatury na sekundę. Używając metody synchronicznej, przetwarzasz jeden odczyt, czekasz na bazę danych, a następnie przetwarzasz kolejny. Używając execute async, loopujesz przez przychodzące odczyty bez zatrzymywania się. Dla każdego odczytu odpalasz zapytanie insert, łapiesz ResponseFuture, podpinasz swoje callbacki success i error, i natychmiast przechodzisz do następnego odczytu. Wypychasz setki requestów do sieci w ułamku sekundy. Driver multipleksuje te zapytania przez twoje istniejące połączenia z bazą danych. Klaster Cassandry obsługuje je równolegle i streamuje wyniki z powrotem. Twoje callbacki success i error odpalają się w miarę napływania odpowiedzi, całkowicie niezależnie od kolejności, w jakiej je wysłałeś. To podejście drastycznie zwiększa twoją przepustowość, ponieważ nakłada na siebie czas oczekiwania na sieć dla wszystkich tych zapytań. Ogranicza cię tylko przepustowość sieci i wydajność bazy danych, a nie blokowanie wątków. Musisz śledzić liczbę requestów in flight, aby nie przeciążyć kolejki drivera, ale podstawową zasadą jest utrzymanie pełnego pipeline'u sieciowego. Najważniejszym wnioskiem jest to, że przepustowość bazy danych polega na minimalizacji czasu bezczynności, a intensywne wykorzystanie execute async z podpiętymi callbackami zmusza twoją aplikację do spędzania czasu na wypychaniu danych, zamiast na czekaniu na sieć. Jeśli uznałeś to za przydatne i chcesz wesprzeć podcast, możesz wyszukać DevStoriesEU na Patreonie. To wszystko w tym odcinku. Dzięki za słuchanie i koduj dalej!
9

Lightweight Transactions

3m 35s

Bezpiecznie wdrażaj operacje compare-and-set. Dowiedz się, jak działają Lightweight Transactions (LWTs) w Cassandrze i jak sprawdzać specjalistyczną kolumnę applied w wynikach w Pythonie.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Apache Cassandra z Pythonem, odcinek 9 z 12. W systemie rozproszonym bez globalnych blokad, dwóch użytkowników próbuje zarejestrować dokładnie ten sam username w tej samej milisekundzie. Jak zagwarantować, że tylko jeden z nich faktycznie go dostanie? Mechanizm, który to rozwiązuje, nazywa się Lightweight Transaction. Zanim wyjaśnię, jak je zaimplementować, musimy doprecyzować samą nazwę. Lightweight Transaction w Cassandrze to nie jest tradycyjna transakcja ACID na wielu tabelach. Nie możesz otworzyć bloku transakcji, zapisać danych do trzech różnych tabel i zrobić rollback wszystkiego, jeśli jeden krok się nie powiedzie. Lightweight Transaction jest ściśle ograniczona do jednej partycji. To operacja warunkowa, w zasadzie rozproszone compare and set. Aby zająć unikalny username bez race conditions, piszesz standardowe zapytanie insert, ale na samym końcu dodajesz klauzulę IF NOT EXISTS. Cassandra sprawdzi klaster, żeby zobaczyć, czy ten partition key już istnieje. Jeśli go nie ma, zapis przechodzi. Dla operacji update używasz klauzuli IF, po której podajesz nazwę kolumny i oczekiwaną wartość. Możesz poinstruować bazę danych, żeby zaktualizowała status konta tylko wtedy, gdy obecny status pasuje do konkretnego stringa. Wykonywanie tych zapytań z poziomu Pythona wymaga innego obsłużenia odpowiedzi niż przy normalnym zapisie. Standardowy zapis w Cassandrze albo przechodzi po cichu, albo rzuca błędem timeout. Ale kiedy dodajesz klauzulę IF, baza danych musi powiedzieć twojej aplikacji, czy warunek został faktycznie spełniony. Pythonowy driver obsługuje to, zwracając specjalny result set. Kiedy wykonujesz Lightweight Transaction, pierwszy wiersz zwróconych danych zawiera specjalną systemową kolumnę typu boolean. Driver udostępnia tę kolumnę pod nazwą applied, otoczoną nawiasami kwadratowymi. Wykonujesz swoje zapytanie insert i pobierasz pierwszy wiersz z wyniku. Następnie sprawdzasz tę kolumnę nawias kwadratowy applied nawias kwadratowy. Jeśli driver zwraca ci wiersze jako słowniki, odwołujesz się do tego klucza jako stringa. Jeśli wartość ewaluuje się do true, warunek został spełniony, a twój nowy użytkownik z powodzeniem zajął ten username. Operacja jest zakończona. Oto kluczowa kwestia. Musisz dokładnie zrozumieć, co się dzieje, kiedy ta kolumna nawias kwadratowy applied nawias kwadratowy ewaluuje się do false. Jeśli insert się nie powiedzie, ponieważ username jest już zajęty, Cassandra nie zwraca po prostu zwykłej flagi odrzucenia. Driver wypełnia wiersz z wynikiem rzeczywistymi danymi, przez które twój warunek nie został spełniony. Ponieważ twój warunek został odrzucony, baza danych odczytuje istniejący wiersz i zwraca go do twojej pythonowej aplikacji w dokładnie tej samej odpowiedzi. Otrzymujesz flagę applied z wartością false, a obok niej dostajesz obecny stan konfliktowego rekordu. Jeśli próbowałeś zrobić update salda na podstawie oczekiwanego poprzedniego salda, natychmiast dostajesz z powrotem rzeczywiste, obecne saldo. Twój kod w Pythonie dokładnie wie, jakie dane zablokowały transakcję, co całkowicie eliminuje potrzebę uruchamiania kolejnego zapytania select, żeby dowiedzieć się, dlaczego zapis został odrzucony. Lightweight Transactions dają ci ścisłą kontrolę współbieżności na poziomie partycji, a pythonowy driver sprawia, że jest to bardzo wydajne, przekazując ci stan blokujący za darmo za każdym razem, gdy warunek nie zostanie spełniony. Dzięki za wysłuchanie. Trzymajcie się wszyscy.
10

Modele Object Mapper

4m 00s

Unikaj surowych ciągów znaków CQL i modeluj swoje dane za pomocą klas Pythona. Dowiedz się, jak używać cqlengine do definiowania tabel, określania primary keys i synchronizowania schematu.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Apache Cassandra z Pythonem, odcinek 10 z 12. Pisanie surowych stringów CQL w kodzie twojej aplikacji w Pythonie może szybko stać się trudnym do utrzymania bałaganem. Kończysz z wielolinijkowymi stringami pełnymi interpolacji zmiennych, przez co łatwo o literówki, a refaktoryzacja schematu staje się bolesna. Potrzebujesz sposobu na reprezentowanie tabel bazy danych jako natywnych obiektów Pythona. I to jest dokładnie to, co zapewniają modele Object Mappera. Driver Datastax dla Pythona zawiera object mapper o nazwie cqlengine. Pozwala ci on definiować tabele Cassandry za pomocą standardowych klas Pythona. Jeśli kiedykolwiek korzystałeś z Django ORM lub SQLAlchemy, składnia wyda ci się bardzo znajoma. Ale jest jedna główna różnica, którą musimy od razu wyjaśnić. W tych relacyjnych mapperach deklarujesz klucze obce, aby połączyć ze sobą tabele. Cassandra nie jest relacyjną bazą danych, więc relacyjne powiązania po prostu tu nie istnieją. Model w cqlengine mapuje się dokładnie na jedną, samodzielną tabelę w Cassandrze. Zbudujmy model Comment dla aplikacji zdjęciowej. Chcemy pogrupować wszystkie komentarze dla konkretnego zdjęcia i chcemy, żeby były uporządkowane chronologicznie. Najpierw tworzysz klasę w Pythonie o nazwie Comment, która dziedziczy po klasie bazowej Model z cqlengine. Wewnątrz tej klasy definiujesz swoje kolumny jako atrybuty klasy. Zaczynamy od atrybutu photo underscore id. Przypisujesz go do typu kolumny UUID i przekazujesz argument primary underscore key równa się True. Ponieważ jest to pierwszy primary key, który deklarujesz w klasie, cqlengine automatycznie przypisuje go jako partition key. Następnie potrzebujemy sposobu na unikalne zidentyfikowanie i posortowanie każdego komentarza wewnątrz tej partycji zdjęcia. Definiujesz drugi atrybut o nazwie comment underscore id. Przypisujesz go do typu kolumny TimeUUID i również przekazujesz primary underscore key równa się True. I tu robi się ciekawie. W cqlengine każdy primary key zdefiniowany po partition key automatycznie staje się clustering key. Nie potrzebujesz specjalnego bloku konfiguracji klastrowania. Dosłowna kolejność z góry na dół, w jakiej definiujesz atrybuty kolumn w swojej klasie w Pythonie, dyktuje strukturę primary key w Cassandrze. Po ustawieniu primary keys, dodajesz właściwe kolumny z danymi. Definiujesz atrybut body i przypisujesz mu typ kolumny Text. To po prostu zwykłe dane, więc żadne argumenty primary key nie są potrzebne. Teraz masz deklaratywną klasę w Pythonie, która opisuje schemat twojej tabeli. Ale ta tabela jeszcze nie istnieje w twojej bazie danych. Aby wypchnąć ten schemat do Cassandry, używasz funkcji o nazwie sync underscore table. Przekazujesz swoją klasę modelu Comment bezpośrednio do tej funkcji. Mapper odczytuje strukturę twojej klasy, tłumaczy ją na poprawny, surowy statement CQL i go wykonuje. Jeśli tabela nie istnieje, sync underscore table ją tworzy. Jeśli tabela już istnieje, sprawdza, czy dodałeś nowe kolumny do swojej klasy w Pythonie i modyfikuje tabelę, aby do niej pasowała. Ważne jest, aby wiedzieć, że sync underscore table nigdy nie usunie danych, ani nie zmieni istniejących primary keys, utrzymując twoją główną strukturę danych bezpieczną podczas aktualizacji. Prawdziwa siła definiowania modeli w ten sposób to nie tylko ukrywanie składni bazy danych, ale osadzenie definicji schematu bezpośrednio w warstwie aplikacji, gdzie żyje logika biznesowa. To wszystko w tym odcinku. Dzięki za wysłuchanie i twórz dalej!
11

Tworzenie zapytań za pomocą cqlengine

4m 04s

Płynnie pobieraj i filtruj dane za pomocą obiektów QuerySet w cqlengine Object Mapper. Omawiamy operatory filtrowania, immutability oraz ograniczenia dotyczące sortowania.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Apache Cassandra z Pythonem, odcinek 11 z 12. Filtrowanie rekordów bazy danych za pomocą object mappera zazwyczaj wydaje się bezproblemowe, pozwalając ci na wyszukiwanie po dowolnym polu. Ale w Cassandrze, jeśli spróbujesz filtrować po kolumnie, która nie jest jawnie indeksowana lub nie jest częścią primary key, baza danych po prostu odmówi wykonania twojego query. Rozwiązaniem jest zrozumienie, jak konstruować poprawne requesty, co prowadzi nas do tematu tworzenia queries za pomocą cqlengine. W cqlengine, kiedy chcesz pobrać dane z modelu, wchodzisz w interakcję z QuerySetem. Jeśli masz model reprezentujący tabelę w bazie danych, możesz uzyskać dostęp do rekordów, wywołując atrybut objects, a następnie metodę all. Zwraca to QuerySet reprezentujący każdy wiersz w tej tabeli. Pobieranie każdego wiersza to rzadko to, czego chcesz w rozproszonej bazie danych, dlatego potrzebujesz sposobu na zawężenie wyników. Aby ograniczyć zwracane dane, używasz metody filter. Często mylące jest to, jak to filtrowanie faktycznie się odbywa. Developerzy przychodzący z innych frameworków czasami zakładają, że object mapper pobiera wszystkie dane do klienta w Pythonie i filtruje listę w pamięci. To całkowita nieprawda. Metoda filter mapuje się bezpośrednio na ścisłą klauzulę WHERE w CQL. Oznacza to, że kolumny, które przekazujesz do metody filter, muszą być zgodne z regułami query w Cassandrze. Możesz filtrować tylko po partition keys, clustering columns lub kolumnach z secondary index. Jeśli spróbujesz filtrować po standardowym, nieindeksowanym polu tekstowym, cqlengine nie ukryje błędu ani nie przetworzy go lokalnie. Przekaże query prosto do Cassandry, a Cassandra je odrzuci. Spójrzmy na konkretny scenariusz. Masz model Automobile i chcesz znaleźć wszystkie samochody marki Tesla wyprodukowane po 2012 roku. Zaczynasz od wywołania objects dot filter. Przekazujesz keyword argument manufacturer ustawiony na string Tesla. Aby obsłużyć warunek roku, cqlengine udostępnia specjalne operatory filtrowania. Stosujesz je, dopisując podwójne podkreślenie i skrót operatora bezpośrednio do nazwy kolumny. Dla warunku ostro większego niż, używasz podwójnego podkreślenia g t. Dodajesz więc drugi keyword argument do wywołania filter: year podwójne podkreślenie g t równa się 2012. Mapper płynnie tłumaczy to na poprawne query w CQL. Dostępnych jest kilka innych operatorów dla różnych warunków. Jeśli chciałbyś sprawdzić konkretne modele samochodów zamiast roku, mógłbyś użyć operatora podwójne podkreślenie in. Przekazałbyś model podwójne podkreślenie in, ustawiony na listę w Pythonie zawierającą nazwy Model S i Model 3. Baza danych zwróci rekordy pasujące do dowolnej wartości z tej listy. Oto kluczowa sprawa. QuerySety są całkowicie niemutowalne. Kiedy wywołujesz metodę filter na istniejącym QuerySecie, nie modyfikuje to tego obiektu w miejscu. Zamiast tego, zwraca zupełnie nowy QuerySet z zaaplikowanymi dodatkowymi filtrami. Możesz utworzyć bazowy QuerySet, który filtruje tylko po manufacturer Tesla i przypisać go do zmiennej. Następnie możesz użyć tej jednej zmiennej do wygenerowania wielu różnych, przefiltrowanych QuerySetów dla różnych lat lub modeli, po prostu ponownie wywołując filter na bazowej zmiennej. Oryginalne bazowe query pozostaje całkowicie niezmienione. Ponieważ QuerySety są niemutowalne, możesz programatycznie konstruować bardzo specyficzne, złożone queries krok po kroku, bezpiecznie reużywając bazowych warunków w całej aplikacji, zanim w ogóle zostanie wykonany jakikolwiek network call. Dzięki, że wpadłeś. Mam nadzieję, że dowiedziałeś się czegoś nowego.
12

Vector Search dla AI

3m 58s

Zabezpiecz swoje umiejętności na przyszłość dzięki Vector Search w Cassandrze 5.0. Odkryj, jak przechowywać i odpytywać wielowymiarowe wektory, aby napędzać nowoczesne aplikacje sztucznej inteligencji i uczenia maszynowego.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Apache Cassandra z Pythonem, odcinek 12 z 12. Duże modele językowe zmieniają sposób, w jaki budujemy aplikacje, ale niosą ze sobą poważne wyzwanie w kwestii przechowywania danych. Gdy model sztucznej inteligencji potrzebuje kontekstu, tradycyjne zapytania do bazy danych zawodzą, ponieważ szukają dokładnych dopasowań znaków, całkowicie ignorując rzeczywistą intencję stojącą za promptem. Nowoczesne workloady sztucznej inteligencji wymagają zupełnie innego sposobu odpytywania danych. W tym miejscu do gry wchodzi Vector Search, wprowadzony natywnie w Cassandrze 5.0. Łatwo pomylić Vector Search z prostym, leksykalnym wyszukiwaniem full-text. Standardowy indeks full-text, taki jak Lucene, opiera się wyłącznie na słowach kluczowych. Jeśli użytkownik przeszukuje twoją bazę danych pod kątem frazy kopia zapasowa bazy danych, wyszukiwanie leksykalne skanuje dokładnie te stringi. Vector Search działa inaczej. Zamiast dosłownych słów kluczowych, wektory wychwytują głębsze, semantyczne znaczenie danych. Vector Search rozumie, że zapisanie snapshotu danych albo archiwizacja tabel odnoszą się do dokładnie tego samego konceptu co backup bazy danych, nawet jeśli te słowa nie mają ze sobą żadnych wspólnych liter. Aby to zadziałało, Cassandra polega na embeddingach wektorowych. Embedding to tablica liczb zmiennoprzecinkowych generowana przez model machine learningowy. Te tablice działają jak matematyczne współrzędne reprezentujące głębsze znaczenie twojego tekstu. Przechowujesz te tablice bezpośrednio w Cassandrze, obok standardowych danych twojej aplikacji. Kiedy musisz znaleźć odpowiednie treści w ogromnych zbiorach dokumentów, wykonujesz Vector Search. Baza danych porównuje wektor przychodzącego zapytania z wektorami zapisanymi w twoich tabelach. Oblicza matematyczną odległość między nimi. Mniejsze odległości oznaczają silniejsze podobieństwo semantyczne. Wyobraź sobie, że budujesz wewnętrznego chatbota dla dużego zespołu inżynierów. Masz tysiące stron dokumentacji technicznej. Inżynier wpisuje pytanie o to, jak zlikwidować klaster stagingowy. Najpierw twoja aplikacja przekazuje to pytanie do modelu generującego embeddingi, który tłumaczy zdanie na tablicę floatów. Następnie wysyłasz tę tablicę liczbową do Cassandry jako zapytanie Vector Search. Cassandra natychmiast skanuje wielowymiarową przestrzeń i pobiera wewnętrzne dokumenty, których embeddingi znajdują się najbliżej zadanego pytania. Zwraca odpowiednią instrukcję wycofywania środowisk testowych, ponieważ znaczenie semantyczne idealnie się pokrywa, niezależnie od innej terminologii. Ta funkcja została stworzona specjalnie z myślą o aplikacjach sztucznej inteligencji. Większość z tych aplikacji opiera się na pobieraniu wysoce trafnego kontekstu, aby przekazać go do modelu językowego, zanim ten wygeneruje odpowiedź. Wprowadzając Vector Search bezpośrednio do Cassandry 5.0, eliminujesz potrzebę uruchamiania oddzielnej, samodzielnej wektorowej bazy danych. Przechowujesz swoje główne rekordy operacyjne i ich semantyczne embeddingi w dokładnie tej samej rozproszonej architekturze, polegając na wysokiej dostępności i skalowaniu, które zapewnia Cassandra. Jako że to kończy naszą serię o Apache Cassandra, zachęcam cię do zgłębienia oficjalnej dokumentacji i samodzielnego wypróbowania generowania embeddingów dla twoich własnych danych. Jeśli masz sugestie, jakie technologie powinniśmy omówić w następnej kolejności, odwiedź stronę devstories dot eu i daj nam znać. Vector Search wypełnia lukę między językiem a storage'em, zamieniając złożone ludzkie intencje w problem geometryczny, który Cassandra potrafi rozwiązać w dużej skali. To wszystko w tym odcinku. Dzięki za wysłuchanie i nie przestawaj tworzyć!