Hoppa till innehåll

Sharding

DocStore skriver en fil per post. Utan sharding skulle en stor samling samla alla filer i en katalog, och katalogåtgärder försämras när antalet poster växer. storh delar därför in postfiler i hinkar efter två tecken av postens id:

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

Hinken hämtas från början av UUID:s sista grupp (tecken 24 och 25), som består av slumpmässig entropi. Två hexadecimala tecken ger 256 hinkar, så en samling med 100k poster har i genomsnitt färre än 400 filer per katalog.

Varför svansen och inte huvudet

UUIDv7-id:n börjar med en tidsstämpel på 48 bitar i millisekunder, så id:n som skapas nära varandra delar inledande tecken. Sharding efter prefix skulle skicka varje skrivningstopp till samma belastade katalog och rotera hinkar först när tiden går. Svansens byte är slumpmässiga för varje post, så skrivningar fördelas jämnt från första posten.

Sharding märks inte vid läsningar och frågor: get() beräknar hinken från id:t och stream() returnerar poster sorterade efter id över alla hinkar, så markörsidnumrering fungerar som om samlingen vore en enda ordnad lista.

I stor skala

Antalet hinkar växer aldrig: två hexadecimala tecken betyder 256 kataloger oavsett samlingens storlek. Det som växer är antalet filer per hink:

Poster Filer per hink
10k ~40
100k ~390
1M ~3,900
10M ~39,000

Moderna filsystem indexerar katalogposter, så några tusen filer per hink håller punktuppslagningar snabba; 1M poster fungerar bra för get() och indexerade frågor. Att öppna ett lager tar konstant tid oavsett samlingens storlek: rensningen av föräldralösa temporärfiler körs bara när en kvarlämnad skrivarmarkör visar att en tidigare process kraschade mitt i en skrivning. Det som växer linjärt i stor skala är åtgärder över hela samlingen: oindexerade skanningar, reindex() och verify() går igenom varje postfil, så behandla dem som batchjobb. När en samling passerar ungefär 10M poster börjar antalet filer per hink och det totala antalet inoder spela roll: dela upp samlingarna eller använd den segmenterade loggen för skanningsdata, som håller filantalet lågt genom sin konstruktion.

De andra motorerna

SegmentedLog och Queue använder inte sharding eftersom de inte skapar en fil per post. Loggen lägger till poster i segmentfiler under segments/, med en gräns på max_segment_bytes (1 MiB som standard), och kön lägger till händelser i en enda queue.log. Filantalet hålls lågt genom konstruktionen.