Lewati ke konten

Sharding

DocStore menulis satu file per rekaman. Tanpa sharding, koleksi besar akan menumpuk semua file dalam satu direktori, dan operasi direktori melambat seiring bertambahnya jumlah entri. Karena itu, storh mengelompokkan file rekaman berdasarkan dua karakter dari id rekaman:

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

Bucket diambil dari awal grup terakhir UUID (karakter 24 dan 25), yang berisi entropi acak. Dua karakter heksadesimal menghasilkan 256 bucket, sehingga koleksi dengan 100k rekaman rata-rata memiliki kurang dari 400 file per direktori.

Mengapa bagian akhir, bukan bagian awal

Id UUIDv7 dimulai dengan stempel waktu milidetik 48-bit, sehingga id yang dibuat berdekatan memiliki karakter awal yang sama. Sharding berdasarkan prefiks akan mengirim setiap lonjakan penulisan ke direktori panas yang sama dan baru mengganti bucket seiring berjalannya waktu. Byte bagian akhir bersifat acak untuk setiap rekaman, sehingga penulisan tersebar merata sejak rekaman pertama.

Sharding tidak terlihat dalam pembacaan dan kueri: get() menghitung bucket dari id, dan stream() mengembalikan rekaman yang diurutkan berdasarkan id dari semua bucket, sehingga paginasi kursor berperilaku seolah-olah koleksi tersebut adalah satu daftar terurut.

Dalam skala besar

Jumlah bucket tidak pernah bertambah: dua karakter heksadesimal berarti 256 direktori untuk ukuran koleksi berapa pun. Yang bertambah adalah jumlah file per bucket:

Rekaman File per bucket
10k ~40
100k ~390
1M ~3,900
10M ~39,000

Sistem file modern mengindeks entri direktori, sehingga beberapa ribu file per bucket menjaga pencarian langsung tetap cepat; 1M rekaman masih nyaman untuk get() dan kueri terindeks. Membuka penyimpanan membutuhkan waktu konstan berapa pun ukuran koleksinya: pemindaian file sementara yatim hanya berjalan ketika penanda penulis mati menunjukkan bahwa proses sebelumnya mengalami crash saat menulis. Yang bertambah secara linear dalam skala besar adalah operasi seluruh koleksi: pemindaian tanpa indeks, reindex(), dan verify() masing-masing menelusuri setiap file rekaman, jadi perlakukan sebagai tugas batch. Ketika jumlah rekaman per koleksi melewati sekitar 10M, jumlah per bucket dan total penggunaan inode mulai menjadi perhatian: pisahkan koleksi, atau gunakan log tersegmentasi untuk data yang perlu dipindai, yang menjaga jumlah file tetap rendah sejak awal.

Mesin lainnya

SegmentedLog dan Queue tidak menggunakan sharding karena keduanya tidak membuat satu file per rekaman. Log menambahkan data ke file segmen di bawah segments/, yang dibatasi oleh max_segment_bytes (default 1 MiB), dan antrean menambahkan peristiwa ke satu queue.log. Jumlah file tetap rendah sejak awal.