Bitmap-indekser i Go: søk i vill hastighet

Bitmap-indekser i Go: søk i vill hastighet

introduksjon

Jeg ga denne rapporten på engelsk på GopherCon Russia 2019-konferansen i Moskva og på russisk på et møte i Nizhny Novgorod. Vi snakker om en bitmap-indeks - mindre vanlig enn B-tre, men ikke mindre interessant. Deling ta opp taler på konferansen på engelsk og tekstutskrifter på russisk.

Vi skal se på hvordan en bitmap-indeks fungerer, når den er bedre, når den er dårligere enn andre indekser, og i hvilke tilfeller er den betydelig raskere enn dem; La oss se hvilke populære DBMS-er som allerede har punktgrafikkindekser; La oss prøve å skrive vår egen i Go. Og "til dessert" vil vi bruke ferdige biblioteker for å lage vår egen superraske spesialiserte database.

Jeg håper virkelig at verkene mine vil være nyttige og interessante for deg. Gå!

Innledning


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

Hei alle sammen! Klokken er seks om kvelden, og vi er alle kjempetrøtte. Flott tid å snakke om kjedelig databaseindeksteori, ikke sant? Ikke bekymre deg, jeg har et par linjer med kildekode her og der. 🙂

Alle vitser til side, rapporten er stappfull av informasjon, og vi har ikke mye tid. Så la oss komme i gang.
Bitmap-indekser i Go: søk i vill hastighet
I dag vil jeg snakke om følgende:

  • hva er indekser;
  • hva er en punktgrafikkindeks;
  • hvor det brukes og hvor det IKKE brukes og hvorfor;
  • enkel implementering i Go og litt slit med kompilatoren;
  • litt mindre enkel, men mye mer produktiv implementering i Go assembler;
  • "problemer" med punktgrafikkindekser;
  • eksisterende implementeringer.

Så hva er indekser?

Bitmap-indekser i Go: søk i vill hastighet

Indeksen er en egen datastruktur som vi vedlikeholder og oppdaterer i tillegg til hoveddataene. Den brukes til å fremskynde søket. Uten indekser ville søk kreve å gå gjennom dataene fullstendig (en prosess kalt full skanning), og denne prosessen har lineær algoritmisk kompleksitet. Men databaser inneholder vanligvis enorme mengder data og lineær kompleksitet er for treg. Ideelt sett ville vi fått en logaritmisk eller konstant.

Dette er et enormt komplekst emne, fylt med finesser og avveininger, men etter å ha sett på flere tiår med databaseutvikling og forskning, er jeg villig til å si at det bare er noen få mye brukte tilnærminger til å lage databaseindekser.

Bitmap-indekser i Go: søk i vill hastighet

Den første tilnærmingen er å hierarkisk redusere søkeområdet, dele søkeområdet i mindre deler.

Dette gjør vi vanligvis ved å bruke forskjellige typer trær. Et eksempel kan være en stor boks med materialer i skapet ditt som inneholder mindre esker med materialer fordelt på ulike emner. Hvis du trenger materialer, vil du sannsynligvis se etter dem i en boks som sier "Materials" i stedet for en som sier "Cookies", ikke sant?

Bitmap-indekser i Go: søk i vill hastighet

Den andre tilnærmingen er å umiddelbart velge ønsket element eller gruppe av elementer. Vi gjør dette i hash-kart eller omvendte indekser. Å bruke hash-kart er veldig lik det forrige eksempelet, men i stedet for en boks med bokser, har du en haug med små bokser med siste gjenstander i skapet ditt.

Bitmap-indekser i Go: søk i vill hastighet

Den tredje tilnærmingen er å eliminere behovet for søk. Dette gjør vi ved hjelp av Bloom-filtre eller gjøkfiltre. De første gir et svar umiddelbart, og sparer deg for å måtte søke.

Bitmap-indekser i Go: søk i vill hastighet

Den siste tilnærmingen er å utnytte all kraften som moderne maskinvare gir oss. Dette er nøyaktig hva vi gjør i bitmap-indekser. Ja, når vi bruker dem, må vi noen ganger gå gjennom hele indeksen, men vi gjør det supereffektivt.

Som jeg sa, emnet for databaseindekser er stort og fullt av kompromisser. Det betyr at vi noen ganger kan bruke flere tilnærminger samtidig: hvis vi trenger å øke hastigheten på søket enda mer, eller om vi må dekke alle mulige søketyper.

I dag vil jeg snakke om den minst kjente tilnærmingen til disse - bitmap-indekser.

Hvem er jeg til å snakke om dette emnet?

Bitmap-indekser i Go: søk i vill hastighet

Jeg jobber som teamleder hos Badoo (kanskje du er mer kjent med vårt andre produkt, Bumble). Vi har allerede mer enn 400 millioner brukere over hele verden og mange funksjoner som velger den beste matchen for dem. Vi gjør dette ved å bruke tilpassede tjenester, inkludert punktgrafikkindekser.

Så hva er en bitmap-indeks?

Bitmap-indekser i Go: søk i vill hastighet
Bitmap-indekser, som navnet antyder, bruker punktgrafikk eller bitsett for å implementere en søkeindeks. Fra et fugleperspektiv består denne indeksen av en eller flere slike punktgrafikk som representerer alle enheter (som mennesker) og deres egenskaper eller parametere (alder, øyenfarge osv.), og en algoritme som bruker bitoperasjoner (AND, OR, NOT ) for å svare på søket.
Bitmap-indekser i Go: søk i vill hastighet
Vi har blitt fortalt at punktgrafikkindekser er best egnet og svært effektive for tilfeller der det er søk som kombinerer søk på tvers av mange kolonner med lav kardinalitet (tenk "øyenfarge" eller "sivilstand" versus noe som "avstand fra sentrum"). Men jeg skal vise senere at de fungerer helt fint for kolonner med høy kardinalitet også.

La oss se på det enkleste eksemplet på en punktgrafikkindeks.
Bitmap-indekser i Go: søk i vill hastighet
Tenk deg at vi har en liste over restauranter i Moskva med binære egenskaper som disse:

  • nær metro;
  • det er privat parkering;
  • det er en veranda (har terrasse);
  • du kan reservere bord (godtar reservasjoner);
  • egnet for vegetarianere (veganvennlig);
  • dyrt (dyrt).

Bitmap-indekser i Go: søk i vill hastighet
La oss gi hver restaurant et sekvensnummer som starter fra 0 og tildele minne for 6 punktgrafikk (ett for hver karakteristikk). Vi vil deretter fylle ut disse punktgrafikkene avhengig av om restauranten har denne egenskapen eller ikke. Hvis restaurant 4 har en veranda, vil bit nr. 4 i "har en veranda" bitmap settes til 1 (hvis det ikke er noen veranda, så til 0).
Bitmap-indekser i Go: søk i vill hastighet
Nå har vi den enkleste punktgrafikkindeksen som er mulig, og vi kan bruke den til å svare på spørsmål som:

  • "Vis meg vegetarvennlige restauranter";
  • «Vis meg rimelige restauranter med veranda hvor du kan reservere bord.»

Bitmap-indekser i Go: søk i vill hastighet
Bitmap-indekser i Go: søk i vill hastighet
Hvordan? La oss ta en titt. Den første forespørselen er veldig enkel. Alt vi trenger å gjøre er å ta det "vegetarvennlige" punktgrafikkbildet og gjøre det om til en liste over restauranter hvis biter er utsatt.
Bitmap-indekser i Go: søk i vill hastighet
Bitmap-indekser i Go: søk i vill hastighet
Den andre forespørselen er litt mer komplisert. Vi må bruke NOT-bitmappen på den "dyre" bitmap-en for å få en liste over rimelige restauranter, så OG den med "kan jeg bestille bord"-bitmap og OG resultatet med "det er en veranda"-bitmap. Den resulterende bitmap vil inneholde en liste over virksomheter som oppfyller alle våre kriterier. I dette eksemplet er dette bare Yunost-restauranten.
Bitmap-indekser i Go: søk i vill hastighet
Bitmap-indekser i Go: søk i vill hastighet
Det er mye teori involvert, men ikke bekymre deg, vi får se koden veldig snart.

Hvor brukes punktgrafikkindekser?

Bitmap-indekser i Go: søk i vill hastighet
Hvis du Google bitmap-indekserer, vil 90 % av svarene være relatert til Oracle DB på en eller annen måte. Men andre DBMS-er støtter vel også en så kul ting, ikke sant? Ikke egentlig.

La oss gå gjennom listen over hovedmistenkte.
Bitmap-indekser i Go: søk i vill hastighet
MySQL støtter ennå ikke punktgrafikkindekser, men det er et forslag som foreslår å legge til dette alternativet (https://dev.mysql.com/worklog/task/?id=1524).

PostgreSQL støtter ikke punktgrafikkindekser, men bruker enkle punktgrafikk- og bitoperasjoner for å kombinere søkeresultater på tvers av flere andre indekser.

Tarantool har bitset-indekser og støtter enkle søk på dem.

Redis har enkle bitfelt (https://redis.io/commands/bitfield) uten muligheten til å søke etter dem.

MongoDB støtter ennå ikke bitmap-indekser, men det er også et forslag som foreslår at dette alternativet legges til https://jira.mongodb.org/browse/SERVER-1723

Elasticsearch bruker punktgrafikk internt (https://www.elastic.co/blog/frame-of-reference-and-roaring-bitmaps).

Bitmap-indekser i Go: søk i vill hastighet

  • Men en ny nabo har dukket opp i huset vårt: Pilosa. Dette er en ny ikke-relasjonell database skrevet i Go. Den inneholder bare punktgrafikkindekser og baserer alt på dem. Vi snakker om det litt senere.

Implementering i Go

Men hvorfor brukes punktgrafikkindekser så sjeldent? Før jeg svarer på dette spørsmålet, vil jeg gjerne vise deg hvordan du implementerer en veldig enkel punktgrafikkindeks i Go.
Bitmap-indekser i Go: søk i vill hastighet
Bitmaps er i hovedsak bare biter av data. I Go, la oss bruke byte-skiver til dette.

Vi har ett punktgrafikk for en restaurantkarakteristikk, og hver bit i punktgrafikken indikerer om en bestemt restaurant har denne egenskapen eller ikke.
Bitmap-indekser i Go: søk i vill hastighet
Vi trenger to hjelpefunksjoner. En vil bli brukt til å fylle punktgrafikkene våre med tilfeldige data. Tilfeldig, men med en viss sannsynlighet for at restauranten har hver eiendom. For eksempel tror jeg at det er svært få restauranter i Moskva hvor du ikke kan reservere bord, og det ser ut til at rundt 20 % av etablissementene er egnet for vegetarianere.

Den andre funksjonen vil konvertere punktgrafikken til en liste over restauranter.
Bitmap-indekser i Go: søk i vill hastighet
Bitmap-indekser i Go: søk i vill hastighet
For å svare på spørsmålet «Vis meg rimelige restauranter som har en uteplass og kan reservere bord», trenger vi to-bits operasjoner: NOT og AND.

Vi kan forenkle koden vår litt ved å bruke den mer komplekse OG IKKE-operatoren.

Vi har funksjoner for hver av disse operasjonene. Begge går gjennom skivene, tar de tilsvarende elementene fra hver, kombinerer dem med litt operasjon og legger resultatet inn i den resulterende skiven.
Bitmap-indekser i Go: søk i vill hastighet
Og nå kan vi bruke punktgrafikkene og funksjonene våre for å svare på søket.
Bitmap-indekser i Go: søk i vill hastighet
Ytelsen er ikke så høy, selv om funksjonene er veldig enkle og vi sparte mye penger ved å ikke returnere en ny resulterende skive hver gang funksjonen ble kalt.

Etter å ha gjort litt profilering med pprof, la jeg merke til at Go-kompilatoren manglet en veldig enkel, men veldig viktig optimalisering: funksjonsinlining.
Bitmap-indekser i Go: søk i vill hastighet
Faktum er at Go-kompilatoren er fryktelig redd for løkker som går gjennom skiver, og nekter kategorisk å legge inn funksjoner som inneholder slike løkker.
Bitmap-indekser i Go: søk i vill hastighet
Men jeg er ikke redd, og jeg kan lure kompilatoren ved å bruke goto i stedet for en loop, som i de gode gamle dager.

Bitmap-indekser i Go: søk i vill hastighet
Bitmap-indekser i Go: søk i vill hastighet

Og, som du kan se, nå vil kompilatoren gjerne legge inn funksjonen vår! Som et resultat klarer vi å spare ca. 2 mikrosekunder. Ikke verst!

Bitmap-indekser i Go: søk i vill hastighet

Den andre flaskehalsen er lett å se hvis du ser nøye på monteringsresultatet. Kompilatoren la til en skivegrensekontroll rett inne i vår hotteste loop. Faktum er at Go er et trygt språk, kompilatoren er redd for at mine tre argumenter (tre skiver) er av ulik størrelse. Tross alt, så vil det være en teoretisk mulighet for forekomsten av et såkalt bufferoverløp.

La oss berolige kompilatoren ved å vise den at alle skivene er like store. Vi kan gjøre dette ved å legge til en enkel sjekk i begynnelsen av funksjonen vår.
Bitmap-indekser i Go: søk i vill hastighet
Når du ser dette, hopper kompilatoren gladelig over sjekken, og vi ender opp med å spare ytterligere 500 nanosekunder.

Store butches

Ok, vi klarte å presse litt ytelse ut av vår enkle implementering, men dette resultatet er faktisk mye dårligere enn det som er mulig med nåværende maskinvare.

Alt vi gjør er grunnleggende bitoperasjoner, og prosessorene våre utfører dem veldig effektivt. Men dessverre "mater" vi prosessoren vår med svært små arbeidsstykker. Våre funksjoner utfører operasjoner på en byte-for-byte-basis. Vi kan veldig enkelt justere koden vår for å fungere med 8-byte biter ved å bruke UInt64-skiver.

Bitmap-indekser i Go: søk i vill hastighet

Som du kan se, satte denne lille endringen fart på programmet vårt åtte ganger ved å øke batchstørrelsen med åtte ganger. Gevinsten kan sies å være lineær.

Bitmap-indekser i Go: søk i vill hastighet

Implementering i assembler

Bitmap-indekser i Go: søk i vill hastighet
Men dette er ikke slutten. Våre prosessorer kan jobbe med biter på 16, 32 og til og med 64 byte. Slike "brede" operasjoner kalles single instruction multiple data (SIMD; én instruksjon, mange data), og prosessen med å transformere kode slik at den bruker slike operasjoner kalles vektorisering.

Dessverre er Go-kompilatoren langt fra utmerket til vektorisering. For øyeblikket er den eneste måten å vektorisere Go-kode på å ta og sette disse operasjonene manuelt ved å bruke Go assembler.

Bitmap-indekser i Go: søk i vill hastighet

Go assembler er et merkelig beist. Du vet sikkert at assemblerspråk er noe som er sterkt knyttet til arkitekturen til datamaskinen du skriver for, men det er ikke tilfelle i Go. Go assembler er mer som et IRL (intermediate representation language) eller et mellomspråk: det er praktisk talt plattformuavhengig. Rob Pike ga en utmerket prestasjon rapportere om dette emnet for flere år siden på GopherCon i Denver.

I tillegg bruker Go et uvanlig Plan 9-format, som skiller seg fra de generelt aksepterte AT&T- og Intel-formatene.
Bitmap-indekser i Go: søk i vill hastighet
Det er trygt å si at det ikke er det morsomste å skrive Go assembler for hånd.

Men heldigvis er det allerede to verktøy på høyt nivå som hjelper oss å skrive Go assembler: PeachPy og avo. Begge verktøyene genererer Go assembler fra kode på høyere nivå skrevet i henholdsvis Python og Go.
Bitmap-indekser i Go: søk i vill hastighet
Disse verktøyene forenkler ting som registerallokering, skriving av looper og forenkler generelt prosessen med å komme inn i en verden av monteringsprogrammering i Go.

Vi bruker avo, så programmene våre vil være nesten vanlige Go-programmer.
Bitmap-indekser i Go: søk i vill hastighet
Slik ser det enkleste eksempelet på et avo-program ut. Vi har en hoved()-funksjon, som i seg selv definerer Add()-funksjonen, hvis betydning er å legge til to tall. Det er hjelpefunksjoner her for å få parametere ved navn og få et av de gratis og passende prosessorregistrene. Hver prosessoroperasjon har en tilsvarende funksjon på avo, som vist i ADDQ. Til slutt ser vi en hjelpefunksjon for å lagre den resulterende verdien.
Bitmap-indekser i Go: søk i vill hastighet
Ved å kalle go generer vil vi kjøre programmet på avo og som et resultat vil to filer genereres:

  • add.s med den resulterende koden i Go assembler;
  • stub.go med funksjonshoder for å koble de to verdenene: Go og assembler.

Bitmap-indekser i Go: søk i vill hastighet
Nå som vi har sett hva avo gjør og hvordan, la oss se på funksjonene våre. Jeg implementerte både skalar- og vektorversjoner (SIMD) av funksjonene.

La oss først se på skalarversjonene.
Bitmap-indekser i Go: søk i vill hastighet
Som i forrige eksempel ber vi om et gratis og gyldig register for generell bruk, vi trenger ikke å beregne forskyvninger og størrelser for argumentene. avo gjør alt dette for oss.
Bitmap-indekser i Go: søk i vill hastighet
Vi pleide å bruke etiketter og goto (eller hopp) for å forbedre ytelsen og lure Go-kompilatoren, men nå gjør vi det fra starten av. Poenget er at sykluser er et konsept på høyere nivå. I assembler har vi kun etiketter og hopp.
Bitmap-indekser i Go: søk i vill hastighet
Den gjenværende koden skal allerede være kjent og forståelig. Vi emulerer en løkke med etiketter og hopp, tar et lite stykke data fra våre to skiver, kombinerer dem med en bitoperasjon (OG IKKE i dette tilfellet) og legger deretter resultatet inn i den resulterende skiven. Alle.
Bitmap-indekser i Go: søk i vill hastighet
Slik ser den endelige assembler-koden ut. Vi trengte ikke å beregne forskyvninger og størrelser (uthevet i grønt) eller holde styr på registrene som ble brukt (uthevet i rødt).
Bitmap-indekser i Go: søk i vill hastighet
Hvis vi sammenligner ytelsen til assembly-språkimplementeringen med ytelsen til den beste implementeringen i Go, vil vi se at den er den samme. Og dette er forventet. Vi gjorde tross alt ikke noe spesielt - vi gjengav bare hva en Go-kompilator ville gjøre.

Dessverre kan vi ikke tvinge kompilatoren til å legge inn funksjonene våre skrevet på assemblerspråk. Go-kompilatoren har foreløpig ikke en slik funksjon, selv om det har vært en forespørsel om å legge den til i en stund.

Dette er grunnen til at det er umulig å få noen fordel av små funksjoner i assemblerspråk. Vi må enten skrive store funksjoner, eller bruke den nye math/bits-pakken, eller omgå assembler-språket.

La oss nå se på vektorversjonene av funksjonene våre.
Bitmap-indekser i Go: søk i vill hastighet
For dette eksemplet bestemte jeg meg for å bruke AVX2, så vi vil bruke operasjoner som opererer på 32-byte biter. Strukturen til koden er veldig lik den skalære versjonen: lasting av parametere, be om et gratis delt register, etc.
Bitmap-indekser i Go: søk i vill hastighet
En nyvinning er at bredere vektoroperasjoner bruker spesielle brede registre. Når det gjelder 32-byte biter, er dette registre prefikset med Y. Dette er grunnen til at du ser YMM()-funksjonen i koden. Hvis jeg brukte AVX-512 med 64-bits biter, ville prefikset vært Z.

Den andre innovasjonen er at jeg bestemte meg for å bruke en optimalisering kalt loop unrolling, som betyr å gjøre åtte loopoperasjoner manuelt før jeg hopper til begynnelsen av loopen. Denne optimaliseringen reduserer antall grener i koden, og er begrenset av antall tilgjengelige gratisregistre.
Bitmap-indekser i Go: søk i vill hastighet
Vel, hva med ytelse? Hun er vakker! Vi oppnådde en speedup på omtrent syv ganger sammenlignet med den beste Go-løsningen. Imponerende, ikke sant?
Bitmap-indekser i Go: søk i vill hastighet
Men selv denne implementeringen kan potensielt akselereres ved å bruke AVX-512, forhåndshenting eller en JIT (just-in-time kompilator) for spørringsplanleggeren. Men dette er absolutt et tema for en egen rapport.

Problemer med punktgrafikkindekser

Nå som vi allerede har sett på en enkel implementering av en bitmap-indeks i Go og en mye mer produktiv en i assembly-språk, la oss endelig snakke om hvorfor bitmap-indekser brukes så sjelden.
Bitmap-indekser i Go: søk i vill hastighet
Eldre artikler nevner tre problemer med punktgrafikkindekser, men nyere artikler og jeg argumenterer for at de ikke lenger er relevante. Vi skal ikke dykke dypt inn i hvert av disse problemene, men se på dem overfladisk.

Problemet med høy kardinalitet

Så vi blir fortalt at punktgrafikkindekser bare er egnet for felt med lav kardinalitet, det vil si de som har få verdier (for eksempel kjønn eller øyenfarge), og grunnen er at den vanlige representasjonen av slike felt (ett bit per verdi) i tilfelle av høy kardinalitet, vil det ta for mye plass, og dessuten vil disse punktgrafikkindeksene være dårlig (sjelden) fylt.
Bitmap-indekser i Go: søk i vill hastighet
Bitmap-indekser i Go: søk i vill hastighet
Noen ganger kan vi bruke en annen representasjon, for eksempel standarden vi bruker for å representere tall. Men det var bruken av kompresjonsalgoritmer som endret alt. I løpet av de siste tiårene har forskere og forskere kommet opp med et stort antall komprimeringsalgoritmer for punktgrafikk. Deres største fordel er at det ikke er nødvendig å dekomprimere bitmaps for å utføre bitoperasjoner - vi kan utføre bitoperasjoner direkte på komprimerte bitmaps.
Bitmap-indekser i Go: søk i vill hastighet
Nylig har hybride tilnærminger begynt å dukke opp, for eksempel brølende punktgrafikk. De bruker samtidig tre forskjellige representasjoner for bitmaps - selve bitmaps, arrays og såkalte bit-runs - og balanserer mellom dem for å maksimere ytelsen og minimere minneforbruket.

Du kan finne brølende punktgrafikk i de mest populære applikasjonene. Det er allerede et stort antall implementeringer for et bredt utvalg programmeringsspråk, inkludert mer enn tre implementeringer for Go.
Bitmap-indekser i Go: søk i vill hastighet
En annen tilnærming som kan hjelpe oss med å håndtere høy kardinalitet kalles binning. Tenk deg at du har et felt som representerer en persons høyde. Høyde er et flyttall, men vi mennesker tenker ikke på det på den måten. For oss er det ingen forskjell mellom høyde 185,2 cm og 185,3 cm.

Det viser seg at vi kan gruppere lignende verdier i grupper innen 1 cm.

Og hvis vi også vet at svært få mennesker er kortere enn 50 cm og høyere enn 250 cm, så kan vi i hovedsak gjøre et felt med uendelig kardinalitet om til et felt med en kardinalitet på omtrent 200 verdier.

Om nødvendig kan vi selvfølgelig gjøre ytterligere filtrering i etterkant.

Problem med høy båndbredde

Det neste problemet med punktgrafikkindekser er at det kan være veldig dyrt å oppdatere dem.

Databaser må kunne oppdatere data mens potensielt hundrevis av andre søk søker etter dataene. Vi trenger låser for å unngå problemer med samtidig datatilgang eller andre delingsproblemer. Og der det er én stor lås, er det et problem - låsestrid, når denne låsen blir en flaskehals.
Bitmap-indekser i Go: søk i vill hastighet
Dette problemet kan løses eller omgås ved å bruke sharding eller bruke versjonerte indekser.

Sharding er en enkel og velkjent ting. Du kan dele en punktgrafikkindeks på samme måte som andre data. I stedet for én stor lås, vil du få en haug med små låser og dermed bli kvitt låsestriden.

Den andre måten å løse problemet på er å bruke versjonerte indekser. Du kan ha én kopi av indeksen som du bruker til å søke eller lese, og en som du bruker til å skrive eller oppdatere. Og en gang i en viss tidsperiode (for eksempel en gang hver 100 ms eller 500 ms) dupliserer du dem og bytter dem. Selvfølgelig er denne tilnærmingen bare aktuelt i tilfeller der søknaden din kan håndtere en litt hengende søkeindeks.

Disse to tilnærmingene kan brukes samtidig: du kan ha en sharded versjonert indeks.

Mer komplekse spørsmål

Det siste problemet med punktgrafikkindekser er at vi blir fortalt at de ikke er godt egnet for mer komplekse typer søk, for eksempel span-søk.

Faktisk, hvis du tenker på det, er bitoperasjoner som AND, OR, etc. ikke særlig egnet for spørsmål a la "Vis meg hoteller med rompriser fra 200 til 300 dollar per natt."
Bitmap-indekser i Go: søk i vill hastighet
En naiv og veldig uklokt løsning ville være å ta resultatene for hver dollarverdi og kombinere dem med en bitvis ELLER-operasjon.
Bitmap-indekser i Go: søk i vill hastighet
En litt bedre løsning ville være å bruke gruppering. For eksempel i grupper på 50 dollar. Dette vil fremskynde prosessen vår med 50 ganger.

Men problemet løses også enkelt ved å bruke en visning laget spesielt for denne typen forespørsel. I vitenskapelige artikler kalles det områdekodede punktgrafikk.
Bitmap-indekser i Go: søk i vill hastighet
I denne representasjonen setter vi ikke bare en bit for en verdi (for eksempel 200), men setter denne verdien og alt høyere. 200 og oppover. Samme for 300:300 og over. Og så videre.

Ved å bruke denne representasjonen kan vi svare på denne typen søk ved å gå gjennom indeksen bare to ganger. Først vil vi få en liste over hoteller der rommet koster mindre eller $300, og deretter vil vi fjerne de der rommet koster mindre eller $199. Klar.
Bitmap-indekser i Go: søk i vill hastighet
Du vil bli overrasket, men til og med geospørringer er mulig ved å bruke punktgrafikkindekser. Trikset er å bruke en geometrisk representasjon som omgir koordinaten din med en geometrisk figur. For eksempel S2 fra Google. Figuren skal være mulig å representere i form av tre eller flere kryssende linjer som kan nummereres. På denne måten kan vi gjøre geospørringen om til flere spørringer "langs gapet" (langs disse nummererte linjene).

Klar løsninger

Jeg håper jeg interesserte deg litt, og at du nå har et annet nyttig verktøy i arsenalet ditt. Hvis du noen gang trenger å gjøre noe slikt, vet du hvilken vei du skal se.

Imidlertid har ikke alle tid, tålmodighet eller ressurser til å lage punktgrafikkindekser fra bunnen av. Spesielt mer avanserte, som bruker SIMD, for eksempel.

Heldigvis finnes det flere ferdige løsninger som kan hjelpe deg.
Bitmap-indekser i Go: søk i vill hastighet

Brølende punktgrafikk

For det første er det det samme brølende punktgrafikkbiblioteket som jeg allerede har snakket om. Den inneholder alle nødvendige beholdere og bitoperasjoner som du trenger for å lage en fullverdig punktgrafikkindeks.
Bitmap-indekser i Go: søk i vill hastighet
Dessverre er det for øyeblikket ingen av Go-implementeringene som bruker SIMD, noe som betyr at Go-implementeringer er mindre effektive enn C-implementeringer, for eksempel.

hår

Et annet produkt som kan hjelpe deg er Pilosa DBMS, som faktisk bare har punktgrafikkindekser. Dette er en relativt ny løsning, men den vinner hjerter i stor fart.
Bitmap-indekser i Go: søk i vill hastighet
Pilosa bruker brølende punktgrafikk internt og gir deg muligheten til å bruke dem, forenkler og forklarer alle tingene jeg snakket om ovenfor: gruppering, områdekodede punktgrafikk, konseptet med et felt, etc.

La oss ta en rask titt på et eksempel på bruk av Pilosa for å svare på et spørsmål du allerede er kjent med.
Bitmap-indekser i Go: søk i vill hastighet
Eksemplet er veldig likt det du så før. Vi oppretter en klient til Pilosa-serveren, lager en indeks og de nødvendige feltene, fyller deretter feltene våre med tilfeldige data med sannsynligheter og til slutt utfører vi den kjente spørringen.

Etter det bruker vi IKKE på "dyrt"-feltet, deretter krysser vi resultatet (eller OG det) med "terrasse"-feltet og med "reservasjoner"-feltet. Og til slutt får vi det endelige resultatet.
Bitmap-indekser i Go: søk i vill hastighet
Jeg håper virkelig at denne nye typen indekser i overskuelig fremtid også vil dukke opp i DBMS-er som MySQL og PostgreSQL - bitmap-indekser.
Bitmap-indekser i Go: søk i vill hastighet

Konklusjon

Bitmap-indekser i Go: søk i vill hastighet
Hvis du ikke har sovnet ennå, takk. Jeg måtte kort berøre mange emner på grunn av begrenset tid, men jeg håper foredraget var nyttig og kanskje til og med motiverende.

Bitmap-indekser er gode å vite om, selv om du ikke trenger dem akkurat nå. La dem være et annet verktøy i verktøykassen din.

Vi har sett på ulike ytelsestriks for Go og ting som Go-kompilatoren ikke takler særlig godt ennå. Men dette er absolutt nyttig for enhver Go-programmerer å vite.

Det var alt jeg ville fortelle deg. Takk skal du ha!

Kilde: www.habr.com

Legg til en kommentar