Gå til indhold

Sharding

DocStore skriver én fil pr. post. Uden sharding ville en stor samling samle alle filer i én mappe, og mappehandlinger bliver langsommere, når antallet af poster vokser. storh opdeler derfor postfiler i buckets efter postens id med to tegn:

Text
pages/data/2d/018bcfe5-6800-7000-8000-2d5dba6274da.jsonc

Bucketen tages fra starten af det sidste segment i UUID (tegn 24 og 25), som består af tilfældig entropi. To hexadecimale tegn giver 256 buckets, så en samling med 100k poster har i gennemsnit under 400 filer pr. mappe.

Hvorfor halen og ikke hovedet

UUIDv7-id'er begynder med et tidsstempel på 48 bit i millisekunder, så id'er, der oprettes tæt på hinanden, deler de første tegn. Sharding efter præfiks ville sende hver skrivebølge til den samme overbelastede mappe og kun skifte bucket, som tiden går. Bytes i halen er tilfældige pr. post, så skrivninger fordeles jævnt fra den første post.

Sharding er usynligt for læsninger og forespørgsler: get() beregner bucketen ud fra id'et, og stream() returnerer poster sorteret efter id på tværs af alle buckets, så markørbaseret sideinddeling fungerer, som om samlingen var én ordnet liste.

I stor skala

Antallet af buckets vokser aldrig: to hexadecimale tegn giver 256 mapper uanset samlingens størrelse. Det, der vokser, er antallet af filer pr. bucket:

Poster Filer pr. bucket
10k ~40
100k ~390
1M ~3,900
10M ~39,000

Moderne filsystemer indekserer mappeposter, så nogle få tusinde filer pr. bucket holder direkte opslag hurtige; 1M poster fungerer fint med get() og indekserede forespørgsler. Åbning af et lager tager konstant tid uanset samlingens størrelse: oprydningen af forældreløse midlertidige filer kører kun, når en markør for en død skriveproces viser, at en tidligere proces gik ned midt i en skrivning. Det, der vokser lineært i stor skala, er operationer på hele samlingen: scanninger uden indeks, reindex() og verify() gennemgår hver postfil, så betragt dem som batchjob. Når en samling når omkring 10M poster, begynder antallet pr. bucket og det samlede inodeforbrug at have betydning: opdel samlingerne, eller brug den segmenterede log til data, der kræver scanninger, som holder filantallet lavt fra starten.

De andre motorer

SegmentedLog og Queue bruger ikke sharding, fordi de ikke opretter én fil pr. post. Loggen føjer data til segmentfiler under segments/, begrænset af max_segment_bytes (1 MiB som standard), og køen føjer hændelser til én queue.log. Filantallet forbliver lavt fra starten.