Bitmap-index i Go: sökning i vild hastighet

Bitmap-index i Go: sökning i vild hastighet

Inledande ord

Jag presenterade detta föredrag pĂ„ engelska vid GopherCon Russia 2019 i Moskva och pĂ„ ryska vid meet-up i Nizjnij Novgorod. Det handlar om bitmap-index — ett som Ă€r mindre vanligt Ă€n B-tree, men inte mindre intressant. Jag delar med mig av en inspelning frĂ„n föredraget pĂ„ konferensen pĂ„ engelska och en texttranskription pĂ„ ryska.

Vi kommer att titta pÄ hur bitmap-index Àr uppbyggda, nÀr de Àr bÀttre, nÀr de Àr sÀmre Àn andra index och i vilka fall de Àr betydligt snabbare; se vilka populÀra DBMS som redan har bitmap-index; försöka skriva vÄrt eget i Go. Som "dessert" kommer vi att anvÀnda fÀrdiga bibliotek för att skapa vÄr supersnabba specialiserade databas.

Jag hoppas verkligen att mitt arbete kommer att vara anvÀndbart och intressant för er. DÄ kör vi!

Introduktion

Spela video

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

Hej alla! Klockan Ă€r sex pĂ„ kvĂ€llen, vi Ă€r alla supertrötta. En utmĂ€rkt tid att prata om trĂ„kig teori kring databasindex, eller hur? Oroa er inte, jag har nĂ„gra rader kĂ€llkod hĂ€r och dĂ€r. 🙂

Om vi ska vara seriösa Àr föredraget fyllt med information och vi har inte sÄ mycket tid. LÄt oss börja.
Bitmap-index i Go: sökning i vild hastighet
Idag kommer jag att prata om följande:

  • vad index Ă€r;
  • vad ett bitmap-index Ă€r;
  • var det anvĂ€nds och var det INTE anvĂ€nds och varför;
  • en enkel implementering i Go och lite kamp med kompileraren;
  • en nĂ„got mindre enkel, men mycket mer effektiv implementering i GoAssembly;
  • "problem" med bitmap-index;
  • befintliga implementationer.

Vad Àr dÄ index?

Bitmap-index i Go: sökning i vild hastighet

Ett index Àr en separat datastruktur som vi hÄller och uppdaterar i tillÀgg till huvuddata. Den anvÀnds för att pÄskynda sökningen. Utan index skulle en sökning krÀva en fullstÀndig genomgÄng av datan (en process kallad full scan), och denna process har linjÀr algoritmisk komplexitet. Men databaser innehÄller vanligtvis enorma mÀngder data och linjÀr komplexitet Àr för lÄngsam. Idealiskt sett vill vi ha logarithmisk eller konstant.

Detta Àr ett stort komplext Àmne, fyllt med nyanser och kompromisser, men efter att ha sett pÄ Äratal av utveckling och forskningen kring olika databaser Àr jag beredd att pÄstÄ att det finns ett fÄtal allmÀnt anvÀnda tillvÀgagÄngssÀtt för att skapa databaser.

Bitmap-index i Go: sökning i vild hastighet

Den första metoden innebÀr hierarkisk minskning av sökomrÄdet, dÀr sökomrÄdet delas upp i mindre delar.

Vanligtvis gör vi detta genom att anvÀnda olika typer av trÀd. Ett exempel kan vara en stor lÄda med material i ditt skÄp, som innehÄller mindre lÄdor med material uppdelade efter olika teman. Om du behöver material kommer du förmodligen att leta efter dem i lÄdan som Àr mÀrkt "Material", och inte i den som har ordet "Kakor" pÄ sig, eller hur?

Bitmap-index i Go: sökning i vild hastighet

Den andra metoden innebÀr att direkt identifiera det önskade elementet eller en grupp av element. Vi gör detta i hash maps eller omvÀnda index. AnvÀndningen av hash maps liknar det föregÄende exemplet, men istÀllet för en lÄda med lÄdor har du en massa smÄ lÄdor med slutliga objekt i ditt skÄp.

Bitmap-index i Go: sökning i vild hastighet

Den tredje metoden Àr att eliminera behovet av sökning. Detta gör vi med hjÀlp av Bloom-filter eller cuckoo-filter. De första ger svar ögonblickligen, vilket befriar dig frÄn att behöva söka.

Bitmap-index i Go: sökning i vild hastighet

Den sista metoden handlar om att utnyttja alla resurser som modern hÄrdvara ger oss. Det Àr just vad vi gör med bitmap-index. Ja, nÀr vi anvÀnder dem mÄste vi ibland gÄ igenom hela indexet, men vi gör det supereffektivt.

Som jag redan nÀmnt Àr temat databasindex omfattande och fyllt med kompromisser. Det betyder att vi ibland kan anvÀnda flera metoder samtidigt: om vi behöver snabba upp sökningen Ànnu mer eller om vi behöver tÀcka alla möjliga typer av sökningar.

Idag kommer jag att prata om den minst kÀnda metoden bland de nÀmnda - bitmap-index.

Vem Àr jag för att prata om detta Àmne?

Bitmap-index i Go: sökning i vild hastighet

Jag arbetar som teamledare pÄ Badoo (kanske kÀnner du bÀttre till vÄr andra produkt - Bumble). Vi har redan över 400 miljoner anvÀndare vÀrlden över och mÄnga funktioner som arbetar med att matcha dem med den bÀsta partnern. Vi gör detta med hjÀlp av skrÀddarsydda tjÀnster, inklusive bitmap-index.

SÄ vad Àr ett bitmap-index?

Bitmap-index i Go: sökning i vild hastighet
Bitmap-indexar, som namnet antyder, anvÀnder bitmaps eller bitset för att implementera ett sökindex. I ett fÄgelperspektiv bestÄr detta index av en eller flera sÄdana bitmaps som representerar entiteter (som mÀnniskor) och deras egenskaper eller parametrar (Älder, ögonfÀrg etc.), samt en algoritm som anvÀnder bitoperationer (OCH, ELLER, INTE) för att svara pÄ sökfrÄgor.
Bitmap-index i Go: sökning i vild hastighet
Det sÀgs att bitmap-indexar Àr allra mest lÀmpliga och mycket effektiva för situationer dÀr sökningar kombinerar frÄgor över mÄnga kolumner med lÄg kardinalitet (tÀnk dig "ögonfÀrg" eller "civilstÄnd" mot nÄgot som "avstÄnd frÄn stadens centrum"). Men senare ska jag visa att de fungerar utmÀrkt Àven med kolumner med hög kardinalitet.

LÄt oss titta pÄ ett enkelt exempel pÄ ett bitmap-index.
Bitmap-index i Go: sökning i vild hastighet
TÀnk dig att vi har en lista över restauranger i Moskva med binÀra egenskaper som dessa:

  • nĂ€ra tunnelbanan (near metro);
  • har privat parkering (has private parking);
  • har terrass (has terrace);
  • accepterar bokningar (accepts reservations);
  • veganvĂ€nlig (vegan friendly);
  • dyr (expensive).

Bitmap-index i Go: sökning i vild hastighet
LÄt oss ge varje restaurang ett ordningsnummer som börjar pÄ 0 och avsÀtta minne för 6 bitmaps (en för varje egenskap). Sedan kommer vi att fylla dessa bitmaps beroende pÄ om restaurangen har denna egenskap eller inte. Om restaurang 4 har en terrass, kommer bit nr 4 i bitmapen "har terrass" att sÀttas till 1 (om det inte finns en terrass, sÀtts det till 0).
Bitmap-index i Go: sökning i vild hastighet
Nu har vi det enklaste bitmap-indexet som finns, och vi kan anvÀnda det för att svara pÄ frÄgor som:

  • "Visa mig restauranger som Ă€r veganvĂ€nliga";
  • "Visa mig prisvĂ€rda restauranger med terrass dĂ€r man kan boka bord."

Bitmap-index i Go: sökning i vild hastighet
Bitmap-index i Go: sökning i vild hastighet
Hur? LÄt oss titta. Den första frÄgan Àr mycket enkel. Det vi behöver göra Àr att ta bitmapen "veganvÀnlig" och omvandla den till en lista över restauranger vars bitar Àr satt.
Bitmap-index i Go: sökning i vild hastighet
Bitmap-index i Go: sökning i vild hastighet
Den andra förfrÄgan Àr lite mer komplicerad. Vi behöver anvÀnda bitoperatören NOT pÄ bitmappen "dyr", för att fÄ en lista över prisvÀrda restauranger, och sedan AND:a den med bitmappen "kan boka bord" och AND:a resultatet med bitmappen "har terrass". Den resulterande bitmappen kommer att innehÄlla en lista över stÀllen som uppfyller alla vÄra kriterier. I det hÀr exemplet Àr det endast restaurangen "Ungdom".
Bitmap-index i Go: sökning i vild hastighet
Bitmap-index i Go: sökning i vild hastighet
Det finns mycket teori hÀr, men oroa dig inte, vi kommer snart att se koden.

Var anvÀnds bitmap-index?

Bitmap-index i Go: sökning i vild hastighet
Om du "googlar" bitmap-index kommer 90% av svaren pÄ ett eller annat sÀtt att relatera till Oracle DB. Men andra databashanteringssystem stödjer sÀkert ocksÄ den hÀr fantastiska funktionen, eller hur? Inte riktigt.

LÄt oss gÄ igenom listan över huvudmisstÀnkta.
Bitmap-index i Go: sökning i vild hastighet
MySQL stöder Ànnu inte bitmap-index, men det finns ett förslag om att lÀgga till den hÀr funktionen (https://dev.mysql.com/worklog/task/?id=1524).

PostgreSQL stöder inte bitmap-index, men anvÀnder enkla bitmaps och bitoperationer för att kombinera sökresultat frÄn flera andra index.

Tarantool har bitset-index och stöder enkel sökning med dem.

Redis har enkla bitfÀlt (https://redis.io/commands/bitfield) utan möjlighet till sökning.

MongoDB stöder Ànnu inte bitmap-index, men det finns ocksÄ ett förslag om att lÀgga till denna funktion. https://jira.mongodb.org/browse/SERVER-1723

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

Bitmap-index i Go: sökning i vild hastighet

  • Men i vĂ„rt hus har vi fĂ„tt en ny granne: Pilosa. Det Ă€r en ny icke-relationell databas skriven i Go. Den innehĂ„ller endast bitmap-index och bygger allt pĂ„ dem. Vi kommer att prata om den lite senare.

Implementation i Go

Men varför anvÀnds bitmap-index sÄ sÀllan? Innan jag svarar pÄ den frÄgan skulle jag vilja visa dig en implementation av ett mycket enkelt bitmap-index i Go.
Bitmap-index i Go: sökning i vild hastighet
Bitmaps representeras i grunden som bara bitar av data. I Go lÄt oss anvÀnda byte-slices för detta.

Vi har en bitmap för en restaurangegenskap, och varje bit i bitmappen anger om en viss restaurang har den egenskapen eller inte.
Bitmap-index i Go: sökning i vild hastighet
Vi behöver tvÄ hjÀlpfunktioner. Den ena kommer att anvÀndas för att fylla vÄra bitmaps med slumpmÀssiga data. SlumpmÀssiga, men med en viss sannolikhet för att restaurangen har varje egenskap. Till exempel anser jag att det finns vÀldigt fÄ restauranger i Moskva dÀr man inte kan boka bord, och jag tror att ungefÀr 20 % av stÀllena Àr lÀmpliga för vegetarianer.

Den andra funktionen kommer att konvertera bitmappen till en lista över restauranger.
Bitmap-index i Go: sökning i vild hastighet
Bitmap-index i Go: sökning i vild hastighet
För att svara pÄ frÄgan "Visa mig billiga restauranger som har en veranda och dÀr man kan boka bord" behöver vi tvÄ bitoperationer: NOT och AND.

Vi kan förenkla vÄr kod nÄgot genom att anvÀnda en mer avancerad operation AND NOT.

Vi har funktioner för var och en av dessa operationer. BÄda gÄr igenom skivor, tar motsvarande element frÄn varje, förenar dem med en bitoperation och lÀgger resultatet i en resultatlista.
Bitmap-index i Go: sökning i vild hastighet
Och nu kan vi anvÀnda vÄra bitmaps och funktioner för att svara pÄ sökfrÄgan.
Bitmap-index i Go: sökning i vild hastighet
Prestandan Àr inte sÄ hög, trots att funktionerna Àr mycket enkla och vi sparade en del resurser genom att inte returnera en ny resultatlista vid varje funktionsanrop.

Efter att ha profilerat lite med pprof mÀrkte jag att Go-kompilatorn missade en mycket enkel men viktig optimering: funktionsinlining.
Bitmap-index i Go: sökning i vild hastighet
Problemet Àr att Go-kompilatorn Àr fruktansvÀrt rÀdd för loopar som gÄr igenom skivor och vÀgrar kategoriskt att inlinera funktioner som innehÄller sÄdana loopar.
Bitmap-index i Go: sökning i vild hastighet
Men jag Àr inte rÀdd och kan lura kompilatorn genom att anvÀnda goto istÀllet för en loop, precis som pÄ gamla goda tider.

Bitmap-index i Go: sökning i vild hastighet
Bitmap-index i Go: sökning i vild hastighet

Och som ni ser, nu inlinar kompilatorn gladeligen vÄr funktion! I slutÀndan lyckas vi spara omkring 2 mikrosekunder. Inte dumt!

Bitmap-index i Go: sökning i vild hastighet

Det andra flaskhalsen Àr inte svÄrt att se om man noggrant tittar pÄ assemblerutdata. Kompilatorn har lagt till en grÀnskontroll för skivan direkt inuti vÄr mest frekventa loop. Problemet Àr att Go Àr ett sÀkert sprÄk, och kompilatorn Àr orolig för att mina tre argument (tre skivor) har olika storlekar. Det skulle annars teoretiskt kunna leda till sÄ kallad buffertöverskridning.

LÄt oss lugna kompilatorn genom att visa att alla slice har samma storlek. Vi kan göra detta genom att lÀgga till en enkel kontroll i början av vÄr funktion.
Bitmap-index i Go: sökning i vild hastighet
NÀr kompilatorn ser detta hoppar den glatt över kontrollen och vi sparar ytterligare 500 nanosekunder.

Stora batchar

Okej, vi har lyckats pressa ut nÄgon prestanda frÄn vÄr enkla implementering, men detta resultat Àr faktiskt mycket sÀmre Àn vad som Àr möjligt med den aktuella hÄrdvaran.

Allt vi gör Àr grundlÀggande bitoperationer, och vÄra processorer utför dem mycket effektivt. Men tyvÀrr "matade" vi vÄr processor med vÀldigt smÄ jobb. VÄra funktioner utför operationer byte för byte. Vi kan enkelt finjustera vÄr kod sÄ att den arbetar med 8-byte tillskott genom att anvÀnda UInt64 slices.

Bitmap-index i Go: sökning i vild hastighet

Som ni ser har denna lilla Àndring ökat hastigheten pÄ vÄrt program Ätta gÄnger genom att öka batchstorleken Ätta gÄnger. Vinsten kan sÀgas vara linjÀr.

Bitmap-index i Go: sökning i vild hastighet

Assemblerimplementation

Bitmap-index i Go: sökning i vild hastighet
Men det Àr inte slutet. VÄra processorer kan arbeta med block pÄ 16, 32 och till och med 64 byte. SÄdana "bredare" operationer kallas single instruction multiple data (SIMD; en instruktion, flera data), och processen att omvandla koden sÄ att den anvÀnder dessa operationer kallas vektorisering.

TyvÀrr Àr Go-kompilatorn lÄngt ifrÄn en mÀstare pÄ vektorisering. För tillfÀllet Àr det enda sÀttet att vektorisera kod i Go att manuellt lÀgga in dataoperationerna med hjÀlp av Go-assembler.

Bitmap-index i Go: sökning i vild hastighet

Go-assembler Àr ett konstigt djur. Du vet förmodligen att assembler Àr nÄgot som Àr starkt kopplat till den datorarkitektur du skriver för, men sÄ Àr det inte i Go. Go-assembler liknar mer IRL (intermediate representation language) eller ett mellanliggande sprÄk: det Àr praktiskt taget plattformsoberoende. Rob Pike hade en utmÀrkt med en presentation presentation om detta för nÄgra Är sedan pÄ GopherCon i Denver.

Utöver detta anvÀnder Go ett ovanligt format, Plan 9, som skiljer sig frÄn de vedertagna formaten AT&T och Intel.
Bitmap-index i Go: sökning i vild hastighet
Man kan med sÀkerhet sÀga att det inte Àr det roligaste att skriva Go-assembler för hand.

Men lyckligtvis finns det redan tvÄ högpresterande verktyg som hjÀlper oss att skriva Go-assembler: PeachPy och avo. BÄda verktygen genererar Go-assembler frÄn mer hög nivÄ kod skriven i Python och Go respektive.
Bitmap-index i Go: sökning i vild hastighet
Dessa verktyg förenklar saker som registerallokering, skrivning av loopar och i allmÀnhet gör det enklare att komma in i vÀrlden av assemblerprogrammering i Go.

Vi kommer att anvÀnda avo, sÄ vÄra program kommer att vara nÀstan vanliga Go-program.
Bitmap-index i Go: sökning i vild hastighet
SÄ hÀr ser det enklaste exemplet pÄ ett avo-program ut. Vi har en funktion main() som definierar en funktion Add() vars syfte Àr att addera tvÄ tal. HÀr finns hjÀlpfunktioner för att hÀmta parametrar efter namn och för att fÄ ett av de lediga och lÀmpliga processorregistren. Varje processoroperation har en motsvarande funktion i avo, som kan ses i ADDQ. Och slutligen har vi en hjÀlpfunktion för att spara det resulterande vÀrdet.
Bitmap-index i Go: sökning i vild hastighet
Genom att anropa go generate kommer vi att köra programmet pÄ avo, och i slutÀndan kommer tvÄ filer att genereras:

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

Bitmap-index i Go: sökning i vild hastighet
Nu nÀr vi har sett vad och hur avo gör, lÄt oss titta pÄ vÄra funktioner. Jag har implementerat bÄde de skalÀra och vektoriserade (SIMD) versionerna av funktionerna.

LÄt oss först titta pÄ de skalÀra versionerna.
Bitmap-index i Go: sökning i vild hastighet
Som i det föregÄende exemplet ber vi om ett ledigt och korrekt allmÀnt register, vi behöver inte berÀkna förskjutningar och storlekar för argumenten. Allt detta gör avo Ät oss.
Bitmap-index i Go: sökning i vild hastighet
Tidigare anvÀnde vi etiketter och goto (eller hopp) för att öka prestandan och för att lura Go-kompilatorn, men nu gör vi detta frÄn grunden. PoÀngen Àr att loopar Àr ett högre begrepp. I assembler har vi endast etiketter och hopp.
Bitmap-index i Go: sökning i vild hastighet
Den kvarstÄende koden bör redan vara bekant och förstÄelig. Vi emulerar en loop med etiketter och hopp, tar en liten del av data frÄn vÄra tvÄ skivor, kombinerar dem med en bitoperation (AND NOT i detta fall) och lÀgger sedan resultatet i den resulterande skivan. Det Àr allt.
Bitmap-index i Go: sökning i vild hastighet
SÄ hÀr ser den slutliga koden i assembler ut. Vi behövde inte berÀkna förskjutningar och storlekar (markerade i grönt) eller hÄlla koll pÄ anvÀnda register (markerade i rött).
Bitmap-index i Go: sökning i vild hastighet
Om vi jĂ€mför prestandan för en implementation i assembler med prestandan för den bĂ€sta implementationen i Go, kommer vi att se att de Ă€r likvĂ€rdiga. Och det Ă€r förvĂ€ntat. Vi har ju inte gjort nĂ„got speciellt – vi har bara Ă„terskapat vad Go-kompilatorn skulle göra.

TyvÀrr kan vi inte fÄ kompilatorn att inlinera vÄra funktioner skrivna i assembler. Go-kompilatorn har för nÀrvarande inte den möjligheten, trots att efterfrÄgan pÄ att lÀgga till det har funnits under ganska lÄng tid.

Det Àr dÀrför det Àr omöjligt att fÄ nÄgra fördelar frÄn smÄ funktioner i assembler. Vi mÄste antingen skriva stora funktioner eller anvÀnda det nya paketet math/bits, eller sÄ mÄste vi undvika assembler helt.

LÄt oss nu titta pÄ de vektoriserade versionerna av vÄra funktioner.
Bitmap-index i Go: sökning i vild hastighet
För detta exempel har jag valt att anvÀnda AVX2, sÄ vi kommer att anvÀnda operationer som arbetar med 32-byte-block. Koden har en struktur som liknar den skalÀra varianten: lÀsa in parametrar, be om ett ledigt gemensamt register, och sÄ vidare.
Bitmap-index i Go: sökning i vild hastighet
En nyhet Àr att bredare vektoroperationer anvÀnder speciella breda register. NÀr det gÀller 32-byte-block Àr det registrets som har prefixet Y. Det Àr dÀrför du ser funktionen YMM() i koden. Om jag hade anvÀnt AVX-512 med 64-byte-block skulle prefixet vara Z.

En andra nyhet Àr att jag har valt att anvÀnda en optimering som kallas loop unrolling, vilket innebÀr att jag gör Ätta loopoperationer manuellt innan jag hoppar tillbaka till början av loopen. Denna optimering minskar antalet förgreningar i koden, och den Àr begrÀnsad av antalet tillgÀngliga register.
Bitmap-index i Go: sökning i vild hastighet
Men hur Àr det med prestanda? Den Àr fantastisk! Vi har fÄtt en acceleration pÄ cirka sju gÄnger jÀmfört med den bÀsta lösningen i Go. Imponerande, eller hur?
Bitmap-index i Go: sökning i vild hastighet
Men Àven denna implementation skulle potentiellt kunna accelereras ytterligare genom att anvÀnda AVX-512, prefetching eller JIT (just-in-time kompilator) för frÄgeplaneraren. Men det Àr definitivt ett Àmne för ett separat föredrag.

Problem med bitmap-index

Nu nÀr vi har gÄtt igenom en enkel implementation av bitmap-index i Go och en mycket mer prestandakraftig i assembler, lÄt oss Àntligen prata om varför bitmap-index sÄ sÀllan anvÀnds.
Bitmap-index i Go: sökning i vild hastighet
I Àldre vetenskapliga arbeten nÀmns tre problem med bitmap-index, men nyare studier och jag hÀvdar att dessa inte lÀngre Àr aktuella. Vi ska inte gÄ djupt in pÄ varje problem, men vi kommer att behandla dem ytligt.

Problemet med hög kardinalitet

SÄ, vi fÄr veta att bitmap-index endast Àr lÀmpliga för fÀlt med lÄg kardinalitet, det vill sÀga sÄdana som har fÄ vÀrden (till exempel kön eller ögonfÀrg), och anledningen Àr att den vanliga representationen av sÄdana fÀlt (en bit per vÀrde) vid hög kardinalitet skulle ta för mycket plats, och dessutom skulle dessa bitmap-index vara svagt (sÀllan) fyllda.
Bitmap-index i Go: sökning i vild hastighet
Bitmap-index i Go: sökning i vild hastighet
Ibland kan vi anvĂ€nda en annan representation, till exempel den standard som vi anvĂ€nder för att representera tal. Men det var verkligen introduktionen av komprimeringsalgoritmer som förĂ€ndrade allt. Under de senaste decennierna har forskare och forskare kommit pĂ„ ett stort antal komprimeringsalgoritmer för bitmaps. Deras huvudsakliga fördel Ă€r att vi inte behöver dekomprimera bitmaps för att utföra bitoperationer — vi kan utföra bitoperationer direkt pĂ„ de komprimerade bitmaps.
Bitmap-index i Go: sökning i vild hastighet
PĂ„ senare tid har ocksĂ„ hybrida metoder dykt upp, som till exempel roaring bitmaps. De anvĂ€nder samtidigt tre olika representationer för bitmaps — sjĂ€lva bitmaps, arrayer och sĂ„ kallade bit runs — och balanserar mellan dem för att maximera prestanda och minimera minnesanvĂ€ndning.

Du kan se roaring bitmaps i mÄnga populÀra applikationer. Det finns redan ett stort antal implementationer för de mest olika programmeringssprÄken, inklusive mer Àn tre implementationer för Go.
Bitmap-index i Go: sökning i vild hastighet
En annan metod som kan hjÀlpa oss att hantera hög kardinalitet kallas gruppering (binning). TÀnk dig att du har ett fÀlt som representerar en persons höjd. Höjd Àr ett flyttal, men vi mÀnniskor tÀnker inte pÄ det pÄ det sÀttet. För oss spelar det ingen roll mellan en höjd pÄ 185,2 cm och en pÄ 185,3 cm.

Detta innebÀr att vi kan gruppera liknande vÀrden i grupper inom 1 cm.

Och om vi dessutom vet att mycket fÄ mÀnniskor har en höjd lÀgre Àn 50 cm och högre Àn 250 cm, kan vi i princip omvandla ett fÀlt med oÀndlig kardinalitet till ett fÀlt med kardinalitet pÄ cirka 200 vÀrden.

SjÀlvklart, om sÄ behövs kan vi göra ytterligare filtrering efterÄt.

Problemet med hög genomströmning

NÀsta problem med bitmap-index Àr att deras uppdatering kan vara vÀldigt kostsam.

Databaser mÄste möjliggöra uppdateringar av data samtidigt som potentiellt hundratals andra förfrÄgningar söker i dessa data. Vi behöver lÄs för att undvika problem med samtidig Ätkomst till data eller andra problem med delad Ätkomst. Och dÀr det finns ett stort lÄs, finns det ett problem med lock contention, dÀr detta lÄs blir en flaskhals.
Bitmap-index i Go: sökning i vild hastighet
Detta problem kan lösas eller kringgÄs genom sharding eller anvÀndning av versionshanterade index.

Sharding Àr en enkel och vÀlkÀnd teknik. Du kan sharda bitmap-index precis som du shardar andra data. IstÀllet för ett stort lÄs fÄr du flera smÄ lÄs och pÄ sÄ sÀtt undviker du lock contention.

Ett andra sÀtt att lösa problemet Àr att anvÀnda versionshanterade index. Du kan ha en kopia av indexet som du anvÀnder för sökning eller lÀsning, och en för skrivning eller uppdatering. Och med jÀmna mellanrum (till exempel var 100 ms eller 500 ms) duplicerar du dem och byter plats. Naturligtvis Àr denna metod endast tillÀmplig nÀr din applikation kan fungera med ett nÄgot fördröjt sökindex.

Dessa tvÄ metoder kan anvÀndas samtidigt: du kan ha ett shardat versionshanterat index.

Mer komplexa frÄgor

Det sista problemet med bitmap-index Àr att de sÀgs vara dÄligt lÀmpade för mer komplexa typer av frÄgor, till exempel frÄgor "inom intervall".

Och faktiskt, om vi tÀnker efter, Àr bitoperationer som AND, OR osv. inte riktigt lÀmpliga för frÄgor som "Visa mig hotell med priser frÄn 200 till 300 dollar per natt".
Bitmap-index i Go: sökning i vild hastighet
En naiv och mycket oklok lösning skulle vara att ta resultaten för varje dollarvÀrde och kombinera dem med en bitoperation OR.
Bitmap-index i Go: sökning i vild hastighet
En nÄgot mer korrekt lösning skulle vara att anvÀnda gruppering. Till exempel, i grupper om 50 dollar. Detta skulle snabba upp vÄr process med 50 gÄnger.

Men problemet löses lika enkelt med hjÀlp av en representation som skapats speciellt för denna typ av frÄgor. Inom vetenskapliga arbeten kallas det range-encoded bitmaps.
Bitmap-index i Go: sökning i vild hastighet
I en sÄdan representation sÀtter vi inte bara en bit för ett visst vÀrde (till exempel 200), utan vi anger det vÀrdet och allt som Àr högre. 200 och högre. Samma sak gÀller för 300: 300 och högre. Och sÄ vidare.

Genom att anvÀnda denna representation kan vi svara pÄ denna typ av sökfrÄga genom att gÄ igenom indexet endast tvÄ gÄnger. Först fÄr vi en lista med hotell dÀr rummet kostar mindre Àn 300 dollar, och sedan tar vi bort de dÀr rumspriserna Àr lÀgre Àn 199 dollar. Klart.
Bitmap-index i Go: sökning i vild hastighet
Du kommer att bli förvĂ„nad, men Ă€ven geosökningar Ă€r möjliga med hjĂ€lp av bitmap-index. Tricket Ă€r att anvĂ€nda en geopresentation som omger dina koordinater med en geometrisk figur. Till exempel, S2 frĂ„n Google. Figuren mĂ„ste kunna representeras med tre eller fler skĂ€rande linjer som kan numreras. PĂ„ sĂ„ sĂ€tt kan vi omvandla vĂ„r geosökning till flera ”intervall”-frĂ„gor (via dessa numrerade linjer).

FÀrdiga lösningar

Jag hoppas att jag har vÀckt ditt intresse lite och att du nu har ett anvÀndbart verktyg i din verktygslÄda. Om du nÄgonsin behöver göra nÄgot liknande, vet du vilken vÀg du ska titta.

Men inte alla har tid, tÄlamod och resurser för att skapa bitmap-index frÄn grunden. SÀrskilt mer avancerade som anvÀnder SIMD, till exempel.

Lyckligtvis finns det flera fÀrdiga lösningar som kan hjÀlpa dig.
Bitmap-index i Go: sökning i vild hastighet

Roaring bitmaps

Först finns det det som kallas roaring bitmaps-biblioteket, som jag redan nÀmnt. Det innehÄller alla nödvÀndiga containrar och bitoperationer som du behöver för att skapa ett fullstÀndigt bitmap-index.
Bitmap-index i Go: sökning i vild hastighet
TyvÀrr anvÀnder ingen av Go-implementationerna för nÀrvarande SIMD, vilket innebÀr att Go-implementationerna Àr mindre effektiva Àn de i C, till exempel.

Pilosa

En annan produkt som kan hjÀlpa dig Àr databasen Pilosa, som i grunden bara har bitmap-index. Det Àr en relativt ny lösning, men den vinner snabbt hjÀrtan.
Bitmap-index i Go: sökning i vild hastighet
Pilosa anvÀnder roaring bitmaps internt och ger dig möjlighet att anvÀnda dem, förenklar och förklarar alla de saker jag nÀmnde ovan: gruppering, range-encoded bitmaps, begreppet fÀlt osv.

LÄt oss snabbt titta pÄ ett exempel pÄ hur Pilosa anvÀnds för att svara pÄ en frÄga som ni redan kÀnner till.
Bitmap-index i Go: sökning i vild hastighet
Exemplet liknar mycket det ni har sett tidigare. Vi skapar en klient till Pilosa-servern, skapar ett index och nödvÀndiga fÀlt, fyller sedan vÄra fÀlt med slumpmÀssiga data med sannolikheter och utför slutligen den bekanta förfrÄgan.

Efter detta anvÀnder vi NOT pÄ fÀltet "expensive", sedan korsar vi resultatet (eller AND-ar) med fÀltet "terrace" och fÀltet "reservations". Och till slut fÄr vi det slutgiltiga resultatet.
Bitmap-index i Go: sökning i vild hastighet
Jag hoppas verkligen att inom en snar framtid kommer ocksĂ„ databaser som MySQL och PostgreSQL att fĂ„ denna nya typ av index — bitmap-index.
Bitmap-index i Go: sökning i vild hastighet

Slutsats

Bitmap-index i Go: sökning i vild hastighet
Om du Ànnu inte har somnat, tack. Jag var tvungen att snabbprata om mÄnga Àmnen pÄ grund av tidsbegrÀnsningar, men jag hoppas att presentationen varit nyttig och kanske till och med motiverande.

Det Àr bra att kÀnna till bitmap-index, Àven om du just nu kanske inte behöver dem. LÄt dem vara ytterligare ett verktyg i din verktygslÄda.

Vi har sett olika trick för att öka prestandan för Go och de saker som Go-kompilatorn fortfarande inte hanterar sÀrskilt bra. Och detta Àr definitivt nÄgot varje Go-programmerare bör veta.

Det Àr allt jag ville berÀtta. Tack!

KĂ€lla: habr.com

Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar đŸ”„ Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar | ProHoster