Bitmappsindex i Go: sök med vild hastighet

Bitmappsindex i Go: sök med vild hastighet

introduktion

Jag gav den hÀr rapporten pÄ engelska vid GopherCon Russia 2019-konferensen i Moskva och pÄ ryska vid ett möte i Nizhny Novgorod. Vi pratar om ett bitmappsindex - mindre vanligt Àn B-trÀd, men inte mindre intressant. Delning inspelning tal vid konferensen pÄ engelska och textutskrifter pÄ ryska.

Vi kommer att titta pÄ hur ett bitmappsindex fungerar, nÀr det Àr bÀttre, nÀr det Àr sÀmre Àn andra index, och i vilka fall det Àr betydligt snabbare Àn dem; LÄt oss se vilka populÀra DBMS som redan har bitmappsindex; LÄt oss försöka skriva vÄrt eget i Go. Och "till efterrÀtt" kommer vi att anvÀnda fÀrdiga bibliotek för att skapa vÄr egen supersnabba specialiserade databas.

Jag hoppas verkligen att mina verk kommer att vara anvÀndbara och intressanta för dig. GÄ!

Inledning


http://bit.ly/bitmapindexes
https://github.com/mkevac/gopherconrussia2019

Hej alla! Klockan Ă€r sex pĂ„ kvĂ€llen och vi Ă€r alla supertrötta. Bra tid att prata om trĂ„kig databasindexteori, eller hur? Oroa dig inte, jag har ett par rader med kĂ€llkod hĂ€r och dĂ€r. 🙂

Bortsett frÄn alla skÀmt, rapporten Àr proppfull av information, och vi har inte mycket tid. SÄ lÄt oss börja.
Bitmappsindex i Go: sök med vild hastighet
Idag ska jag prata om följande:

  • vad Ă€r index;
  • vad Ă€r ett bitmappsindex;
  • var den anvĂ€nds och var den INTE anvĂ€nds och varför;
  • enkel implementering i Go och lite kamp med kompilatorn;
  • nĂ„got mindre enkel, men mycket mer produktiv implementering i Go assembler;
  • "problem" med bitmappsindex;
  • befintliga implementeringar.

SÄ vad Àr index?

Bitmappsindex i Go: sök med vild hastighet

Indexet Àr en separat datastruktur som vi underhÄller och uppdaterar utöver huvuddatan. Den anvÀnds för att pÄskynda sökningen. Utan index skulle sökning krÀva att man gick igenom data helt (en process som kallas full scan), och denna process har linjÀr algoritmisk komplexitet. Men databaser innehÄller vanligtvis enorma mÀngder data och den linjÀra komplexiteten Àr för lÄngsam. Helst skulle vi fÄ en logaritmisk eller konstant.

Det hÀr Àr ett enormt komplext Àmne, fyllt med finesser och avvÀgningar, men efter att ha tittat pÄ Ärtionden av databasutveckling och forskning Àr jag villig att sÀga att det bara finns ett fÄtal allmÀnt anvÀnda metoder för att skapa databasindex.

Bitmappsindex i Go: sök med vild hastighet

Det första tillvÀgagÄngssÀttet Àr att hierarkiskt minska sökutrymmet, dela upp sökutrymmet i mindre delar.

Vi brukar göra detta med olika sorters trÀd. Ett exempel skulle vara en stor lÄda med material i din garderob som innehÄller mindre lÄdor med material uppdelade i olika Àmnen. Om du behöver material, kommer du förmodligen att leta efter dem i en ruta som sÀger "Material" snarare Àn en som sÀger "Cookies", eller hur?

Bitmappsindex i Go: sök med vild hastighet

Det andra tillvÀgagÄngssÀttet Àr att omedelbart vÀlja önskat element eller grupp av element. Vi gör detta i hashkartor eller omvÀnda index. Att anvÀnda hashkartor Àr vÀldigt likt det tidigare exemplet, men istÀllet för en lÄda med lÄdor har du en massa smÄ lÄdor med sista föremÄl i din garderob.

Bitmappsindex i Go: sök med vild hastighet

Det tredje tillvÀgagÄngssÀttet Àr att eliminera behovet av sökning. Det gör vi med hjÀlp av Bloom-filter eller gökfilter. De första ger ett svar direkt, vilket gör att du slipper söka.

Bitmappsindex i Go: sök med vild hastighet

Det sista tillvÀgagÄngssÀttet Àr att fullt ut utnyttja all kraft som modern hÄrdvara ger oss. Det Àr precis vad vi gör i bitmappsindex. Ja, nÀr vi anvÀnder dem behöver vi ibland gÄ igenom hela indexet, men vi gör det supereffektivt.

Som jag sa, Àmnet databasindex Àr stort och fullt av kompromisser. Det gör att vi ibland kan anvÀnda flera tillvÀgagÄngssÀtt samtidigt: om vi behöver pÄskynda sökningen Ànnu mer, eller om vi behöver tÀcka in alla möjliga söktyper.

Idag kommer jag att prata om det minst kÀnda tillvÀgagÄngssÀttet för dessa - bitmappsindex.

Vem Àr jag att tala om detta Àmne?

Bitmappsindex i Go: sök med vild hastighet

Jag arbetar som teamleader pÄ Badoo (du kanske Àr mer bekant med vÄr andra produkt, Bumble). Vi har redan mer Àn 400 miljoner anvÀndare runt om i vÀrlden och mÄnga funktioner som vÀljer den bÀsta matchningen för dem. Vi gör detta med hjÀlp av anpassade tjÀnster, inklusive bitmappsindex.

SÄ vad Àr ett bitmappsindex?

Bitmappsindex i Go: sök med vild hastighet
Bitmappsindex, som namnet antyder, anvÀnder bitmappar eller bituppsÀttningar för att implementera ett sökindex. Ur fÄgelperspektiv bestÄr detta index av en eller flera sÄdana bitmappar som representerar alla enheter (som mÀnniskor) och deras egenskaper eller parametrar (Älder, ögonfÀrg, etc.), och en algoritm som anvÀnder bitoperationer (AND, OR, NOT) ) för att svara pÄ sökfrÄgan.
Bitmappsindex i Go: sök med vild hastighet
Vi fÄr höra att bitmappsindex Àr bÀst lÀmpade och mycket presterande för fall dÀr det finns sökningar som kombinerar sökfrÄgor över mÄnga kolumner med lÄg kardinalitet (tÀnk "ögonfÀrg" eller "Àktenskaplig status" kontra nÄgot som "avstÄnd frÄn centrum" ). Men jag ska visa senare att de fungerar utmÀrkt Àven för kolumner med hög kardinalitet.

LÄt oss titta pÄ det enklaste exemplet pÄ ett bitmappsindex.
Bitmappsindex i Go: sök med vild hastighet
FörestÀll dig att vi har en lista över restauranger i Moskva med binÀra egenskaper som dessa:

  • nĂ€ra tunnelbana;
  • det finns privat parkering;
  • det finns en veranda (har terrass);
  • du kan boka bord (accepterar reservationer);
  • lĂ€mplig för vegetarianer (veganvĂ€nlig);
  • dyrt (dyrt).

Bitmappsindex i Go: sök med vild hastighet
LÄt oss ge varje restaurang ett sekvensnummer som börjar frÄn 0 och allokera minne för 6 bitmappar (en för varje egenskap). Vi kommer sedan att fylla i dessa bitmappar beroende pÄ om restaurangen har den hÀr egenskapen eller inte. Om restaurang 4 har en veranda kommer bit nr 4 i bitmappen "har en veranda" att sÀttas till 1 (om det inte finns nÄgon veranda, dÄ till 0).
Bitmappsindex i Go: sök med vild hastighet
Nu har vi det enklaste bitmappsindexet som Àr möjligt, och vi kan anvÀnda det för att svara pÄ frÄgor som:

  • "Visa mig vegetariska restauranger";
  • "Visa mig billiga restauranger med en veranda dĂ€r du kan boka bord."

Bitmappsindex i Go: sök med vild hastighet
Bitmappsindex i Go: sök med vild hastighet
Hur? LÄt oss ta en titt. Den första begÀran Àr mycket enkel. Allt vi behöver göra Àr att ta den "vegetarvÀnliga" bitmappen och förvandla den till en lista över restauranger vars bitar Àr exponerade.
Bitmappsindex i Go: sök med vild hastighet
Bitmappsindex i Go: sök med vild hastighet
Den andra begÀran Àr lite mer komplicerad. Vi mÄste anvÀnda NOT-bitmappen pÄ den "dyra" bitmappen för att fÄ en lista över billiga restauranger, sedan OCH den med bitmappen "kan jag boka bord" och OCH resultatet med bitmappen "det finns en veranda". Den resulterande bitmappen kommer att innehÄlla en lista över anlÀggningar som uppfyller alla vÄra kriterier. I det hÀr exemplet Àr detta bara restaurangen Yunost.
Bitmappsindex i Go: sök med vild hastighet
Bitmappsindex i Go: sök med vild hastighet
Det Àr mycket teori inblandat, men oroa dig inte, vi kommer att se koden mycket snart.

Var anvÀnds bitmappsindex?

Bitmappsindex i Go: sök med vild hastighet
Om du Google bitmappsindexerar kommer 90% av svaren att vara relaterade till Oracle DB pÄ ett eller annat sÀtt. Men andra DBMS:er stöder sÀkert ocksÄ en sÄdan cool grej, eller hur? Inte riktigt.

LÄt oss gÄ igenom listan över huvudmisstÀnkta.
Bitmappsindex i Go: sök med vild hastighet
MySQL stöder Ànnu inte bitmappsindex, men det finns ett förslag som föreslÄr att du lÀgger till det hÀr alternativet (https://dev.mysql.com/worklog/task/?id=1524).

PostgreSQL stöder inte bitmappsindex, men anvÀnder enkla bitmappar och bitoperationer för att kombinera sökresultat över flera andra index.

Tarantool har bituppsÀttningsindex och stöder enkla sökningar pÄ dem.

Redis har enkla bitfÀlt (https://redis.io/commands/bitfield) utan möjlighet att söka efter dem.

MongoDB stöder Ànnu inte bitmappsindex, men det finns ocksÄ ett förslag som föreslÄr att detta alternativ lÀggs till https://jira.mongodb.org/browse/SERVER-1723

Elasticsearch anvÀnder bitmappar internt (https://www.elastic.co/blog/frame-of-reference-and-roaring-bitmaps).

Bitmappsindex i Go: sök med vild hastighet

  • Men en ny granne har dykt upp i vĂ„rt hus: Pilosa. Detta Ă€r en ny icke-relationell databas skriven i Go. Den innehĂ„ller bara bitmappsindex och baserar allt pĂ„ dem. Vi ska prata om det lite senare.

Implementering i Go

Men varför anvÀnds bitmappsindex sÄ sÀllan? Innan jag svarar pÄ den hÀr frÄgan skulle jag vilja visa dig hur du implementerar ett mycket enkelt bitmappsindex i Go.
Bitmappsindex i Go: sök med vild hastighet
Bitmaps Àr i huvudsak bara bitar av data. I Go, lÄt oss anvÀnda byte-skivor för detta.

Vi har en bitmapp för en restaurangegenskap, och varje bit i bitmappen anger om en viss restaurang har den hÀr egenskapen eller inte.
Bitmappsindex i Go: sök med vild hastighet
Vi kommer att behöva tvÄ hjÀlpfunktioner. En kommer att anvÀndas för att fylla vÄra bitmappar med slumpmÀssiga data. SlumpmÀssigt, men med en viss sannolikhet att restaurangen har varje fastighet. Till exempel tror jag att det finns vÀldigt fÄ restauranger i Moskva dÀr man inte kan boka bord, och det förefaller mig som om cirka 20 % av anlÀggningarna Àr lÀmpliga för vegetarianer.

Den andra funktionen konverterar bitmappen till en lista över restauranger.
Bitmappsindex i Go: sök med vild hastighet
Bitmappsindex i Go: sök med vild hastighet
För att svara pÄ frÄgan "Visa mig billiga restauranger som har en uteplats och kan göra reservationer" behöver vi tvÄ bitars operationer: NOT och AND.

Vi kan förenkla vÄr kod lite genom att anvÀnda den mer komplexa AND NOT-operatorn.

Vi har funktioner för var och en av dessa operationer. BÄda gÄr igenom skivorna, tar motsvarande element frÄn var och en, kombinerar dem med lite operation och lÀgger resultatet i den resulterande skivan.
Bitmappsindex i Go: sök med vild hastighet
Och nu kan vi anvÀnda vÄra bitmappar och funktioner för att svara pÄ sökfrÄgan.
Bitmappsindex i Go: sök med vild hastighet
Prestanda Àr inte sÄ hög, Àven om funktionerna Àr vÀldigt enkla och vi sparade mycket pengar genom att inte returnera en ny resulterande skiva varje gÄng funktionen anropades.

Efter att ha gjort lite profilering med pprof, mÀrkte jag att Go-kompilatorn saknade en mycket enkel men mycket viktig optimering: funktionsinförande.
Bitmappsindex i Go: sök med vild hastighet
Faktum Àr att Go-kompilatorn Àr fruktansvÀrt rÀdd för loopar som gÄr igenom skivor, och vÀgrar kategoriskt att infoga funktioner som innehÄller sÄdana loopar.
Bitmappsindex i Go: sök med vild hastighet
Men jag Àr inte rÀdd och jag kan lura kompilatorn genom att anvÀnda goto istÀllet för en loop, som pÄ den gamla goda tiden.

Bitmappsindex i Go: sök med vild hastighet
Bitmappsindex i Go: sök med vild hastighet

Och, som du kan se, nu kommer kompilatorn med glÀdje att infoga vÄr funktion! Som ett resultat lyckas vi spara cirka 2 mikrosekunder. Inte dÄligt!

Bitmappsindex i Go: sök med vild hastighet

Den andra flaskhalsen Àr lÀtt att se om man tittar noga pÄ monteringsutdata. Kompilatorn lade till en skivgrÀnskontroll mitt i vÄr hetaste loop. Faktum Àr att Go Àr ett sÀkert sprÄk, kompilatorn Àr rÀdd att mina tre argument (tre skivor) Àr av olika storlek. NÀr allt kommer omkring kommer det att finnas en teoretisk möjlighet att ett sÄ kallat buffertspill intrÀffar.

LÄt oss lugna kompilatorn genom att visa att alla skivor har samma storlek. Vi kan göra detta genom att lÀgga till en enkel bock i början av vÄr funktion.
Bitmappsindex i Go: sök med vild hastighet
NÀr kompilatorn ser detta hoppar kompilatorn glatt över kontrollen och det slutar med att vi sparar ytterligare 500 nanosekunder.

Stora butcher

Okej, vi lyckades pressa ut lite prestanda ur vÄr enkla implementering, men detta resultat Àr faktiskt mycket sÀmre Àn vad som Àr möjligt med nuvarande hÄrdvara.

Allt vi gör Àr grundlÀggande bitoperationer, och vÄra processorer utför dem mycket effektivt. Men tyvÀrr "matar" vi vÄr processor med mycket smÄ bitar av arbete. VÄra funktioner utför operationer pÄ en byte-för-byte-basis. Vi kan mycket enkelt justera vÄr kod sÄ att den fungerar med 8-byte-bitar med UInt64-skivor.

Bitmappsindex i Go: sök med vild hastighet

Som du kan se pÄskyndade denna lilla förÀndring vÄrt program Ätta gÄnger genom att öka batchstorleken med Ätta gÄnger. FörstÀrkningen kan sÀgas vara linjÀr.

Bitmappsindex i Go: sök med vild hastighet

Implementering i assembler

Bitmappsindex i Go: sök med vild hastighet
Men detta Àr inte slutet. VÄra processorer kan arbeta med bitar pÄ 16, 32 och till och med 64 byte. SÄdana "breda" operationer kallas single-instruction multiple data (SIMD; en instruktion, mÄnga data), och processen att transformera kod sÄ att den anvÀnder sÄdana operationer kallas vektorisering.

TyvÀrr Àr Go-kompilatorn lÄngt ifrÄn utmÀrkt pÄ vektorisering. För nÀrvarande Àr det enda sÀttet att vektorisera Go-kod att ta och lÀgga dessa operationer manuellt med Go assembler.

Bitmappsindex i Go: sök med vild hastighet

Go assembler Àr ett konstigt odjur. Du vet sÀkert att assemblersprÄk Àr nÄgot som Àr starkt knutet till arkitekturen pÄ datorn du skriver för, men sÄ Àr det inte i Go. Go assembler Àr mer som ett IRL (intermediate representation language) eller mellansprÄk: det Àr praktiskt taget plattformsoberoende. Rob Pike gjorde en utmÀrkt prestation Rapportera om detta Àmne för flera Är sedan pÄ GopherCon i Denver.

Dessutom anvÀnder Go ett ovanligt Plan 9-format, som skiljer sig frÄn de allmÀnt accepterade AT&T- och Intel-formaten.
Bitmappsindex i Go: sök med vild hastighet
Det Àr sÀkert att sÀga att det inte Àr det roligaste att skriva Go assembler för hand.

Men lyckligtvis finns det redan tvÄ verktyg pÄ hög nivÄ som hjÀlper oss att skriva Go assembler: PeachPy och avo. BÄda verktygen genererar Go assembler frÄn kod pÄ högre nivÄ skriven i Python respektive Go.
Bitmappsindex i Go: sök med vild hastighet
Dessa verktyg förenklar saker som registertilldelning, skrivning av loopar och förenklar i allmÀnhet processen att komma in i en vÀrld av monteringsprogrammering i Go.

Vi kommer att anvÀnda avo, sÄ vÄra program kommer att vara nÀstan vanliga Go-program.
Bitmappsindex i Go: sök med vild hastighet
SÄ hÀr ser det enklaste exemplet pÄ ett avo-program ut. Vi har en main()-funktion, som inom sig definierar Add()-funktionen, vars betydelse Àr att addera tvÄ tal. Det finns hjÀlpfunktioner hÀr för att fÄ parametrar efter namn och fÄ ett av de gratis och lÀmpliga processorregistren. Varje processoroperation har en motsvarande funktion pÄ avo, som ses i ADDQ. Slutligen ser vi en hjÀlpfunktion för att lagra det resulterande vÀrdet.
Bitmappsindex i Go: sök med vild hastighet
Genom att anropa go generera kommer vi att köra programmet pÄ avo och som ett resultat kommer tvÄ filer att genereras:

  • add.s med den resulterande koden i Go assembler;
  • stub.go med funktionsrubriker för att koppla samman de tvĂ„ vĂ€rldarna: Go och assembler.

Bitmappsindex i Go: sök med vild hastighet
Nu nÀr vi har sett vad avo gör och hur, lÄt oss titta pÄ vÄra funktioner. Jag implementerade bÄde skalÀra och vektorversioner (SIMD) av funktionerna.

LÄt oss först titta pÄ de skalÀra versionerna.
Bitmappsindex i Go: sök med vild hastighet
Liksom i föregÄende exempel ber vi om ett gratis och giltigt register för allmÀnt ÀndamÄl, vi behöver inte berÀkna offset och storlekar för argumenten. avo gör allt detta för oss.
Bitmappsindex i Go: sök med vild hastighet
Vi brukade anvÀnda etiketter och goto (eller hopp) för att förbÀttra prestanda och lura Go-kompilatorn, men nu gör vi det frÄn början. PoÀngen Àr att cykler Àr ett begrepp pÄ högre nivÄ. I assembler har vi bara etiketter och hopp.
Bitmappsindex i Go: sök med vild hastighet
Den ÄterstÄende koden bör redan vara bekant och förstÄelig. Vi emulerar en slinga med etiketter och hopp, tar en liten bit data frÄn vÄra tvÄ skivor, kombinerar dem med en bitoperation (OCH INTE i det hÀr fallet) och lÀgger sedan in resultatet i den resulterande skivan. Allt.
Bitmappsindex i Go: sök med vild hastighet
SÄ hÀr ser den slutliga assemblerkoden ut. Vi behövde inte berÀkna offset och storlekar (markerade i grönt) eller hÄlla reda pÄ vilka register som anvÀndes (markerade i rött).
Bitmappsindex i Go: sök med vild hastighet
Om vi ​​jĂ€mför prestandan för implementeringen av assemblersprĂ„ket med prestandan för den bĂ€sta implementeringen i Go kommer vi att se att det Ă€r samma sak. Och detta förvĂ€ntas. Vi gjorde trots allt inget speciellt – vi Ă„tergav bara vad en Go-kompilator skulle göra.

TyvÀrr kan vi inte tvinga kompilatorn att infoga vÄra funktioner skrivna pÄ assemblersprÄk. Go-kompilatorn har för nÀrvarande inte en sÄdan funktion, Àven om det har funnits en begÀran om att lÀgga till den under ganska lÄng tid.

Det Àr dÀrför det Àr omöjligt att fÄ nÄgon nytta av smÄ funktioner i assemblersprÄk. Vi mÄste antingen skriva stora funktioner, eller anvÀnda det nya math/bits-paketet, eller kringgÄ assembler-sprÄket.

LÄt oss nu titta pÄ vektorversionerna av vÄra funktioner.
Bitmappsindex i Go: sök med vild hastighet
För det hÀr exemplet bestÀmde jag mig för att anvÀnda AVX2, sÄ vi kommer att anvÀnda operationer som fungerar pÄ 32-byte bitar. Strukturen pÄ koden Àr mycket lik den skalÀra versionen: ladda parametrar, be om ett gratis delat register, etc.
Bitmappsindex i Go: sök med vild hastighet
En innovation Àr att bredare vektoroperationer anvÀnder speciella breda register. I fallet med 32-byte bitar Àr dessa register prefixerade med Y. Det Àr dÀrför du ser YMM()-funktionen i koden. Om jag anvÀnde AVX-512 med 64-bitars bitar, skulle prefixet vara Z.

Den andra innovationen Àr att jag bestÀmde mig för att anvÀnda en optimering som kallas loop unrolling, vilket innebÀr att man gör Ätta loopoperationer manuellt innan man hoppar till början av loopen. Denna optimering minskar antalet filialer i koden och begrÀnsas av antalet tillgÀngliga gratisregister.
Bitmappsindex i Go: sök med vild hastighet
Tja, hur Àr det med prestanda? Hon Àr vacker! Vi uppnÄdde en speedup pÄ cirka sju gÄnger jÀmfört med den bÀsta Go-lösningen. Imponerande, eller hur?
Bitmappsindex i Go: sök med vild hastighet
Men Àven denna implementering skulle potentiellt kunna accelereras genom att anvÀnda AVX-512, förhÀmtning eller en JIT (just-in-time kompilator) för frÄgeschemalÀggaren. Men detta Àr verkligen ett Àmne för en separat rapport.

Problem med bitmappsindex

Nu nÀr vi redan har tittat pÄ en enkel implementering av ett bitmappsindex i Go och ett mycket mer produktivt i assemblersprÄk, lÄt oss Àntligen prata om varför bitmappsindex anvÀnds sÄ sÀllan.
Bitmappsindex i Go: sök med vild hastighet
I Àldre tidningar nÀmns tre problem med bitmappsindex, men nyare tidningar och jag hÀvdar att de inte lÀngre Àr relevanta. Vi kommer inte att fördjupa oss i vart och ett av dessa problem, utan kommer att titta pÄ dem ytligt.

Problemet med hög kardinalitet

SÄ vi fÄr veta att bitmappsindex endast Àr lÀmpliga för fÀlt med lÄg kardinalitet, det vill sÀga de som har fÄ vÀrden (till exempel kön eller ögonfÀrg), och anledningen Àr att den vanliga representationen av sÄdana fÀlt (ett bit per vÀrde) vid hög kardinalitet kommer det att ta upp för mycket utrymme och dessutom kommer dessa bitmappsindex att vara dÄligt (sÀllan) fyllda.
Bitmappsindex i Go: sök med vild hastighet
Bitmappsindex i Go: sök med vild hastighet
Ibland kan vi anvÀnda en annan representation, till exempel den standard vi anvÀnder för att representera siffror. Men det var tillkomsten av komprimeringsalgoritmer som förÀndrade allt. Under de senaste decennierna har forskare och forskare kommit fram till ett stort antal komprimeringsalgoritmer för bitmappar. Deras frÀmsta fördel Àr att det inte finns nÄgot behov av att dekomprimera bitmappar för att utföra bitoperationer - vi kan utföra bitoperationer direkt pÄ komprimerade bitmappar.
Bitmappsindex i Go: sök med vild hastighet
Nyligen har hybridmetoder börjat dyka upp, som rytande bitmappar. De anvĂ€nder samtidigt tre olika representationer för bitmappar – sjĂ€lva bitmappar, arrayer och sĂ„ kallade bitkörningar – och balanserar mellan dem för att maximera prestanda och minimera minnesförbrukningen.

Du kan hitta brusande bitmappar i de mest populÀra applikationerna. Det finns redan ett stort antal implementeringar för en mÀngd olika programmeringssprÄk, inklusive mer Àn tre implementeringar för Go.
Bitmappsindex i Go: sök med vild hastighet
Ett annat tillvÀgagÄngssÀtt som kan hjÀlpa oss att hantera hög kardinalitet kallas binning. FörestÀll dig att du har ett fÀlt som representerar en persons lÀngd. Höjd Àr ett flyttal, men vi mÀnniskor tÀnker inte pÄ det sÄ. För oss Àr det ingen skillnad mellan höjd 185,2 cm och 185,3 cm.

Det visar sig att vi kan gruppera liknande vÀrden i grupper inom 1 cm.

Och om vi dessutom vet att vÀldigt fÄ mÀnniskor Àr kortare Àn 50 cm och lÀngre Àn 250 cm, sÄ kan vi i princip förvandla ett fÀlt med oÀndlig kardinalitet till ett fÀlt med en kardinalitet pÄ cirka 200 vÀrden.

Vid behov kan vi naturligtvis göra ytterligare filtrering i efterhand.

Problem med hög bandbredd

NÀsta problem med bitmappsindex Àr att det kan bli mycket dyrt att uppdatera dem.

Databaser mÄste kunna uppdatera data medan potentiellt hundratals andra frÄgor söker efter data. Vi behöver lÄs för att undvika problem med samtidig dataÄtkomst eller andra delningsproblem. Och dÀr det finns ett stort lÄs, finns det ett problem - lÄsstrid, nÀr detta lÄs blir en flaskhals.
Bitmappsindex i Go: sök med vild hastighet
Detta problem kan lösas eller kringgÄs genom att anvÀnda sharding eller anvÀnda versionsindex.

Sharding Àr en enkel och vÀlkÀnd sak. Du kan dela ett bitmappsindex pÄ samma sÀtt som andra data. IstÀllet för ett stort lÄs kommer du att fÄ ett gÀng smÄ lÄs och dÀrmed bli av med lÄsstriden.

Det andra sÀttet att lösa problemet Àr att anvÀnda versionerade index. Du kan ha en kopia av indexet som du anvÀnder för att söka eller lÀsa, och en som du anvÀnder för att skriva eller uppdatera. Och en gÄng under en viss tidsperiod (till exempel en gÄng var 100:e ms eller 500 ms) duplicerar du dem och byter ut dem. Naturligtvis Àr detta tillvÀgagÄngssÀtt endast tillÀmpligt i de fall din applikation kan hantera ett nÄgot efterslÀpande sökindex.

Dessa tvÄ tillvÀgagÄngssÀtt kan anvÀndas samtidigt: du kan ha ett fragmenterat versionsindex.

Mer komplexa frÄgor

Det sista problemet med bitmappsindex Àr att vi fÄr höra att de inte Àr vÀl lÀmpade för mer komplexa typer av frÄgor, sÄsom span-frÄgor.

Faktum Àr att om du tÀnker efter sÄ Àr bitoperationer som AND, OR, etc. inte sÀrskilt lÀmpliga för frÄgor a la "Visa mig hotell med rumspriser frÄn 200 till 300 dollar per natt."
Bitmappsindex i Go: sök med vild hastighet
En naiv och mycket oklok lösning skulle vara att ta resultaten för varje dollarvÀrde och kombinera dem med en bitvis ELLER-operation.
Bitmappsindex i Go: sök med vild hastighet
En lite bÀttre lösning skulle vara att anvÀnda gruppering. Till exempel i grupper om 50 dollar. Detta skulle pÄskynda vÄr process med 50 gÄnger.

Men problemet löses ocksÄ enkelt genom att anvÀnda en vy som skapats specifikt för denna typ av förfrÄgningar. I vetenskapliga artiklar kallas det för avstÄndskodade bitmappar.
Bitmappsindex i Go: sök med vild hastighet
I den hÀr representationen stÀller vi inte bara in en bit för nÄgot vÀrde (till exempel 200), utan stÀller in detta vÀrde och allt högre. 200 och uppÄt. Samma för 300:300 och uppÄt. Och sÄ vidare.

Med den hÀr representationen kan vi svara pÄ den hÀr typen av sökfrÄgor genom att gÄ igenom indexet bara tvÄ gÄnger. Först kommer vi att fÄ en lista över hotell dÀr rummet kostar mindre eller $300, och sedan tar vi bort de dÀr rummet kostar mindre eller $199. Redo.
Bitmappsindex i Go: sök med vild hastighet
Du kommer att bli förvÄnad, men Àven geoqueries Àr möjliga med hjÀlp av bitmappsindex. Tricket Àr att anvÀnda en geometrisk representation som omger din koordinat med en geometrisk figur. Till exempel S2 frÄn Google. Figuren ska vara möjlig att representera i form av tre eller flera korsande linjer som kan numreras. PÄ sÄ sÀtt kan vi förvandla vÄr geoquery till flera frÄgor "langs gapet" (lÀngs dessa numrerade rader).

Klara lösningar

Jag hoppas att jag intresserade dig lite och att du nu har ytterligare ett anvÀndbart verktyg i din arsenal. Om du nÄgon gÄng behöver göra nÄgot sÄnt hÀr, vet du vart du ska leta.

Men alla har inte tid, tÄlamod eller resurser att skapa bitmappsindex frÄn början. SÀrskilt mer avancerade, med till exempel SIMD.

Som tur Àr finns det flera fÀrdiga lösningar som hjÀlper dig.
Bitmappsindex i Go: sök med vild hastighet

Rytande bitmappar

För det första finns det samma rytande bitmapsbibliotek som jag redan pratat om. Den innehÄller alla nödvÀndiga behÄllare och bitoperationer som du behöver för att göra ett fullfjÀdrat bitmappsindex.
Bitmappsindex i Go: sök med vild hastighet
TyvÀrr, för tillfÀllet anvÀnder ingen av Go-implementeringarna SIMD, vilket innebÀr att Go-implementationer Àr mindre prestanda Àn C-implementationer, till exempel.

hÄr

En annan produkt som kan hjÀlpa dig Àr Pilosa DBMS, som faktiskt bara har bitmappsindex. Detta Àr en relativt ny lösning, men den vinner hjÀrtan i hög hastighet.
Bitmappsindex i Go: sök med vild hastighet
Pilosa anvÀnder brusande bitmappar internt och ger dig möjligheten att anvÀnda dem, förenklar och förklarar allt jag pratade om ovan: gruppering, avstÄndskodade bitmappar, konceptet med ett fÀlt osv.

LÄt oss ta en snabb titt pÄ ett exempel pÄ hur du anvÀnder Pilosa för att svara pÄ en frÄga du redan Àr bekant med.
Bitmappsindex i Go: sök med vild hastighet
Exemplet Àr vÀldigt likt det du sÄg tidigare. Vi skapar en klient till Pilosa-servern, skapar ett index och de nödvÀndiga fÀlten, fyller sedan vÄra fÀlt med slumpmÀssiga data med sannolikheter och, slutligen, exekverar vi den vÀlbekanta frÄgan.

Efter det anvÀnder vi INTE pÄ fÀltet "dyrt", skÀr sedan resultatet (eller OCH det) med fÀltet "terrass" och med fÀltet "reservationer". Och Àntligen fÄr vi slutresultatet.
Bitmappsindex i Go: sök med vild hastighet
Jag hoppas verkligen att den hÀr nya typen av index inom en överskÄdlig framtid ocksÄ kommer att dyka upp i DBMS som MySQL och PostgreSQL - bitmappsindex.
Bitmappsindex i Go: sök med vild hastighet

Slutsats

Bitmappsindex i Go: sök med vild hastighet
Om du inte har somnat Àn, tack. Jag var tvungen att kort beröra mÄnga Àmnen pÄ grund av begrÀnsad tid, men jag hoppas att föredraget var anvÀndbart och kanske till och med motiverande.

Bitmapindex Àr bra att veta om, Àven om du inte behöver dem just nu. LÄt dem vara ytterligare ett verktyg i din verktygslÄda.

Vi har tittat pÄ olika prestandatrick för Go och saker som Go-kompilatorn inte hanterar sÀrskilt bra Ànnu. Men detta Àr absolut anvÀndbart för alla Go-programmerare att veta.

Det var allt jag ville berÀtta för dig. Tack!

KĂ€lla: will.com

LĂ€gg en kommentar