Fjärrkomprimeringar i RocksDB-moln

(Hieu Pham) (10 juni , 2020)

Inledning

RocksDB är en LSM-lagringsmotor vars tillväxt har spridit sig enormt under de senaste åren . RocksDB-Cloud är öppen källkod och är fullt kompatibelt med RocksDB, med den ytterligare funktionen att all data görs hållbar genom att automatiskt lagra den i molnlagring (t.ex. Amazon S3).

Vi på Rockset använder RocksDB-Cloud som en av byggstenarna i Rocksets distribuerade Konvergerat index . Rockset är utformat med moln-native-principer, och en av de primära designprinciperna i en cloud-native-databas är att separera beräkningen från lagring. Vi kommer att diskutera hur vi utökade RocksDB-Cloud för att ha en ren separation av dess lagringsbehov och dess beräkningsbehov.

En kompaktor, som drivs av US Navy Seabees, som utför jordkomprimering

RocksDB: s LSM-motor

RocksDB-Cloud lagrar data i lokalt ansluten SSD eller snurrskivor. SSD eller den snurrande skivan ger den lagring som behövs för att lagra den data som den serverar. Nya skrivningar till RocksDB-Cloud skrivs till en minnesminne i minnet, och när memtabellen är full, spolas den till en ny SST-fil i lagret.

Att vara en LSM-lagringsmotor, en uppsättning av bakgrundstrådar används för komprimering, och komprimering är en process för att kombinera en uppsättning SST-filer och generera nya SST-filer med överskrivna nycklar och borttagna nycklar rensade från utdatafilerna. Komprimering behöver mycket beräkningsresurser. Ju högre skrivhastighet i databasen desto mer beräkningsresurser behövs för komprimering, eftersom systemet bara är stabilt om komprimering kan hålla jämna steg med nya skrivningar till din databas.

Problemet vid beräkning och lagring är inte uppdelade

I en typisk RocksDB-baserat system, komprimering sker på processorer som är lokala på servern som är värd för lagring också. I det här fallet uppdelas inte beräkning och lagring. Och det betyder att om din skrivhastighet ökar men den totala storleken på din databas förblir densamma, måste du dynamiskt tillhandahålla fler servrar, sprida dina data till alla dessa servrar och sedan utnyttja den extra beräkningen på dessa servrar för att hålla jämna steg med komprimeringsbelastning.

Detta har två problem:

  • Att sprida dina data till fler servrar är inte direkt eftersom du måste kopiera mycket data för att göra det. Det betyder att du inte kan reagera snabbt på en snabbt föränderlig arbetsbelastning.
  • Lagringskapacitetsutnyttjandet på var och en av dina servrar blir mycket låg eftersom du sprider dina data till fler servrar. Du tappar förhållandet mellan pris och prestanda på grund av all oanvänd lagring på dina servrar.

Vår lösning

Den främsta anledningen till att RocksDB-Cloud är lämplig för att separera kompakteringsberäkning och lagring beror på att det är en LSM-lagringsmotor. Till skillnad från en B-Tree-databas uppdaterar RocksDB-Cloud aldrig en SST-fil när den har skapats. Detta innebär att alla SST-filer i hela systemet är skrivskyddade utom den minsta delen av data i din aktiva memtable. RocksDB-Cloud fortsätter alla SST-filer i en molnlagringsobjekt som S3, och dessa molnobjekt är säkert tillgängliga från alla dina servrar eftersom de är skrivskyddade.

Så vår idé är att om en RocksDB -Cloud server A kan inkapsla ett komprimeringsjobb med sin uppsättning molnobjekt och sedan skicka begäran till en fjärrstatslös server B – och den servern B kan hämta relevanta objekt från molnbutiken, göra komprimeringen, producera en uppsättning utdata SST-filer som skrivs tillbaka till molnobjektbutiken och sedan kommunicerar den informationen tillbaka till servern A-vi har i huvudsak separerat lagringen (som finns i server A) från komprimeringsberäkningen (som finns i server B). Server A har lagringen och medan server B inte har någon permanent lagring utan bara den beräkning som behövs för komprimering. Voila!

RocksDB pluggbar kompakterings-API

Vi utökade basen RocksDB API med två nya metoder som gör komprimeringsmotorn i RocksDB extern pluggbar. I db.h introducerar vi ett nytt API för att registrera en komprimeringstjänst.

Status RegisterPluggableCompactionService(std::unique_ptr);

Detta API registrerar pluginet som används för att utföra komprimeringsjobbet av RocksDB. Fjärrkomprimering sker i två steg: Run och InstallFiles. Plugin, PluggableCompactionService , skulle därför ha två API: er:

Status Run(const PluggableCompactionParam& job, PluggableCompactionResult* result) std::vector InstallFiles(
const std::vector& remote_paths,
const std::vector& local_paths,
const EnvOptions& env_options, Env* local_env)

Run är där komprimering utförs.I vår fjärrkomprimeringsarkitektur skulle Run skicka en RPC till ett fjärrkomprimeringsnivå och få ett komprimeringsresultat som bland annat har listan över nyligen komprimerade SST-filer.

InstallFiles är där RocksDB installerar de nyligen komprimerade SST-filerna från molnet (remote_paths) till sin lokala databas (local_paths).

Rocksets komprimeringsnivå

Nu visar vi hur vi använde den pluggbara komprimeringstjänsten som beskrivs ovan i Rocksets komprimeringstjänst. Som nämnts ovan skickar det första steget, Run, en RPC till en fjärrkomprimeringsnivå med komprimeringsinformation såsom inmatade SST-filnamn och komprimeringsinformation. Vi kallar värden som utför det här komprimeringsjobbet en kompaktor.

komprimatorn , när den mottog komprimeringsförfrågan, skulle öppna en RocksDB-Cloud-instans i spöke -läge. Vad detta betyder är att RocksDB-Cloud öppnar den lokala databasen med endast nödvändiga metadata utan att hämta alla SST-filer från molnlagringen. När den väl har öppnat RocksDB-instansen i ghost -läge, kommer den att utföra komprimeringsjobbet, inklusive att hämta de nödvändiga SST-filerna, komprimera dem och ladda upp de nyligen komprimerade SST-filerna till en tillfällig lagring i molnet.

Här är alternativen för att öppna RocksDB-Cloud i komprimatorn :

rocksdb::CloudOptions cloud_options; cloud_options.ephemeral_resync_on_open = false; cloud_options.constant_sst_file_size_in_sst_file_manager = 1024; cloud_options.skip_cloud_files_in_getchildren = true;rocksdb::Options rocksdb_options;
rocksdb_options.max_open_files = 0; rocksdb_options.disable_auto_compactions = true; rocksdb_options.skip_stats_update_on_db_open = true; rocksdb_options.paranoid_checks = false; rocksdb_options.compaction_readahead_size = 10 * 1024 * 1024;

Det finns flera utmaningar vi mötte under utvecklingen av komprimeringsnivån och våra lösningar:

Förbättra hastigheten för att öppna RocksDB-Cloud i ghost mode

Under öppningen av en RocksDB-instans, förutom att hämta alla SST-filer från molnet (som vi har inaktiverat med ghost -läge), det finns flera andra operationer som kan bromsa öppningsprocessen, särskilt att få listan över SST-filer och få storleken på varje SST-fil. Vanligtvis, om alla SST-filer finns på lokal lagring, skulle latensen för dessa get-filstorleksåtgärder vara liten. Men när kompaktorn öppnar RocksDB-Cloud, skulle var och en av dessa operationer resultera i en fjärrförfrågan till molnlagringen, och den totala kombinerade latensen blir oöverkomligt dyr. Enligt vår erfarenhet skulle en RocksDB-Cloud-instans med tusentals SST-filer ta upp till en minut på grund av tusentals förfrågningar om att få filstorlek till S3. För att komma runt denna begränsning introducerade vi olika alternativ i RocksDB-Cloud-alternativen för att inaktivera dessa RPC under öppningen. Som ett resultat går den genomsnittliga öppningstiden från 7 sekunder till 700 millisekunder.

Inaktivera L0 -> L0-komprimering

Fjärrkomprimering är en kompromiss mellan hastigheten för en enda komprimering och möjligheten att köra fler komprimeringsjobb parallellt. Det beror naturligtvis på att varje fjärrkomprimeringsjobb skulle vara långsammare än samma komprimering som utförs lokalt på grund av kostnaden för dataöverföring i molnet. Därför vill vi minimera flaskhalsen i komprimeringsprocessen, där RocksDB-Cloud inte kan parallellisera så mycket som möjligt.

I LSM-arkitekturen är L0-> L1-komprimering vanligtvis inte parallelliserbar eftersom L0-filer har överlappande intervall. Därför, när en L0-> L1-komprimering uppstår, har RocksDB-Cloud förmågan att också utföra L0-> L0-komprimering, med målet att minska antalet L0-filer och förhindra skrivstall på grund av att RocksDB-Cloud träffar L0-filen begränsa. Avvägningen är emellertid att varje L0-fil skulle växa större i storlek efter varje L0-> L0-komprimering.

Enligt vår erfarenhet orsakar detta alternativ mer problem än fördelarna det ger, eftersom det har större L0-filer resulterar i en mycket längre L0-> L1-komprimering, vilket förvärrar RocksDB-Clouds flaskhals. Därför inaktiverar vi L0-> L0-komprimering och lever med det sällsynta problemet att skriva stall istället. Från vårt experiment hämtar RocksDB-Cloud-komprimering de inkommande skrivningarna mycket bättre.

Du kan använda den nu

RocksDB-Cloud är ett projekt med öppen källkod, så vårt arbete kan utnyttjas av någon annan RocksDB-utvecklare som vill dra fördelar genom att skilja ut sin komprimeringsberäkning från deras lagringsbehov. Vi kör fjärrpakningstjänsten i produktion nu. Den är tillgänglig med 6.7.3-utgåvan av RocksDB-Cloud. Vi diskuterar allt om RocksDB-Cloud i den offentliga Slack-kanalen på http://bit.ly/rockset-community-channel .

Författare:

Hieu Pham – Software Engineer, Rockset
Dhruba Borthakur – CTO, Rockset

Ursprungligen publicerad på https: // rockset.com den 4 juni 2020.

Lämna ett svar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *