Cassandra Indexing: the Good, the Bad and The Ugly

inom NoSQL är operationerna för indexering, hämtning och sökning efter information intimt knutna till de fysiska lagringsmekanismerna. Det är viktigt att komma ihåg att rader lagras över värdar, men en enda rad lagras på en enda värd. (med repliker) kolumnfamiljer lagras i sorterad ordning, vilket gör att frågan om en uppsättning kolumner är effektiv (förutsatt att du spänner över rader).

Den Dåliga : Partitionering

en av de tuffa sakerna att vänja sig vid först är att utan några indexfrågor som spänner över rader kan (mycket) vara dåliga. Att tänka tillbaka till vår lagringsmodell är dock inte förvånande. Strategin som Cassandra använder för att distribuera raderna över värdar kallas partitionering.

partitionering är handlingen att skära upp utbudet av rowkeys som tilldelar dem till ”token ring”, som också tilldelar ansvaret för ett segment (dvs. partition) av rowkey-intervallet till varje värd. Du har förmodligen sett detta när du initierade ditt kluster med en ”token”. Token ger värden en plats längs tokenringen, som tilldelar ansvaret för en del av tokenområdet. Partitionering är handlingen att kartlägga radnyckeln i tokenområdet.

det finns två primära partitioners: Random och ordning bevara. De är lämpligt namngivna. Den RandomPartitioner hashes rowkeys i tokens. Med RandomPartitioner är token en hash av rowkey. Detta gör ett bra jobb med att jämnt fördela dina data över en uppsättning noder, men gör det otroligt svårt att fråga ett intervall av rowkey-utrymmet. Från bara ett ”start rowkey” – värde och ett ”end rowkey” – värde kan Cassandra inte bestämma vilket intervall av tokenutrymmet du behöver. Det behöver i huvudsak utföra en” tabellsökning ”för att svara på frågan, och en” tabellsökning ” i Cassandra är dålig eftersom den måste gå till varje maskin (troligtvis alla maskiner om du har en bra hashfunktion) för att svara på frågan.

nu, till den stora kostnaden för jämn datadistribution, kan du använda Orderserveringpartitioner (OPP). Jag är* inte * nere med OPP. OPP bevarar ordningen när den översätter rowkeys till tokens. Nu, med tanke på ett start rowkey-värde och ett end rowkey-värde, kan Cassandra ** bestämma exakt vilka värdar som har de data du letar efter. Det beräknar startvärdet till en token slutvärdet till en token, och helt enkelt väljer och returnerar allt däremellan. Men genom att bevara ordningen, om inte dina rowkeys är jämnt fördelade över utrymmet, kommer dina tokens inte heller att vara och du får ett skevt kluster, vilket kraftigt ökar kostnaden för konfiguration och administration av klustret. (inte värt det)

det goda: sekundära index

Cassandra tillhandahåller en inbyggd indexeringsmekanism i sekundära index. Sekundära index fungerar av kolumnvärdena. Du deklarerar ett sekundärt index på en Kolumnfamilj. Datastax har bra dokumentation om användningen. Under huven upprätthåller Cassandra en” dold kolumnfamilj ” som index. (Se Ed Anuffs presentation för detaljer) eftersom Cassandra inte behåller kolumnvärdesinformation i någon nod och sekundära index är på kolumnvärde (snarare än rowkeys), måste en fråga fortfarande skickas till alla noder. Dessutom rekommenderas inte sekundära index för uppsättningar med hög kardinalitet. Jag har inte tittat ännu, men jag antar att det här beror på datamodellen som används inom ”hidden column family”. Om den dolda kolumnfamiljen lagrar en rad per unikt värde (med radnycklar som kolumner), skulle det innebära att skanna raderna för att avgöra om de ligger inom intervallet i frågan.
från Eds presentation:

  • rekommenderas inte för höga kardinalitetsvärden (dvs. tidsstämplar,födelsedatum,nyckelord etc.)
  • kräver minst en jämlikhetsjämförelse i en fråga-inte bra för mindre än/större än / intervallfrågor
  • osorterade-resultaten är i tokenordning, inte frågevärdesordning
  • begränsad till sökning på datatyper, Cassandra förstår nativt

med allt detta sagt, sekundära index fungerar ur lådan och vi har haft god framgång med att använda dem på enkla värden.

den fula: gör-det-själv (DIY) / breda rader

nu är skönheten i betraktarens öga. En av de vackra sakerna med NoSQL är enkelheten. Konstruktionerna är enkla: Nyckelrum, kolumnfamiljer, rader och kolumner. Att hålla det enkelt men betyder ibland att du måste ta saker i egna händer.

Detta är fallet med breda radindex. Med hjälp av Cassandras lagringsmodell är det enkelt att bygga egna index där varje radnyckel blir en kolumn i indexet. Detta är ibland svårt att få huvudet runt, men låt oss föreställa oss att vi har ett fall där vi vill välja alla användare i ett Postnummer. Huvudanvändarkolumnfamiljen är knappad på userid, postnummer är en kolumn på varje användarrad. Vi kan använda sekundära index, men det finns en hel del Postnummer. Istället kunde vi behålla en kolumnfamilj med en enda rad som heter ”idx_zipcode”. Vi kunde sedan skriva kolumner i denna rad i formuläret ”zipcode_userid”. Eftersom kolumnerna lagras i sorterad ordning är det snabbt att fråga efter alla kolumner som börjar med ”18964” (t.ex. kan vi använda 18964_ och 18964_zzzzzz som start-och slutvärden).

en uppenbar nackdel med detta tillvägagångssätt är att rader är fristående på en värd. (igen med undantag för repliker) detta innebär att alla frågor kommer att träffa en enda nod. Jag har ännu inte hittat ett bra svar på detta.

dessutom, och IMHO, är den fulaste delen av DIY bred rad indexering ur ett klientperspektiv. I vår implementering har vi gjort vårt bästa för att vara språklig agnostiker på klientsidan, så att människor kan välja det bästa verktyget för jobbet för att interagera med data i Cassandra. Med den mentaliteten presenterar DIY-indexen några problem. Breda rader använder ofta kompositnycklar (tänk om du hade en idx_state_zip, vilket skulle låta dig fråga efter stat då zip). Även om det finns ”inbyggt” stöd för kompositnycklar, implementerar alla klientbibliotek sin egen version av dem (Hector, Astyanax och Thrift). Det betyder att klienten behöver fråga data måste ha den extra logiken för att först fråga indexet, och dessutom måste alla klienter konstruera kompositnyckeln på samma sätt.

Gör Det Bättre…

av denna anledning har vi beslutat att släppa två open source-projekt som hjälper till att driva denna logik till serversidan. Det första projektet är Cassandra-Triggers. Detta gör att du kan bifoga asynkrona aktiviteter för att skriva i Cassandra. (en sådan aktivitet kan vara indexering) vi har också släppt Cassandra-indexering. Det här är varmt av pressarna och är fortfarande i sin linda (t.ex. stöder det bara UT8Types i indexet), men avsikten är att tillhandahålla en generisk server-side-mekanism som indexerar data som den skrivs till Cassandra. Med samma server-side-teknik som vi använde i Cassandra-indexering konfigurerar du helt enkelt de kolumner du vill indexera, och AOP-koden gör resten när du skriver till målet CF. Som alltid är frågor, kommentarer och tankar välkomna. (speciellt om jag är utanför basen någonstans)

Lämna ett svar

Din e-postadress kommer inte publiceras.