Sayfalama ve imleçler
1) Neden paginasyon gereklidir
Pagination, istemci tarafından iletilen ve oluşturulan veri miktarını sınırlar, depolama/ağlar üzerindeki yükü azaltır ve koleksiyon boyunca "yürümek" için deterministik bir yol belirler. Gerçek sistemlerde, sayfalama sadece 'sayfa = 1 & limit = 50'değil, bir dizi protokol sözleşmesi ve tutarlılık değişmezidir.
Tipik hedefler:- İstek başına gecikme ve bellek kontrolü.
- Bir veri kümesini değiştirirken kararlı gezinme (ekle/sil).
- Bir yerden devam etme yeteneği (yeniden başlatma).
- Önbelleğe alma ve önbelleğe alma (prefetch).
- Kötüye kullanıma karşı koruma (hız sınırlaması, geri basınç).
2) Pagination modelleri
2. 1 OFSET/LIMIT (sayfalı)
Fikir: "N çizgilerini atla, M'yi geri ver".
Artıları: basitlik, hemen hemen her SQL/NoSQL ile uyumlu.
- Doğrusal bozulma: Büyük OFSET'ler tam tarama/atlama maliyetiyle sonuçlanır.
- İstekler arasındaki eklemeler/silmeler sırasında kararsızlık ("float" ofsetleri).
- Doğru "yenilenebilirlik" sağlamak zordur.
sql
SELECT
FROM orders
ORDER BY created_at DESC, id DESC
OFFSET 1000 LIMIT 50;
2. 2 İmleç/Keyset/Arama sayfası
Fikir: "K tuşuna bas. "İmleç, sıralanmış kümedeki konumdur.
Artıları:- O (1) dizine devam etmek için erişim.
- Toplama değişiklikleri sırasında stabilite.
- Derin "sayfalarda'en iyi gecikme.
- Kesinlikle tanımlanmış, benzersiz ve monoton sıralama anahtarlarına ihtiyacımız var.
- Uygulaması ve hata ayıklaması daha zordur.
sql
-- Resumption after steam (created_at, id) = (:last_ts,:last_id)
SELECT
FROM orders
WHERE (created_at, id) < (:last_ts,:last_id)
ORDER BY created_at DESC, id DESC
LIMIT 50;
2. 3 Devam belirteçleri
Fikir: sunucu, "konumun" kodlandığı opak bir belirteç döndürür (ve muhtemelen parçacıkların/filtrelerin durumu). Müşteri iç kısımları anlamıyor ve sadece bir sonraki sayfa için bir jeton döndürüyor.
Artıları: esneklik, API'yi bozmadan şemayı değiştirme yeteneği.
Eksileri: belirteç ömür boyu yönetimi, mevduat ile uyumluluk.
2. 4 Zaman ve mantık imleçleri
Zaman tabanlı: "T'ye kadar tüm kayıtlar", imleç - zaman damgası (yalnızca ek iş parçacıkları için uygundur).
Log-sequence/offset tabanlı: imleç - günlükte offset (Kafka offset, journal seq).
Global monotonik kimlikler: Kararlı arama için sıralanabilir anahtarlar olarak Snowflake/UUIDv7.
3) Kursların ve belirteçlerin tasarlanması
3. 1 İyi İmleç Özellikleri
Opak - İstemci formattan bağımsızdır.
Yazarlık/bütünlük: Sahteciliği/manipülasyonu önlemek için HMAC imzası.
Bağlam: sıralama, filtreler, şema sürümü, kiracı/parça içerir.
Ömür boyu: TTL ve indeksleri/erişim haklarını değiştirirken "tekrar oynatmama".
Boyut: URL için uygun kompakt (<= 1-2 KB).
3. 2 Token formatı
Önerilen yığın: JSON - sıkıştırma (zstd/deflate) - Base64URL - HMAC.
Payload yapısı (örnek):json
{
"v": 3 ,//token version
"sort": ["created_at:desc","id:desc"],
"pos": {"created_at":"2025-10-30T12:34:56. 789Z","id":"987654321"},
"filters": {"user_id":"42","status":["paid","shipped"]},
"tenant": "eu-west-1",
"shards": [{"s ": "a, "" hi":"..."}] ,//optional: cross-shard context
"issued_at": "2025-10-31T14:00:00Z",
"ttl_sec": 3600
}
'mac = HMAC (gizli, yük)' üste eklenir ve her şey bir dize belirtecine kodlanır.
3. 3 Güvenlik
İşaret (HMAC/SHA-256).
İsteğe bağlı olarak (AES-GCM) hassas değerlerin (PII) varlığında şifreleyin.
Sunucu doğrulama: sürüm, TTL, kullanıcı otoritesi (RBAC/ABAC).
4) Tutarlılık ve değişmezler
4. 1 Kararlı sıralama
Tam determinizm kullanın: 'ORDER BY ts DESC, id DESC'.
Sıralama anahtarı benzersiz olmalıdır (tiebreaker olarak'id 'ekleyin).
Dizin, kaplayan dizin ile eşleşmelidir.
4. 2 Anlık görüntüler ve izolasyon
Jumbo olmayan sayfalar için, okuma tutarlı anlık görüntü (MVCC/txid) kullanın.
Anlık görüntü pratik değilse (pahalı/çok fazla veri), bir sözleşme formüle edin: "imleç, konumdan kesinlikle önce öğeleri döndürür. Bu haber kaynakları için doğaldır.
4. 3 Sayfalar arasındaki eklemeler/silmeler
Seek-model "kopyaları/eksiklikleri'en aza indirir.
Belge silme/değiştirme davranışı: Sayfalar arasında nadir "deliklere" izin verilir, ancak "zamanda geri" izin verilmez.
5) İndeksleme ve kimlik şemaları
Bileşik indeksler kesinlikle sıralama düzenindedir: '(created_at DESC, id DESC)'.
Monoton kimlikler: Zaman içinde düzen Snowflake/UUIDv7 - aramayı hızlandırın.
Kısayol tuşları: shard-key (örneğin, 'tenant _ id', 'region') ile dağıtın ve shard içinde sıralayın.
Kimlik üreteçleri: NTP sıçramaları sırasında çarpışmalar ve "saat eğriltme" - zaman senkronizasyonu, "regresyon" önlemek.
6) Çapraz parça sayfalama
6. 1 Şemalar
Scatter-Gather: tüm parçalara paralel istekler, yerel arama kursları, sonra toplayıcı üzerinde k-way birleştirme.
Per-Shard Cursors: Belirteç, her parça üzerinde konumlar içerir.
Sınırlı fan çıkışı Adım başına parça sayısını sınırlayın (oran sınırlaması/zaman aşımı bütçesi).
6. Çoklu parça için 2 Jeton
Mağaza dizisi '{shard _ id, last_pos}'. Bir sonraki adımda, her aktif parça için devam edin ve küresel olarak sıralanmış sayfayı vererek tekrar tutun.
7) Protokol sözleşmeleri
7. 1 REST
İstek:
GET /v1/orders? limit=50&cursor=eyJ2IjoiMyIsInNvcnQiOiJjcmVh... (opaque)
Cevap:
json
{
"items": [ /... / ],
"page": {
"limit": 50,
"next_cursor": "eyJ2IjozLCJwb3MiOiJjcmVh...==",
"has_more": true
}
}
Öneriler:
- Üst sınırı olan 'limit' (örneğin, max = 200).
- 'next _ cursor' eksik ise 'has _ more = false'.
- GET idempotence, 'next _ cursor' olmadan verilen yanıtların hesaplanabilirliği (sabit filtreler ve anlık görüntü içeren ilk sayfa).
7. 2 GraphQL (Röle yaklaşımı)
Tipik 'bağlantı' sözleşmesi:graphql type Query {
orders(first: Int, after: String, filter: OrderFilter): OrderConnection!
}
type OrderConnection {
edges: [OrderEdge!]!
pageInfo: PageInfo!
}
type OrderEdge {
node: Order!
cursor: String! // opaque
}
type PageInfo {
hasNextPage: Boolean!
endCursor: String
}
'crossor' opak ve imzalı olmalı; HMAC olmadan "ham Base64 (id)" kullanmayın.
7. 3 gRPC
'Page _ size've' page _ token 'kullanın:proto message ListOrdersRequest {
string filter = 1;
int32 page_size = 2;
string page_token = 3; // opaque
}
message ListOrdersResponse {
repeated Order items = 1;
string next_page_token = 2; // opaque bool has_more = 3;
}
7. 4 Konu ve WebSockets
Sürekli bantlar için: imleç "son görülen ofset/ts'olarak.
Yeniden bağlanma sırasında 'resume _ from' desteği:json
{ "action":"subscribe", "topic":"orders", "resume_from":"2025-10-31T12:00:00Z#987654321" }
8) Önbelleğe alma, Ön yükleme, CDN
Sabit filtreli ilk sayfa için ETag/If-None-Match.
Genel listeler için kısa bir TTL (örneğin, 5-30 s) ile önbellek kontrolü.
Prefetch: return 'next _ cursor've ipuçları (' Link: rel = "next" '), istemci bir sonraki sayfayı önceden yükleyebilir.
Varyasyonlar: Önbellek kısmının anahtarı olarak 'filter/sort/locale/tenant' seçeneğini düşünün.
9) Yük yönetimi ve sınırlandırma
Üst sınır 'limit', örn. 200.
Sunucu tarafı geri baskı: istek süresi> bütçe ise, yanıttaki 'sınırı' azaltın (ve istemciye gerçek sayfa boyutunu açıkça söyleyin).
Kullanıcı/belirteç/kiracı başına oran sınırları.
Zaman aşımı/Yeniden deneme: üstel duraklatma, idempotent tekrarlanan istekleri.
10) UX yönleri
Numaralandırmaya karşı kaydırma: sonsuz kaydırma - imleçler; Sayı sayfaları - ofset (ancak verileri güncellerken yanlışlığı açıklayın).
Yerine dön düğmesi: İstemci imleci yığınını saklayın.
Boş sayfalar: 'Has _ more = false', Diğer düğmesini gösterme.
Kararlı sınırlar: Tam 'toplam'ı yalnızca ucuzsa gösterin (aksi takdirde yaklaşık bir' yaklaşım _ toplam ').
11) Test ve kenar durumları
Denetim listeleri:- Kararlı sıralama: Aynı't'lere sahip öğeler "göz kırpmaz".
- Ekler/Siler - Kopyalar sayfa kesişiminde görünmez.
- Sayfalar arasındaki filtreleri değiştirin: Belirteç eski/uyumsuz olarak reddedilmelidir.
- Token TTL: Geçerlilik süresi dolduktan sonra geçerli hata.
- Büyük derinlik: Gecikme doğrusal olarak büyümez.
- Multishard: doğru birleştirme sırası, açlık yokluğu "yavaş" parçalar.
python
Generate N entries with random inserts between calls
Verify that all pages are merged = = whole ordered fetch
12) Gözlemlenebilirlik ve SLO
Metrikler:- Sayfa uzunluğuna göre 'list _ request _ latency _ ms' (P50/P95/P99).
- 'search _ index _ hit _ ratio' (örten indeks tarafından bırakılan isteklerin oranı).
- 'next _ cursor _ invalid _ rate' (doğrulama/TTL/imza hataları).
- 'merge _ fanout' (sayfa başına dahil olan parça sayısı).
- 'duplicates _ on _ boundary've' gaps _ on _ boundary '(istemci telemetrisinde algılama).
- Günlüklerdeki 'cursor _ id', maske yükünü ilişkilendirin.
- Etiket aralıkları: 'page _ size', 'source _ shards', 'db _ index _ used'.
- Kullanılabilirlik: 99. 'Liste' yöntemlerinde %9.
- Gecikme: Yerel bir şarjda 'page _ size <= 50' için <200 ms P95.
- Belirteç hatası: <0. Toplam çağrı sayısının %1'i.
13) Geçişler ve birlikte çalışabilirlik
Belirteçte 'v'yi etkinleştirin ve N haftalarının eski sürümlerini destekleyin.
Sıralama tuşlarını değiştirirken - imleç olmadan yeni bir liste yapmak için bir istemle "yumuşak'bir hata '409 Çakışma' gönderin.
Felaket durumu (tüm belirteçlerin kükremesi): 'signing _ key _ id' seçeneğini değiştirin ve eskileri reddedin.
14) Uygulama örnekleri
14. 1 Belirteç üretimi (pseudocode)
python payload = json. dumps({...}). encode()
compressed = zlib. compress(payload)
mac = hmac_sha256(signing_key, compressed)
token = base64url_encode(mac + compressed)
14. 2 Belirteç doğrulama
python raw = base64url_decode(token)
mac, compressed = raw[:32], raw[32:]
assert mac == hmac_sha256(signing_key, compressed)
payload = json. loads(zlib. decompress(compressed))
assert now() - payload["issued_at"] < payload["ttl_sec"]
assert payload["filters"] == req. filters
14. 3 Bileşik anahtarla sorgu isteyin
sql
-- Page # 1
SELECT FROM feed
WHERE tenant_id =:t
ORDER BY ts DESC, id DESC
LIMIT:limit;
-- Next pages, continued after (ts0, id0)
SELECT FROM feed
WHERE tenant_id =:t
AND (ts <:ts0 OR (ts =:ts0 AND id <:id0))
ORDER BY ts DESC, id DESC
LIMIT:limit;
15) Güvenlik ve uyumluluk
PII'nin türetilebileceği belirteçlere ham alanlar dahil etmeyin.
TTL'yi imzalayın ve sınırlayın.
Belirteçleri kullanıcılar arasında dayanılmaz hale getirmeye çalışın (yükte 'alt/kiracı/roller' yazın ve doğrulama sırasında kontrol edin).
Sadece belirteç hash'lerini kaydedin.
16) Sık hatalar ve anti-desenler
İmleç olarak Base64 (id): kolay taklit etmek/almak, sıralamayı değiştirirken sözleşmeyi bozar.
Tie-breaker yok: 'ORDER BY ts DESC''id 'olmadan - kopyalar/sıçramalar.
Belirteci geçersiz kılmadan sayfalar arasındaki filtreleri değiştirin.
Derin ofset: Yavaş ve tahmin edilemez.
Sürüm ve TTL olmadan belirteçler.
17) Mini kontrol listesi uygulaması
1. Sıralamayı tanımlayın ve benzersiz bir tie-breaker ekleyin.
2. Bu sipariş için bir genişleme dizini oluşturun.
3. Model seçin: ara + opak belirteç.
4. Belirteç imzalama (ve gerekirse şifreleme) uygulayın.
5. TTL'yi ve sürümleri bırakın.
6. Formüle ve belge 'has _ more', 'next _ cursor' sözleşmeleri.
7. Bir çapraz parça şeması (gerekirse) ve k-yolu birleştirme düşünün.
8. Metrikler, uyarılar ve SLO'lar ekleyin.
9. Özellik tabanlı sayfa kenarlıklarını testlerle kaplayın.
10. Belirteçler için göç stratejisini açıklayın.
18) Bir yaklaşım seçmek için kısa öneriler
"Sayfa numarası've yaklaşık toplamın önemli olduğu dizinler/aramalar: 'OFSET/LIMIT' + önbellek diyelim; Bu toplam yaklaşık rapor.
Akışlar, analizler, derin listeler, yüksek RPS: yalnızca imleç/arama.
Shardy/dağıtılmış koleksiyonlar: parça başına imleçler + birleştirme belirteci.
Threads/CDC: özgeçmiş ile ofset/ts olarak imleçler.
19) API sözleşmesi örneği (özet)
'GET/v1/öğeleri? limit = 50 & imleç =... '
Cevap her zaman 'sayfa içerir. Limit ',' sayfası. has_more', isteğe bağlı 'sayfa. next_cursor'.
İmleç opak, imzalı, TTL ile.
Sıralama deterministik 'ORDER BY created_at DESC, id DESC'dir.
Değişim davranışını ayarla-Öğeler imlece göre "geri gitmez".
Metrikler ve hatalar standartlaştırılmıştır: 'invalid _ cursor', 'expired _ cursor', 'mismatch _ filters'.
Bu makale, büyük verilerde bile hızlı, öngörülebilir ve güvenli, dağınık ve aktif olarak değişen kayıt kümeleri tasarlamak için mimari ilkeler ve hazır desenler sunmaktadır.