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:
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.