Indeksi bitnih slik v Go: iskanje z divjo hitrostjo

Indeksi bitnih slik v Go: iskanje z divjo hitrostjo

uvod

To poročilo sem podal v angleščini na konferenci GopherCon Russia 2019 v Moskvi in ​​v ruščini na srečanju v Nižnem Novgorodu. Govorimo o bitnem indeksu - manj pogost kot B-drevo, a nič manj zanimiv. Skupna raba zapis govori na konferenci v angleščini in transkripti besedil v ruščini.

Pogledali bomo, kako deluje bitni indeks, kdaj je boljši, kdaj slabši od drugih indeksov in v katerih primerih je bistveno hitrejši od njih; Poglejmo, kateri priljubljeni DBMS že imajo bitne indekse; Poskusimo napisati svoje v Go. In "za posladek" bomo uporabili že pripravljene knjižnice za ustvarjanje lastne super hitre specializirane baze podatkov.

Resnično upam, da vam bodo moja dela koristna in zanimiva. Pojdi!

Predstavitev


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

Pozdravljeni vsi skupaj! Ura je šest zvečer in vsi smo zelo utrujeni. Odličen čas za pogovor o dolgočasni teoriji indeksa baze podatkov, kajne? Ne skrbite, tu in tam bom imel nekaj vrstic izvorne kode. 🙂

Šalo na stran, poročilo je polno informacij, časa pa nimamo veliko. Pa začnimo.
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
Danes bom govoril o naslednjem:

  • kaj so indeksi;
  • kaj je bitni indeks;
  • kje se uporablja in kje se NE uporablja in zakaj;
  • enostavna implementacija v Go in malo borbe s prevajalnikom;
  • nekoliko manj preprosta, a veliko bolj produktivna implementacija v Go assembler;
  • “težave” bitnih indeksov;
  • obstoječe izvedbe.

Kaj so torej indeksi?

Indeksi bitnih slik v Go: iskanje z divjo hitrostjo

Indeks je ločena podatkovna struktura, ki jo vzdržujemo in posodabljamo poleg glavnih podatkov. Uporablja se za pospešitev iskanja. Brez indeksov bi iskanje zahtevalo popolno pregledovanje podatkov (postopek, imenovan popolno skeniranje), ta postopek pa ima linearno algoritemsko zapletenost. Toda zbirke podatkov običajno vsebujejo ogromne količine podatkov in linearna kompleksnost je prepočasna. V idealnem primeru bi dobili logaritemsko ali konstantno.

To je zelo zapletena tema, polna razlik in kompromisov, a po pregledu desetletij razvoja in raziskav baze podatkov sem pripravljen reči, da obstaja le nekaj široko uporabljenih pristopov za ustvarjanje indeksov baze podatkov.

Indeksi bitnih slik v Go: iskanje z divjo hitrostjo

Prvi pristop je hierarhično zmanjšanje iskalnega prostora, tako da se iskalni prostor razdeli na manjše dele.

Običajno to počnemo z uporabo različnih vrst dreves. Primer bi bila velika škatla materialov v vaši omari, ki vsebuje manjše škatle materialov, razdeljenih na različne teme. Če potrebujete materiale, jih boste verjetno iskali v škatli z napisom "Materiali" in ne v škatli z napisom "Piškotki", kajne?

Indeksi bitnih slik v Go: iskanje z divjo hitrostjo

Drugi pristop je takojšnja izbira želenega elementa ali skupine elementov. To naredimo v zgoščenih zemljevidih ​​ali obratnih indeksih. Uporaba zgoščenih zemljevidov je zelo podobna prejšnjemu primeru, vendar imate namesto škatle škatel v svoji omari kup majhnih škatel končnih predmetov.

Indeksi bitnih slik v Go: iskanje z divjo hitrostjo

Tretji pristop je odprava potrebe po iskanju. To naredimo s filtri Bloom ali filtri kukavice. Prvi dajo odgovor takoj in vam prihranijo iskanje.

Indeksi bitnih slik v Go: iskanje z divjo hitrostjo

Zadnji pristop je, da v celoti izkoristimo vso moč, ki nam jo daje sodobna strojna oprema. Točno to počnemo v bitnih indeksih. Da, pri njihovi uporabi moramo včasih pregledati celoten indeks, vendar to počnemo super učinkovito.

Kot sem rekel, je tema indeksov baz podatkov obsežna in polna kompromisov. To pomeni, da včasih lahko uporabimo več pristopov hkrati: če želimo iskanje še pospešiti ali če moramo pokriti vse možne vrste iskanja.

Danes bom govoril o najmanj znanem pristopu med temi - o bitnih indeksih.

Kdo sem jaz, da govorim o tej temi?

Indeksi bitnih slik v Go: iskanje z divjo hitrostjo

Delam kot vodja ekipe pri Badoo (morda bolj poznate naš drugi izdelek, Bumble). Imamo že več kot 400 milijonov uporabnikov po vsem svetu in številne funkcije, ki izberejo najboljše ujemanje zanje. To naredimo s storitvami po meri, vključno z indeksi bitnih slik.

Kaj je torej indeks bitne slike?

Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
Indeksi bitnih slik, kot že ime pove, uporabljajo bitne slike ali bitne nize za implementacijo iskalnega indeksa. S ptičje perspektive je ta indeks sestavljen iz ene ali več takšnih bitnih slik, ki predstavljajo poljubne entitete (kot so ljudje) in njihove lastnosti ali parametre (starost, barva oči itd.), ter algoritem, ki uporablja bitne operacije (IN, ALI, NE). ), da odgovorite na iskalno poizvedbo.
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
Povedali so nam, da so bitni indeksi najprimernejši in zelo zmogljivi za primere iskanj, ki združujejo poizvedbe v številnih stolpcih z nizko kardinalnostjo (pomislite na "barvo oči" ali "zakonski stan" v primerjavi z nečim, kot je "oddaljenost od središča mesta"). Kasneje pa bom pokazal, da dobro delujejo tudi za stolpce z visoko kardinalnostjo.

Oglejmo si najpreprostejši primer indeksa bitne slike.
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
Predstavljajte si, da imamo seznam moskovskih restavracij z binarnimi lastnostmi, kot so te:

  • v bližini metroja;
  • na voljo je zasebno parkirišče;
  • obstaja veranda (ima teraso);
  • lahko rezervirate mizo (sprejema rezervacije);
  • primerno za vegetarijance (vegan friendly);
  • drago (drago).

Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
Vsaki restavraciji dajmo zaporedno številko, ki se začne z 0, in dodelimo pomnilnik za 6 bitnih slik (eno za vsako značilnost). Te bitne slike bomo nato zapolnili glede na to, ali ima restavracija to lastnost ali ne. Če ima restavracija 4 verando, bo bit št. 4 v bitni sliki »ima verando« nastavljen na 1 (če verande ni, potem na 0).
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
Zdaj imamo najpreprostejši možen indeks bitne slike in ga lahko uporabimo za odgovore na poizvedbe, kot so:

  • »Pokaži mi vegetarijance prijazne restavracije«;
  • "Pokaži mi poceni restavracije z verando, kjer lahko rezerviraš mizo."

Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
kako Gremo pogledat. Prva zahteva je zelo preprosta. Vse kar moramo storiti je, da vzamemo bitno sliko »vegetarijancem prijazno« in jo spremenimo v seznam restavracij, katerih deli so izpostavljeni.
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
Druga zahteva je nekoliko bolj zapletena. Uporabiti moramo bitno sliko NOT na »dragi« bitni sliki, da dobimo seznam poceni restavracij, nato IN to z bitno sliko »ali lahko rezerviram mizo« in IN rezultat z bitno sliko »tam je veranda«. Nastala bitna slika bo vsebovala seznam ustanov, ki izpolnjujejo vsa naša merila. V tem primeru je to samo restavracija Yunost.
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
Vključenih je veliko teorije, a ne skrbite, kodo bomo videli zelo kmalu.

Kje se uporabljajo bitni indeksi?

Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
Če googlate bitne indekse, bo 90 % odgovorov tako ali drugače povezanih z Oracle DB. Toda drugi DBMS verjetno podpirajo tudi tako kul stvar, kajne? res ne.

Pojdimo skozi seznam glavnih osumljencev.
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
MySQL še ne podpira bitnih indeksov, vendar obstaja predlog, ki predlaga dodajanje te možnosti (https://dev.mysql.com/worklog/task/?id=1524).

PostgreSQL ne podpira bitnih indeksov, ampak uporablja preproste bitne slike in bitne operacije za združevanje rezultatov iskanja v več drugih indeksih.

Tarantool ima bitset indekse in podpira preprosto iskanje po njih.

Redis ima preprosta bitna polja (https://redis.io/commands/bitfield) brez možnosti iskanja.

MongoDB še ne podpira bitnih indeksov, vendar obstaja tudi predlog, ki predlaga, da se doda ta možnost https://jira.mongodb.org/browse/SERVER-1723

Elasticsearch interno uporablja bitne slike (https://www.elastic.co/blog/frame-of-reference-and-roaring-bitmaps).

Indeksi bitnih slik v Go: iskanje z divjo hitrostjo

  • Toda v naši hiši se je pojavila nova soseda: Pilosa. To je nova nerelacijska zbirka podatkov, napisana v Go. Vsebuje le bitne indekse in vse temelji na njih. O tem bomo govorili malo kasneje.

Implementacija v Go

Toda zakaj se indeksi bitnih slik tako redko uporabljajo? Preden odgovorim na to vprašanje, bi vam rad pokazal, kako implementirati zelo preprost indeks bitne slike v Go.
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
Bitne slike so v bistvu samo kosi podatkov. V Go za to uporabimo rezine bajtov.

Imamo eno bitno sliko za eno značilnost restavracije in vsak bit v bitni sliki označuje, ali ima določena restavracija to lastnost ali ne.
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
Potrebovali bomo dve pomožni funkciji. Ena bo uporabljena za zapolnitev bitnih slik z naključnimi podatki. Naključno, vendar z določeno verjetnostjo, da ima restavracija vsak lastnino. Na primer, verjamem, da je v Moskvi zelo malo restavracij, kjer ne morete rezervirati mize, in zdi se mi, da je približno 20% obratov primernih za vegetarijance.

Druga funkcija bo bitno sliko pretvorila v seznam restavracij.
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
Za odgovor na poizvedbo »Pokaži mi poceni restavracije, ki imajo teraso in lahko rezervirajo«, potrebujemo dve bitni operaciji: NE in IN.

Našo kodo lahko nekoliko poenostavimo z uporabo bolj zapletenega operatorja IN NE.

Za vsako od teh operacij imamo funkcije. Oba gresta skozi rezine, iz vsake vzameta ustrezne elemente, jih z bitno operacijo združita in rezultat vneseta v nastalo rezino.
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
In zdaj lahko uporabimo naše bitne slike in funkcije, da odgovorimo na iskalno poizvedbo.
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
Zmogljivost ni tako visoka, čeprav so funkcije zelo preproste in smo prihranili veliko denarja, ker nismo vrnili nove nastale rezine ob vsakem klicu funkcije.

Po malem profiliranju s pprofom sem opazil, da prevajalniku Go manjka ena zelo preprosta, a zelo pomembna optimizacija: vstavljanje funkcij.
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
Dejstvo je, da se prevajalnik Go strašno boji zank, ki gredo skozi rezine, in kategorično zavrača vstavljanje funkcij, ki vsebujejo takšne zanke.
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
Vendar se ne bojim in lahko prevaram prevajalnik z uporabo goto namesto zanke, kot v dobrih starih časih.

Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo

In kot lahko vidite, bo zdaj prevajalnik z veseljem vgradil našo funkcijo! Posledično nam uspe prihraniti približno 2 mikrosekundi. Ni slabo!

Indeksi bitnih slik v Go: iskanje z divjo hitrostjo

Drugo ozko grlo je enostavno opaziti, če pozorno pogledate izhod sestavljanja. Prevajalnik je dodal preverjanje meje rezine prav znotraj naše najbolj vroče zanke. Dejstvo je, da je Go varen jezik, prevajalnik se boji, da so moji trije argumenti (tri rezine) različnih velikosti. Navsezadnje bo obstajala teoretična možnost pojava tako imenovanega prelivanja medpomnilnika.

Pomirimo prevajalnik tako, da mu pokažemo, da so vse rezine enake velikosti. To lahko storimo tako, da na začetku naše funkcije dodamo preprosto preverjanje.
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
Ko to vidi, prevajalnik veselo preskoči preverjanje in na koncu prihranimo dodatnih 500 nanosekund.

Velike zaplete

V redu, uspelo nam je iztisniti nekaj zmogljivosti iz naše preproste izvedbe, vendar je ta rezultat dejansko veliko slabši, kot je mogoče s trenutno strojno opremo.

Vse, kar počnemo, so osnovne bitne operacije, naši procesorji pa jih izvajajo zelo učinkovito. Toda na žalost svoj procesor »nahranimo« z zelo majhnimi deli. Naše funkcije izvajajo operacije na osnovi bajt za bajtom. Našo kodo lahko zelo enostavno prilagodimo za delo z 8-bajtnimi kosi z uporabo rezin UInt64.

Indeksi bitnih slik v Go: iskanje z divjo hitrostjo

Kot lahko vidite, ta majhna sprememba pospeši naš program za osemkrat s povečanjem velikosti serije za osemkrat. Dobiček lahko rečemo, da je linearen.

Indeksi bitnih slik v Go: iskanje z divjo hitrostjo

Implementacija v asemblerju

Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
A to še ni konec. Naši procesorji lahko delajo s kosi velikosti 16, 32 in celo 64 bajtov. Takšne "široke" operacije imenujemo enojni ukaz več podatkov (SIMD; eno navodilo, veliko podatkov), proces preoblikovanja kode, tako da uporablja takšne operacije, pa se imenuje vektorizacija.

Na žalost prevajalnik Go še zdaleč ni odličen pri vektorizaciji. Trenutno je edini način za vektorizacijo kode Go ta, da te operacije vzamete in izvedete ročno z uporabo Go asemblerja.

Indeksi bitnih slik v Go: iskanje z divjo hitrostjo

Go montažer je čudna zver. Verjetno veste, da je zbirni jezik nekaj, kar je močno povezano z arhitekturo računalnika, za katerega pišete, vendar v Go ni tako. Go assembler je bolj podoben IRL (intermediate representation language) ali vmesnemu jeziku: je praktično neodvisen od platforme. Rob Pike je odlično nastopil poročilo na to temo pred nekaj leti na GopherCon v Denverju.

Poleg tega Go uporablja nenavaden format Plan 9, ki se razlikuje od splošno sprejetih formatov AT&T in Intel.
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
Lahko rečemo, da ročno pisanje Go assemblerja ni najbolj zabavno.

Toda na srečo že obstajata dve orodji na visoki ravni, ki nam pomagata napisati Go assembler: PeachPy in avo. Oba pripomočka ustvarita zbirnik Go iz kode višje ravni, napisane v Pythonu oziroma Go.
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
Ti pripomočki poenostavijo stvari, kot so dodeljevanje registrov, pisanje zank in na splošno poenostavijo postopek vstopa v svet programiranja sestavljanja v Go.

Uporabljali bomo avo, tako da bodo naši programi skoraj običajni Go programi.
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
Tako je videti najenostavnejši primer programa avo. Imamo funkcijo main(), ki v sebi definira funkcijo Add(), katere pomen je seštevanje dveh števil. Tukaj so pomožne funkcije za pridobivanje parametrov po imenu in enega od brezplačnih in ustreznih registrov procesorja. Vsaka operacija procesorja ima ustrezno funkcijo na avo, kot je razvidno iz ADDQ. Na koncu vidimo pomožno funkcijo za shranjevanje nastale vrednosti.
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
S klicem go generate bomo izvedli program na avo in posledično bosta generirani dve datoteki:

  • add.s z nastalo kodo v Go assemblerju;
  • stub.go s funkcijskimi glavami za povezovanje dveh svetov: Go in asembler.

Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
Zdaj, ko smo videli, kaj avo počne in kako, si poglejmo naše funkcije. Implementiral sem tako skalarno kot vektorsko (SIMD) različico funkcij.

Najprej si poglejmo skalarne različice.
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
Kot v prejšnjem primeru zahtevamo brezplačen in veljaven register splošnega namena, ni nam treba izračunati odmikov in velikosti za argumente. avo naredi vse to namesto nas.
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
Včasih smo uporabljali oznake in goto (ali skoke) za izboljšanje zmogljivosti in pretentanje prevajalnika Go, zdaj pa to počnemo od začetka. Gre za to, da so cikli koncept višje ravni. V asemblerju imamo samo oznake in skoke.
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
Preostala koda bi morala biti že znana in razumljiva. Posnemamo zanko z oznakami in skoki, vzamemo majhen košček podatkov iz naših dveh rezin, ju združimo z bitno operacijo (IN NE v tem primeru) in nato vstavimo rezultat v nastalo rezino. Vse.
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
Tako izgleda končna koda za sestavljanje. Ni nam bilo treba izračunati odmikov in velikosti (označeno z zeleno) ali slediti uporabljenim registrom (poudarjeno z rdečo).
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
Če primerjamo zmogljivost izvedbe zbirnega jezika z zmogljivostjo najboljše izvedbe v Go, bomo videli, da je enaka. In to je pričakovano. Navsezadnje nismo naredili nič posebnega – le posnemali smo, kar bi naredil Go prevajalnik.

Na žalost ne moremo prisiliti prevajalnika, da vgradi naše funkcije, napisane v zbirnem jeziku. Prevajalnik Go trenutno nima takšne funkcije, čeprav že nekaj časa obstaja zahteva za dodajanje.

Zato je nemogoče izkoristiti majhne funkcije v zbirnem jeziku. Napisati moramo velike funkcije ali uporabiti nov paket math/bits ali zaobiti jezik asembler.

Poglejmo zdaj vektorske različice naših funkcij.
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
Za ta primer sem se odločil uporabiti AVX2, zato bomo uporabili operacije, ki delujejo na 32-bajtnih delih. Struktura kode je zelo podobna skalarni različici: nalaganje parametrov, zahtevanje brezplačnega skupnega registra itd.
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
Ena od novosti je, da širše vektorske operacije uporabljajo posebne široke registre. V primeru 32-bajtnih kosov so to registri s predpono Y. Zato v kodi vidite funkcijo YMM(). Če bi uporabljal AVX-512 s 64-bitnimi kosi, bi bila predpona Z.

Druga novost je, da sem se odločil uporabiti optimizacijo, imenovano odvijanje zanke, kar pomeni ročno izvedbo osmih operacij zanke, preden skočim na začetek zanke. Ta optimizacija zmanjša število vej v kodi in je omejena s številom razpoložljivih brezplačnih registrov.
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
No, kaj pa uspešnost? Ona je lepa! Dosegli smo približno sedemkratno pospešitev v primerjavi z najboljšo rešitvijo Go. Impresivno, kajne?
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
Toda celo to izvedbo bi lahko potencialno pospešili z uporabo AVX-512, vnaprejšnjega pridobivanja ali JIT (just-in-time compiler) za razporejevalnik poizvedb. Ampak to je vsekakor tema za ločeno poročilo.

Težave z indeksi bitnih slik

Zdaj, ko smo si že ogledali preprosto implementacijo indeksa bitne slike v Go in veliko bolj produktivno implementacijo v zbirnem jeziku, se končno pogovorimo o tem, zakaj se indeksi bitne slike tako redko uporabljajo.
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
Starejši članki omenjajo tri težave z bitnimi indeksi, novejši članki pa trdim, da niso več pomembni. Ne bomo se poglabljali v vsakega od teh problemov, ampak jih bomo pogledali površno.

Problem visoke kardinalnosti

Povedali so nam torej, da so bitni indeksi primerni samo za polja z nizko kardinalnostjo, to je tista, ki imajo malo vrednosti (na primer spol ali barva oči), razlog pa je v tem, da običajna predstavitev takih polj (ena bit na vrednost) bo v primeru visoke kardinalnosti zavzelo preveč prostora, poleg tega pa bodo ti indeksi bitne slike slabo (redko) zapolnjeni.
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
Včasih lahko uporabimo drugačno predstavitev, na primer standardno, ki jo uporabljamo za predstavitev števil. Toda pojav algoritmov za stiskanje je vse spremenil. V zadnjih desetletjih so znanstveniki in raziskovalci iznašli veliko število algoritmov za stiskanje bitnih slik. Njihova glavna prednost je, da za izvajanje bitnih operacij ni treba razpakirati bitnih slik – bitne operacije lahko izvajamo neposredno na stisnjenih bitnih slikah.
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
V zadnjem času so se začeli pojavljati hibridni pristopi, kot so rooring bitmaps. Hkrati uporabljajo tri različne predstavitve za bitne slike - same bitne slike, nize in tako imenovane bitne nize - in uravnotežijo med njimi, da povečajo zmogljivost in zmanjšajo porabo pomnilnika.

V najbolj priljubljenih aplikacijah najdete bučne bitne slike. Obstaja že ogromno izvedb za najrazličnejše programske jezike, vključno z več kot tremi izvedbami za Go.
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
Drug pristop, ki nam lahko pomaga pri soočanju z visoko kardinalnostjo, se imenuje združevanje. Predstavljajte si, da imate polje, ki predstavlja višino osebe. Višina je število s plavajočo vejico, vendar si ljudje tega ne predstavljamo tako. Pri nas ni razlike med višino 185,2 cm in 185,3 cm.

Izkazalo se je, da lahko podobne vrednosti združimo v skupine znotraj 1 cm.

In če vemo tudi, da je zelo malo ljudi nižjih od 50 cm in višjih od 250 cm, potem lahko polje z neskončno kardinalnostjo v bistvu spremenimo v polje s kardinalnostjo približno 200 vrednosti.

Seveda lahko po potrebi dodatno filtriramo naknadno.

Težava z visoko pasovno širino

Naslednja težava z indeksi bitnih slik je, da je njihovo posodabljanje lahko zelo drago.

Podatkovne zbirke morajo imeti možnost posodabljanja podatkov, medtem ko potencialno na stotine drugih poizvedb išče podatke. Ključavnice potrebujemo, da se izognemo težavam s sočasnim dostopom do podatkov ali drugim težavam pri skupni rabi. In kjer je ena velika ključavnica, se pojavi problem - spor za ključavnico, ko ta ključavnica postane ozko grlo.
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
To težavo je mogoče rešiti ali se ji izogniti z uporabo razrezov ali indeksov z različicami.

Sharding je preprosta in znana stvar. Indeks bitne slike lahko razdrobite tako kot vse druge podatke. Namesto ene velike ključavnice boste dobili kup majhnih ključavnic in se tako znebili spora glede ključavnic.

Drugi način za rešitev težave je uporaba verzioniranih indeksov. Lahko imate eno kopijo indeksa, ki jo uporabljate za iskanje ali branje, in eno, ki jo uporabljate za pisanje ali posodabljanje. In enkrat v določenem časovnem obdobju (na primer enkrat na 100 ms ali 500 ms) jih podvojiš in zamenjaš. Seveda je ta pristop uporaben samo v primerih, ko vaša aplikacija lahko obravnava rahlo zaostajajoč iskalni indeks.

Ta dva pristopa je mogoče uporabiti hkrati: lahko imate razdrobljen indeks z različicami.

Bolj zapletene poizvedbe

Zadnja težava z indeksi bitnih slik je, da nam povedo, da niso najbolj primerni za bolj zapletene vrste poizvedb, kot so poizvedbe razpona.

Dejansko, če dobro pomislite, bitne operacije, kot so IN, ALI itd., niso preveč primerne za poizvedbe tipa "Pokaži mi hotele s cenami sob od 200 do 300 dolarjev na noč."
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
Naivna in zelo nespametna rešitev bi bila vzeti rezultate za vsako vrednost v dolarjih in jih združiti z bitno operacijo ALI.
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
Nekoliko boljša rešitev bi bila uporaba združevanja. Na primer v skupinah po 50 dolarjev. To bi naš proces pospešilo za 50-krat.

Težavo pa je enostavno rešiti tudi z uporabo pogleda, ustvarjenega posebej za tovrstne zahteve. V znanstvenih člankih se imenuje bitne slike, kodirane po območju.
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
V tej predstavitvi ne nastavimo le enega bita za neko vrednost (na primer 200), ampak nastavimo to vrednost in vse višje. 200 in več. Enako za 300: 300 in več. In tako naprej.

Z uporabo te predstavitve lahko odgovorimo na tovrstno iskalno poizvedbo tako, da samo dvakrat preletimo indeks. Najprej bomo dobili seznam hotelov, kjer soba stane manj ali 300 dolarjev, nato pa bomo z njega odstranili tiste, kjer je cena sobe nižja ali 199 dolarjev. pripravljena
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
Presenečeni boste, vendar so celo geopoizvedbe možne z uporabo bitnih indeksov. Trik je v uporabi geometrijske predstavitve, ki obdaja vašo koordinato z geometrijskim likom. Na primer S2 iz Googla. Slik mora biti mogoče prikazati v obliki treh ali več sekajočih se črt, ki jih je mogoče oštevilčiti. Na ta način lahko našo geopoizvedbo spremenimo v več poizvedb "vzdolž vrzeli" (vzdolž teh oštevilčenih vrstic).

Pripravljene rešitve

Upam, da sem vas malo zanimal in imate zdaj v svojem arzenalu še eno uporabno orodje. Če boste kdaj morali narediti kaj takega, boste vedeli, kam iskati.

Vendar nimajo vsi časa, potrpljenja ali sredstev za ustvarjanje bitnih indeksov iz nič. Predvsem naprednejši, ki uporabljajo na primer SIMD.

Na srečo obstaja več že pripravljenih rešitev, ki vam lahko pomagajo.
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo

Rjoveče bitne slike

Prvič, obstaja ista knjižnica bitnih slik, o kateri sem že govoril. Vsebuje vse potrebne vsebnike in bitne operacije, ki jih boste potrebovali za izdelavo polnopravnega indeksa bitne slike.
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
Na žalost trenutno nobena od implementacij Go ne uporablja SIMD, kar pomeni, da so implementacije Go na primer manj zmogljive od implementacij C.

Pilosa

Drug izdelek, ki vam lahko pomaga, je Pilosa DBMS, ki ima dejansko samo bitne indekse. To je razmeroma nova rešitev, vendar z veliko hitrostjo osvaja srca.
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
Pilosa interno uporablja bučne bitne slike in vam daje možnost, da jih uporabljate, poenostavlja in razlaga vse stvari, o katerih sem govoril zgoraj: združevanje, bitne slike, kodirane z obsegom, koncept polja itd.

Oglejmo si na hitro primer uporabe Pilosa za odgovor na vprašanje, ki ga že poznate.
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
Primer je zelo podoben tistemu, kar ste videli prej. Ustvarimo odjemalca za strežnik Pilosa, ustvarimo indeks in potrebna polja, nato naša polja izpolnimo z naključnimi podatki z verjetnostmi in na koncu izvedemo že znano poizvedbo.

Nato uporabimo NOT na polju "expensive", nato presekamo rezultat (ali IN) s poljem "terasa" in poljem "rezervacije". In končno dobimo končni rezultat.
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
Resnično upam, da se bo ta nova vrsta indeksa v doglednem času pojavila tudi v DBMS-jih, kot sta MySQL in PostgreSQL - bitmap indeksi.
Indeksi bitnih slik v Go: iskanje z divjo hitrostjo

Zaključek

Indeksi bitnih slik v Go: iskanje z divjo hitrostjo
Če še niste zaspali, hvala. Zaradi omejenega časa sem se moral na kratko dotakniti mnogih tem, a upam, da je bil pogovor koristen in morda celo motivirajoč.

Bitne indekse je dobro poznati, tudi če jih trenutno ne potrebujete. Naj bodo še eno orodje v vaši orodjarni.

Ogledali smo si različne trike za zmogljivost za Go in stvari, ki jih prevajalnik Go še ne obvlada najbolje. Toda to je popolnoma koristno vedeti za vsakega programerja Go.

To je vse, kar sem ti hotel povedati. Hvala vam!

Vir: www.habr.com

Dodaj komentar