Bittérképes indexek a Go-ban: vad sebességű keresés

Bittérképes indexek a Go-ban: vad sebességű keresés

nyitóbeszéd

Ezt a beszámolót angolul a moszkvai GopherCon Russia 2019 konferencián, oroszul pedig egy Nyizsnyij Novgorodban tartott találkozón tartottam. Bittérképes indexről beszélünk - kevésbé gyakori, mint a B-fa, de nem kevésbé érdekes. Megosztás rekord a konferencián elhangzott beszédek angol nyelven és szöveges átiratok orosz nyelven.

Megnézzük, hogyan működik a bitmap index, mikor jobb, mikor rosszabb, mint más indexek, és milyen esetekben lényegesen gyorsabb náluk; Nézzük meg, mely népszerű DBMS-ek rendelkeznek már bittérképes indexekkel; Próbáljuk meg megírni a sajátunkat a Go-ban. Desszertként pedig kész könyvtárakat fogunk használni saját szupergyors speciális adatbázisunk létrehozásához.

Nagyon remélem, hogy munkáim hasznosak és érdekesek lesznek számodra. Megy!

Bevezetés


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

Sziasztok! Este hat óra, és mindannyian nagyon fáradtak vagyunk. Jó alkalom beszélni az unalmas adatbázis-index elméletről, igaz? Ne aggódjon, itt-ott lesz pár soros forráskódom. 🙂

Minden viccet félretéve, a jelentés tele van információval, és nincs sok időnk. Tehát kezdjük.
Bittérképes indexek a Go-ban: vad sebességű keresés
Ma a következőkről fogok beszélni:

  • mik az indexek;
  • mi az a bitmap index;
  • hol használják és hol NEM, és miért;
  • egyszerű megvalósítás a Go-ban és egy kis küzdelem a fordítóval;
  • valamivel kevésbé egyszerű, de sokkal produktívabb megvalósítás a Go assemblerben;
  • bitmap indexek „problémái”;
  • meglévő megvalósítások.

Tehát mik azok az indexek?

Bittérképes indexek a Go-ban: vad sebességű keresés

Az index egy különálló adatstruktúra, amelyet a fő adatok mellett karbantartunk és frissítünk. A keresés felgyorsítására szolgál. Indexek nélkül a kereséshez teljesen át kellene menni az adatokon (a teljes vizsgálatnak nevezett folyamat), és ez a folyamat lineáris algoritmussal rendelkezik. Az adatbázisok azonban általában hatalmas mennyiségű adatot tartalmaznak, és a lineáris bonyolultság túl lassú. Ideális esetben logaritmikust vagy konstanst kapunk.

Ez egy rendkívül összetett téma, tele van finomságokkal és kompromisszumokkal, de több évtizedes adatbázisfejlesztés és kutatás után hajlandó vagyok kijelenteni, hogy csak néhány széles körben használt megközelítés létezik az adatbázis-indexek létrehozására.

Bittérképes indexek a Go-ban: vad sebességű keresés

Az első megközelítés a keresési terület hierarchikus csökkentése, a keresési terület kisebb részekre osztása.

Ezt általában különböző típusú fákkal tesszük. Példa erre egy nagy doboz anyag a szekrényében, amely kisebb dobozokat tartalmaz különböző témákra osztva. Ha anyagokra van szüksége, valószínűleg az "Anyagok" feliratú dobozban keresi őket, nem pedig a "Cookie-k" feliratú dobozban, igaz?

Bittérképes indexek a Go-ban: vad sebességű keresés

A második megközelítés a kívánt elem vagy elemcsoport azonnali kiválasztása. Ezt hash térképekben vagy fordított indexekben tesszük. A hash-térképek használata nagyon hasonlít az előző példához, de egy doboz helyett egy csomó kis doboz utolsó cikk van a szekrényében.

Bittérképes indexek a Go-ban: vad sebességű keresés

A harmadik megközelítés a keresés szükségességének megszüntetése. Ezt Bloom szűrőkkel vagy kakukkszűrőkkel tesszük. Az elsők azonnal választ adnak, megkímélve Önt a kereséstől.

Bittérképes indexek a Go-ban: vad sebességű keresés

Az utolsó megközelítés az, hogy teljes mértékben kihasználjuk a modern hardver által biztosított erőt. Pontosan ezt tesszük a bittérképes indexekben. Igen, használatukkor néha át kell mennünk a teljes indexen, de ezt rendkívül hatékonyan tesszük.

Mint mondtam, az adatbázis-indexek témája hatalmas és tele van kompromisszumokkal. Ez azt jelenti, hogy néha több megközelítést is alkalmazhatunk egyszerre: ha még jobban fel kell gyorsítanunk a keresést, vagy ha minden lehetséges keresési típust le kell fednünk.

Ma ezek közül a legkevésbé ismert megközelítésről – a bittérképes indexekről – fogok beszélni.

Ki vagyok én, hogy beszéljek erről a témáról?

Bittérképes indexek a Go-ban: vad sebességű keresés

Csapatvezetőként dolgozom a Badoo-nál (talán Ön jobban ismeri másik termékünket, a Bumble-t). Már több mint 400 millió felhasználónk van szerte a világon, és számos funkciónk választja ki a számukra legmegfelelőbbet. Ezt egyedi szolgáltatásokkal tesszük, beleértve a bittérképes indexeket is.

Tehát mi az a bittérképes index?

Bittérképes indexek a Go-ban: vad sebességű keresés
A bittérképes indexek, ahogy a neve is sugallja, bittérképeket vagy bitkészleteket használnak a keresési index megvalósításához. Madártávlatból ez az index egy vagy több ilyen bittérképből áll, amelyek bármilyen entitást (például embereket) és azok tulajdonságait vagy paramétereit (életkor, szemszín stb.) reprezentálják, valamint egy bitműveleteket használó algoritmust (ÉS, VAGY, NEM). ) a keresési lekérdezés megválaszolásához.
Bittérképes indexek a Go-ban: vad sebességű keresés
Azt mondták nekünk, hogy a bittérképes indexek a legalkalmasabbak és nagyon hatékonyak azokban az esetekben, amikor vannak olyan keresések, amelyek sok alacsony kardinalitású oszlopban egyesítik a lekérdezéseket (gondoljunk a „szemszínre” vagy „családi állapotra” szemben a „városközponttól való távolság”-ra). De később megmutatom, hogy jól működnek a nagy számosságú oszlopoknál is.

Nézzük a bittérképes index legegyszerűbb példáját.
Bittérképes indexek a Go-ban: vad sebességű keresés
Képzelje el, hogy van egy listánk a moszkvai éttermekről, amelyek bináris tulajdonságokkal rendelkeznek, mint például:

  • metró közelében;
  • van saját parkoló;
  • van veranda (terasszal);
  • asztalt foglalhat (foglalásokat elfogad);
  • alkalmas vegetáriánusok számára (vegánbarát);
  • drága (drága).

Bittérképes indexek a Go-ban: vad sebességű keresés
Adjunk minden étteremnek egy 0-tól kezdődő sorszámot, és foglaljunk le memóriát 6 bittérképhez (minden jellemzőhöz egy). Ezután feltöltjük ezeket a bitképeket attól függően, hogy az étterem rendelkezik-e ezzel a tulajdonsággal vagy sem. Ha a 4. étteremnek van veranda, akkor a „Van veranda” bitkép 4. bitje 1-re lesz állítva (ha nincs veranda, akkor 0-ra).
Bittérképes indexek a Go-ban: vad sebességű keresés
Most megvan a lehető legegyszerűbb bittérképes index, és használhatjuk olyan kérdések megválaszolására, mint például:

  • „Mutass vegetáriánusbarát éttermeket”;
  • "Mutasson olcsó éttermeket verandával, ahol asztalt foglalhat."

Bittérképes indexek a Go-ban: vad sebességű keresés
Bittérképes indexek a Go-ban: vad sebességű keresés
Hogyan? Nézzük meg. Az első kérés nagyon egyszerű. Nem kell mást tennünk, mint elővenni a „vegetáriánusbarát” bittérképet, és át kell alakítanunk azon éttermek listáját, amelyeknek a részei láthatók.
Bittérképes indexek a Go-ban: vad sebességű keresés
Bittérképes indexek a Go-ban: vad sebességű keresés
A második kérés egy kicsit bonyolultabb. A NOT bitmap-et kell használnunk a „drága” bittérképen, hogy megkapjuk az olcsó éttermek listáját, majd az ÉS a „Foglalhatok asztalt” bittérképpel, az ÉS az eredmény a „Van egy veranda” bittérképpel. Az eredményül kapott bittérkép azon létesítmények listáját fogja tartalmazni, amelyek minden kritériumunknak megfelelnek. Ebben a példában ez csak a Yunost étterem.
Bittérképes indexek a Go-ban: vad sebességű keresés
Bittérképes indexek a Go-ban: vad sebességű keresés
Sok elmélet van benne, de ne aggódjon, hamarosan látni fogjuk a kódot.

Hol használják a bittérképes indexeket?

Bittérképes indexek a Go-ban: vad sebességű keresés
Ha a Google bittérképes indexeket használja, a válaszok 90%-a ilyen vagy olyan módon kapcsolódik az Oracle DB-hez. De valószínűleg más DBMS-ek is támogatnak egy ilyen klassz dolgot, igaz? Nem igazán.

Nézzük át a fő gyanúsítottak listáját.
Bittérképes indexek a Go-ban: vad sebességű keresés
A MySQL még nem támogatja a bittérképes indexeket, de van egy javaslat, amely javasolja ennek az opciónak a hozzáadását (https://dev.mysql.com/worklog/task/?id=1524).

A PostgreSQL nem támogatja a bittérképes indexeket, de egyszerű bittérképeket és bitműveleteket használ a keresési eredmények több más indexben történő kombinálására.

A Tarantool bitkészlet indexekkel rendelkezik, és támogatja az egyszerű kereséseket.

A Redisnek egyszerű bitmezői vannak (https://redis.io/commands/bitfield) anélkül, hogy képes lenne rájuk keresni.

A MongoDB még nem támogatja a bittérképes indexeket, de van egy javaslat is, amely javasolja ennek a lehetőségnek a hozzáadását https://jira.mongodb.org/browse/SERVER-1723

Az Elasticsearch belsőleg bittérképeket használ (https://www.elastic.co/blog/frame-of-reference-and-roaring-bitmaps).

Bittérképes indexek a Go-ban: vad sebességű keresés

  • De egy új szomszéd jelent meg a házunkban: Pilosa. Ez egy új, nem relációs adatbázis Go nyelven írva. Csak bittérképes indexeket tartalmaz, és azokra alapoz mindent. Kicsit később beszélünk róla.

Megvalósítás a Go-ban

De miért olyan ritkán használják a bitmap indexeket? Mielőtt válaszolnék erre a kérdésre, szeretném megmutatni, hogyan valósíthat meg egy nagyon egyszerű bitmap indexet a Go-ban.
Bittérképes indexek a Go-ban: vad sebességű keresés
A bittérképek lényegében csak adatdarabok. A Go-ban használjunk bájtszeleteket erre.

Egy étterem jellemzőre egy bittérképünk van, és a bittérkép minden egyes bitje jelzi, hogy egy adott étterem rendelkezik-e ezzel a tulajdonsággal vagy sem.
Bittérképes indexek a Go-ban: vad sebességű keresés
Két segédfunkcióra lesz szükségünk. Az egyiket arra használjuk, hogy a bittérképeinket véletlenszerű adatokkal töltsük meg. Véletlenszerű, de bizonyos valószínűséggel az étterem minden tulajdonsággal rendelkezik. Például úgy gondolom, hogy Moszkvában nagyon kevés olyan étterem van, ahol nem lehet asztalt foglalni, és számomra úgy tűnik, hogy az éttermek körülbelül 20%-a vegetáriánusok számára alkalmas.

A második funkció a bittérképet éttermek listájává alakítja.
Bittérképes indexek a Go-ban: vad sebességű keresés
Bittérképes indexek a Go-ban: vad sebességű keresés
A „Mutasson olcsó éttermeket, amelyek terasszal rendelkeznek és le tudnak foglalni” kérdés megválaszolásához két bites műveletre van szükség: NOT és AND.

A bonyolultabb AND NOT operátor használatával kicsit leegyszerűsíthetjük a kódunkat.

Mindegyik művelethez megvannak a függvényeink. Mindkettő átmegy a szeleteken, mindegyikből kiveszi a megfelelő elemeket, egy bitművelettel kombinálja és az eredményt belerakja a kapott szeletbe.
Bittérképes indexek a Go-ban: vad sebességű keresés
És most már használhatjuk bittérképeinket és függvényeinket a keresési lekérdezések megválaszolásához.
Bittérképes indexek a Go-ban: vad sebességű keresés
A teljesítmény nem olyan magas, annak ellenére, hogy a függvények nagyon egyszerűek, és sok pénzt spóroltunk meg azzal, hogy nem adtunk vissza új eredményt a függvény minden egyes meghívásakor.

Miután elvégeztem egy kis profilozást a pprof-fel, észrevettem, hogy a Go fordítóból hiányzik egy nagyon egyszerű, de nagyon fontos optimalizálás: a függvénybeillesztés.
Bittérképes indexek a Go-ban: vad sebességű keresés
A helyzet az, hogy a Go fordító rettenetesen fél a szeleteken átmenő ciklusoktól, és kategorikusan elutasítja az ilyen ciklusokat tartalmazó függvények beágyazását.
Bittérképes indexek a Go-ban: vad sebességű keresés
De nem félek, és be tudom csapni a fordítót, ha ciklus helyett goto-t használok, mint a régi szép időkben.

Bittérképes indexek a Go-ban: vad sebességű keresés
Bittérképes indexek a Go-ban: vad sebességű keresés

És amint látja, most a fordító boldogan beépíti funkciónkat! Ennek eredményeként körülbelül 2 mikroszekundumot sikerül megtakarítanunk. Nem rossz!

Bittérképes indexek a Go-ban: vad sebességű keresés

A második szűk keresztmetszet könnyen belátható, ha alaposan megnézzük az összeállítás kimenetét. A fordító hozzáadott egy szelethatár-ellenőrzést a legforróbb ciklusunkhoz. Az tény, hogy a Go biztonságos nyelv, a fordító attól tart, hogy a három argumentum (három szelet) különböző méretű. Hiszen ekkor elméletileg fennáll az úgynevezett puffertúlcsordulás előfordulásának lehetősége.

Nyugtassuk meg a fordítót azzal, hogy megmutatjuk, hogy minden szelet egyforma méretű. Ezt úgy tehetjük meg, hogy a függvényünk elején egy egyszerű ellenőrzést adunk hozzá.
Bittérképes indexek a Go-ban: vad sebességű keresés
Ezt látva a fordító boldogan kihagyja az ellenőrzést, és a végén még 500 nanoszekundumot spórolunk meg.

Nagy csapások

Oké, sikerült némi teljesítményt kicsikarnunk az egyszerű megvalósításunkból, de ez az eredmény valójában sokkal rosszabb, mint a jelenlegi hardverrel lehetséges.

Csupán alapvető bitműveleteket végzünk, és processzoraink nagyon hatékonyan hajtják végre azokat. De sajnos nagyon kis munkával „etetjük” a processzorunkat. Funkcióink byte-byte alapon hajtják végre a műveleteket. Az UInt8 szeletekkel nagyon egyszerűen módosíthatjuk a kódunkat, hogy működjön a 64 bájtos darabokkal.

Bittérképes indexek a Go-ban: vad sebességű keresés

Amint látja, ez a kis változtatás nyolcszorosára gyorsította fel programunkat azáltal, hogy nyolcszorosára növelte a kötegméretet. A nyereség lineárisnak mondható.

Bittérképes indexek a Go-ban: vad sebességű keresés

Megvalósítás assemblerben

Bittérképes indexek a Go-ban: vad sebességű keresés
De ez még nem a vége. Processzoraink 16, 32 és akár 64 bájtos darabokkal is tudnak dolgozni. Az ilyen „tág” műveleteket egyetlen utasítás többszörös adatnak (SIMD; egy utasítás, sok adat) nevezik, és a kód átalakításának folyamatát, hogy az ilyen műveleteket használjon, vektorizálásnak nevezzük.

Sajnos a Go fordító távolról sem kiváló a vektorizálásban. Jelenleg a Go kód vektorizálásának egyetlen módja az, hogy ezeket a műveleteket manuálisan, a Go assembler segítségével végezzük el.

Bittérképes indexek a Go-ban: vad sebességű keresés

A Go assembler egy furcsa vadállat. Valószínűleg tudja, hogy az assembly nyelv olyan dolog, amely erősen kötődik annak a számítógépnek az architektúrájához, amelyre ír, de ez nem így van a Go esetében. A Go assembler inkább egy IRL-hez (intermediate representation language) vagy intermediate nyelvhez hasonlít: gyakorlatilag platformfüggetlen. Rob Pike kiváló teljesítményt nyújtott jelentés erről a témáról néhány évvel ezelőtt a denveri GopherConon.

Ezenkívül a Go egy szokatlan Plan 9 formátumot használ, amely eltér az általánosan elfogadott AT&T és Intel formátumoktól.
Bittérképes indexek a Go-ban: vad sebességű keresés
Nyugodtan kijelenthetjük, hogy a Go assemblert kézzel írni nem a legszórakoztatóbb.

De szerencsére már van két magas szintű eszköz, amely segít a Go assembler megírásában: PeachPy és avo. Mindkét segédprogram Go assemblert állít elő a Pythonban, illetve a Goban írt magasabb szintű kódból.
Bittérképes indexek a Go-ban: vad sebességű keresés
Ezek a segédprogramok leegyszerűsítik az olyan dolgokat, mint a regiszterek kiosztása, az írási hurkok, és általában leegyszerűsítik az összeállítási programozás világába való belépést a Go-ban.

Az avo-t fogjuk használni, így a programjaink szinte szokásos Go programok lesznek.
Bittérképes indexek a Go-ban: vad sebességű keresés
Így néz ki egy avo program legegyszerűbb példája. Van egy main() függvényünk, ami magában definiálja az Add() függvényt, aminek a jelentése két szám összeadása. Itt vannak segédfunkciók, amelyek segítségével név szerint kaphatunk paramétereket, és megkaphatjuk az ingyenes és megfelelő processzorregiszterek valamelyikét. Minden processzorműveletnek megfelelő funkciója van az avo-n, amint az az ADDQ-ban látható. Végül egy segítő függvényt látunk a kapott érték tárolására.
Bittérképes indexek a Go-ban: vad sebességű keresés
A go generate meghívásával végrehajtjuk a programot az avo-n, és ennek eredményeként két fájl generálódik:

  • add.s a kapott kóddal a Go assemblerben;
  • A stub.go funkciófejlécekkel összekapcsolja a két világot: Go és assembler.

Bittérképes indexek a Go-ban: vad sebességű keresés
Most, hogy láttuk, mit csinál az avo és hogyan, nézzük meg a funkcióinkat. A függvények skaláris és vektoros (SIMD) változatát is megvalósítottam.

Nézzük először a skaláris változatokat.
Bittérképes indexek a Go-ban: vad sebességű keresés
Az előző példához hasonlóan ingyenes és érvényes általános célú regisztert kérünk, nem kell eltolásokat és méreteket számolnunk az argumentumokhoz. avo mindezt értünk teszi.
Bittérképes indexek a Go-ban: vad sebességű keresés
Korábban címkéket és goto-t (vagy ugrásokat) használtunk a teljesítmény javítására és a Go fordító átverésére, de most már az elejétől fogva ezt tesszük. A lényeg az, hogy a ciklusok magasabb szintű fogalom. Az assemblerben csak címkéink és ugrások vannak.
Bittérképes indexek a Go-ban: vad sebességű keresés
A fennmaradó kódnak már ismerősnek és érthetőnek kell lennie. Emulálunk egy ciklust címkékkel és ugrásokkal, kiveszünk egy kis adatot a két szeletünkből, kombináljuk egy bitművelettel (ÉS ebben az esetben NEM), majd az eredményt beletesszük a kapott szeletbe. Minden.
Bittérképes indexek a Go-ban: vad sebességű keresés
Így néz ki a végső assembler kód. Nem kellett eltolásokat és méreteket számolnunk (zöld színnel kiemelve), vagy nyomon követni a használt regisztereket (pirossal kiemelve).
Bittérképes indexek a Go-ban: vad sebességű keresés
Ha összehasonlítjuk az assembly nyelvi megvalósítás teljesítményét a Go legjobb megvalósításának teljesítményével, látni fogjuk, hogy ez ugyanaz. És ez várható is. Végül is nem csináltunk semmi különöset – csak reprodukáltuk azt, amit egy Go fordító csinálna.

Sajnos nem tudjuk rákényszeríteni a fordítót, hogy beépítse assembly nyelven írt függvényeinket. A Go fordító jelenleg nem rendelkezik ilyen funkcióval, bár már jó ideje kérték a hozzáadását.

Ez az oka annak, hogy az assembly nyelven nem lehet hasznot húzni a kis függvényekből. Vagy nagy függvényeket kell írnunk, vagy az új math/bits csomagot kell használnunk, vagy megkerülnünk az assembler nyelvet.

Nézzük most függvényeink vektoros változatait.
Bittérképes indexek a Go-ban: vad sebességű keresés
Ebben a példában az AVX2 használata mellett döntöttem, ezért olyan műveleteket fogunk használni, amelyek 32 bájtos darabokon működnek. A kód felépítése nagyon hasonlít a skaláris verzióhoz: paraméterek betöltése, ingyenes megosztott regiszter kérése stb.
Bittérképes indexek a Go-ban: vad sebességű keresés
Az egyik újítás az, hogy a szélesebb vektorműveletek speciális széles regisztereket használnak. A 32 bájtos darabok esetében ezek Y előtagú regiszterek. Emiatt látható az YMM() függvény a kódban. Ha AVX-512-t használnék 64 bites darabokkal, akkor az előtag Z lenne.

A második újítás az, hogy úgy döntöttem, hogy a loop unrolling nevű optimalizálást használom, ami azt jelenti, hogy nyolc ciklus műveletet kell manuálisan elvégezni, mielőtt a ciklus elejére ugornék. Ez az optimalizálás csökkenti a kódban lévő fiókok számát, és korlátozza a rendelkezésre álló ingyenes regiszterek száma.
Bittérképes indexek a Go-ban: vad sebességű keresés
Nos, mi a helyzet a teljesítménnyel? Ő szép! Körülbelül hétszeres gyorsulást értünk el a legjobb Go megoldáshoz képest. Lenyűgöző, igaz?
Bittérképes indexek a Go-ban: vad sebességű keresés
De még ez a megvalósítás is felgyorsítható az AVX-512, az előzetes letöltés vagy a JIT (just-in-time fordító) használatával a lekérdezésütemezőhöz. De ez minden bizonnyal egy külön jelentés témája.

Problémák a bittérképes indexekkel

Most, hogy már megvizsgáltuk a bittérképes index egyszerű megvalósítását a Go-ban, és egy sokkal produktívabbat assembly nyelven, beszéljünk végre arról, hogy miért használnak olyan ritkán bitmap indexeket.
Bittérképes indexek a Go-ban: vad sebességű keresés
A régebbi cikkek három problémát említenek a bittérképes indexekkel, de az újabb dolgozatok, és én azt állítom, hogy ezek már nem relevánsak. Nem merülünk el mélyen ezekben a problémákban, hanem felületesen vizsgáljuk meg őket.

A magas kardinalitás problémája

Tehát azt mondják nekünk, hogy a bittérképes indexek csak az alacsony kardinalitású mezőkhöz alkalmasak, vagyis azokhoz, amelyeknek kevés értéke van (például nem vagy szemszín), és ennek az az oka, hogy az ilyen mezők szokásos megjelenítése (egy bit per érték) nagy számosság esetén túl sok helyet foglal el, ráadásul ezek a bittérképes indexek rosszul (ritkán) lesznek kitöltve.
Bittérképes indexek a Go-ban: vad sebességű keresés
Bittérképes indexek a Go-ban: vad sebességű keresés
Néha másfajta ábrázolást is használhatunk, például a számok ábrázolására használt standardot. De a tömörítési algoritmusok megjelenése mindent megváltoztatott. Az elmúlt évtizedekben a tudósok és kutatók számos tömörítési algoritmust dolgoztak ki bittérképekhez. Fő előnyük, hogy a bitműveletek végrehajtásához nincs szükség a bitképek kitömörítésére – a tömörített bittérképeken közvetlenül is végezhetünk bitműveleteket.
Bittérképes indexek a Go-ban: vad sebességű keresés
Az utóbbi időben megjelentek a hibrid megközelítések, például az ordító bittérképek. Egyszerre három különböző reprezentációt használnak a bitképekhez – maguk a bitképek, a tömbök és az úgynevezett bitfutások –, és egyensúlyt teremtenek közöttük a teljesítmény maximalizálása és a memóriafelhasználás minimalizálása érdekében.

A legnépszerűbb alkalmazásokban üvöltő bittérképeket találhat. Már most is rengeteg implementáció létezik a legkülönfélébb programozási nyelvekhez, köztük több mint három implementáció a Go számára.
Bittérképes indexek a Go-ban: vad sebességű keresés
Egy másik megközelítés, amely segíthet megbirkózni a nagy számossággal, az úgynevezett binning. Képzelje el, hogy van egy mezője, amely egy személy magasságát jelzi. A magasság egy lebegőpontos szám, de mi, emberek nem így gondoljuk. Nálunk nincs különbség 185,2 cm és 185,3 cm magasság között.

Kiderült, hogy a hasonló értékeket 1 cm-en belül csoportokba csoportosíthatjuk.

És ha azt is tudjuk, hogy nagyon kevesen vannak 50 cm-nél alacsonyabbak és 250 cm-nél magasabbak, akkor egy végtelen számú mezőt lényegében egy körülbelül 200 értékű kardinalitású mezővé alakíthatunk.

Természetesen szükség esetén utólag további szűrést is végezhetünk.

Nagy sávszélesség probléma

A következő probléma a bittérképes indexekkel az, hogy frissítésük nagyon költséges lehet.

Az adatbázisoknak képesnek kell lenniük az adatok frissítésére, miközben potenciálisan több száz egyéb lekérdezés keresi az adatokat. Zárakra van szükségünk, hogy elkerüljük az egyidejű adathozzáféréssel vagy más megosztási problémákkal kapcsolatos problémákat. És ahol egy nagy zár van, ott van egy probléma - zárverseny, amikor ez a zár szűk keresztmetszetté válik.
Bittérképes indexek a Go-ban: vad sebességű keresés
Ez a probléma megoldható vagy megkerülhető felosztással vagy verziószámú indexek használatával.

A megosztás egyszerű és jól ismert dolog. A bittérképes indexeket ugyanúgy feldarabolhatja, mint bármely más adatot. Egy nagy zár helyett egy csomó kis zárat kap, és így megszabadul a zárral kapcsolatos vitáktól.

A probléma megoldásának második módja a verziószámú indexek használata. Rendelkezhet egy példányával az indexből, amelyet keresésre vagy olvasásra használ, és egyet, amelyet íráshoz vagy frissítéshez használ. És bizonyos időközönként (például 100 ms-onként vagy 500 ms-onként egyszer) lemásolja és felcseréli őket. Természetesen ez a megközelítés csak olyan esetekben alkalmazható, amikor az alkalmazás képes kezelni egy kissé lemaradt keresési indexet.

Ez a két megközelítés egyszerre is használható: rendelkezhet szilánkos verziójú indexszel.

Bonyolultabb lekérdezések

A bittérképes indexekkel kapcsolatos végső probléma az, hogy azt mondják, hogy nem alkalmasak bonyolultabb típusú lekérdezésekre, például a span lekérdezésekre.

Valóban, ha jobban belegondolunk, az olyan bitműveletek, mint az ÉS, VAGY stb., nem nagyon alkalmasak az olyan lekérdezésekre, mint a „Mutasd a szállodákat éjszakánként 200 és 300 dollár közötti szobaárakkal”.
Bittérképes indexek a Go-ban: vad sebességű keresés
Naiv és nagyon oktalan megoldás az lenne, ha az eredményeket minden egyes dollárértékre vesszük, és egy bitenkénti VAGY művelettel kombináljuk.
Bittérképes indexek a Go-ban: vad sebességű keresés
Valamivel jobb megoldás lenne a csoportosítás használata. Például 50 dolláros csoportokban. Ez 50-szeresére gyorsítaná fel a folyamatot.

De a probléma is könnyen megoldható egy kifejezetten az ilyen típusú kérésekhez készített nézet használatával. A tudományos közleményekben tartománykódolt bittérképeknek nevezik.
Bittérképes indexek a Go-ban: vad sebességű keresés
Ebben az ábrázolásban nem csak egy bitet állítunk be valamilyen értékhez (például 200), hanem ezt az értéket és mindent magasabbra állítunk. 200 és felette. Ugyanez vonatkozik a 300:300-ra és afelettire is. Stb.

Ezzel a reprezentációval megválaszolhatjuk az ilyen típusú keresési lekérdezéseket, ha kétszer bejárjuk az indexet. Először egy listát kapunk azokról a szállodákról, ahol kevesebb vagy 300 dollár a szoba, majd eltávolítjuk belőle azokat, ahol a szoba ára kevesebb vagy 199 dollár. Kész.
Bittérképes indexek a Go-ban: vad sebességű keresés
Meg fog lepődni, de még a geolekérdezések is lehetségesek bittérképes indexek használatával. A trükk az, hogy olyan geometriai ábrázolást használunk, amely körülveszi a koordinátát egy geometriai ábrával. Például az S2 a Google-tól. Az ábrát három vagy több egymást metsző, számozható vonal formájában kell ábrázolni. Így a geolekérdezésünket több lekérdezéssé alakíthatjuk „a rés mentén” (e számozott sorok mentén).

Kész megoldások

Remélem, egy kicsit érdekelt, és most egy másik hasznos eszköz is van az arzenáljában. Ha valami ilyesmit kell tennie, tudni fogja, hogyan nézzen ki.

Azonban nem mindenkinek van ideje, türelme vagy erőforrása bitmap indexek létrehozására a semmiből. Főleg a fejlettebbek, például SIMD használatával.

Szerencsére több kész megoldás is segíthet.
Bittérképes indexek a Go-ban: vad sebességű keresés

Ordító bitképek

Először is, ott van ugyanaz az ordító bittérképes könyvtár, amelyről már beszéltem. Tartalmazza az összes szükséges konténert és bitműveletet, amelyekre egy teljes bitmap index elkészítéséhez szüksége lesz.
Bittérképes indexek a Go-ban: vad sebességű keresés
Sajnos jelenleg egyik Go implementáció sem használ SIMD-t, ami azt jelenti, hogy a Go implementációk kevésbé teljesítenek, mint például a C implementációk.

szőrös

Egy másik termék, amely segíthet, a Pilosa DBMS, amely valójában csak bittérképes indexekkel rendelkezik. Ez egy viszonylag új megoldás, de nagy sebességgel hódítja meg a szíveket.
Bittérképes indexek a Go-ban: vad sebességű keresés
A Pilosa belsőleg zúgó bittérképeket használ, és lehetőséget ad ezek használatára, leegyszerűsíti és elmagyarázza mindazt, amiről fentebb beszéltem: csoportosítás, tartománykódolt bittérképek, mező fogalma stb.

Vessünk egy rövid pillantást a Pilosa használatára egy olyan kérdés megválaszolására, amelyet már ismer.
Bittérképes indexek a Go-ban: vad sebességű keresés
A példa nagyon hasonló ahhoz, amit korábban láttál. Létrehozunk egy klienst a Pilosa szerverhez, elkészítjük az indexet és a szükséges mezőket, majd kitöltjük a mezőinket véletlenszerű adatokkal valószínűségekkel és végül végrehajtjuk az ismerős lekérdezést.

Ezt követően a "drága" mezőben használjuk a NEM-et, majd az eredményt (vagy ÉS azt) metsszük a "terasz" mezővel és a "foglalások" mezővel. És végül megkapjuk a végeredményt.
Bittérképes indexek a Go-ban: vad sebességű keresés
Nagyon remélem, hogy a belátható jövőben ez az új típusú index az olyan adatbázis-kezelő rendszerekben is megjelenik, mint a MySQL és a PostgreSQL - bitmap indexek.
Bittérképes indexek a Go-ban: vad sebességű keresés

Következtetés

Bittérképes indexek a Go-ban: vad sebességű keresés
Ha még nem aludtál el, köszönöm. Az idő szűkössége miatt sok témát kellett röviden érintenem, de remélem, hasznos volt a beszélgetés, sőt talán motiváló is volt.

A bitmap indexeket akkor is jó tudni, ha jelenleg nincs rájuk szüksége. Legyenek egy másik eszköz az eszköztáradban.

Megvizsgáltunk különféle teljesítmény-trükköket a Go-hoz, és olyan dolgokat, amelyeket a Go fordító még nem kezel túl jól. De ezt minden Go programozónak feltétlenül tudnia kell.

Ez minden, amit el akartam mondani. Köszönöm!

Forrás: will.com

Hozzászólás