İmleç Bazlı Sayfalama vs Ofset Sayfalama: API'niz Hangisini Kullanmalı?

İmleç tabanlı sayfalama ile ofset tabanlı sayfalama karşılaştırması: sayfa kayması, derin ofset maliyeti, anahtar kümesi SQL, Stripe ve Slack örnekleri ve Apidog'da her ikisinin nasıl test edileceği.

INEZA Felin-Michel

INEZA Felin-Michel

31 August 2026

İmleç Bazlı Sayfalama vs Ofset Sayfalama: API'niz Hangisini Kullanmalı?

Kurumsal İçin Apidog

Şirket İçi (On-Premises) Dağıtım

SSO ve RBAC

SOC 2 Uyumlu

Apidog Enterprise'ı Keşfedin

Her liste bitiş noktası eninde sonunda aynı soruyla karşılaşır: 2 milyon siparişi bir istemcinin adım adım gezebileceği sayfalara nasıl bölersiniz? Offset paginasyonu seçerseniz basit SQL ve kullanıcıların anlayabileceği sayfa numaraları elde edersiniz. İmleç tabanlı paginasyonu seçerseniz istikrarlı sonuçlar ve herhangi bir derinlikte tutarlı gecikme elde edersiniz, ancak "sayfa 47'ye git" özelliğinden vazgeçersiniz.

Çoğu ekip offset'i seçer çünkü her öğreticide varsayılan budur. Ardından siparişler tablosu birkaç milyon satıra ulaştığında, 4.000. sayfa zaman aşımına uğramaya başlar ve kullanıcılar kaydırırken aynı kaydı iki kez gördüklerini bildirirler. Bu rehber, her iki stilin nasıl çalıştığını, offset'in nerede sorun çıkardığını, Stripe ve Slack'in neden imleçleri kullandığını ve Apidog'da zincirleme isteklerle her iki stilin nasıl test edileceğini kapsar. Sonunda, bitiş noktanıza hangisinin tam olarak uyduğunu bileceksiniz.

Önce daha geniş resmi görmek isterseniz, API paginasyon rehberimiz her stratejiyi yan yana kapsar. Bu makale, en önemli iki tanesine derinlemesine odaklanır.

Offset paginasyon nasıl çalışır

Offset paginasyon doğrudan SQL'e eşlenir. İstemci bir sayfa numarası ve bir sayfa boyutu gönderir; sunucu bunları LIMIT ve OFFSET'e çevirir.

SELECT id, customer_id, total_cents, created_at
FROM orders
ORDER BY created_at DESC
LIMIT 25 OFFSET 50;

Bu sorgu, sipariş listenizin sayfa başına 25 satırla 3. sayfasını döndürür. İstek şöyle görünür:

GET /v1/orders?page=3&per_page=25

Ve tipik bir yanıt:

{
  "data": [
    {
      "id": "ord_8821",
      "customer_id": "cus_1932",
      "total_cents": 4599,
      "created_at": "2026-08-30T14:22:07Z"
    }
  ],
  "page": 3,
  "per_page": 25,
  "total": 1848203,
  "total_pages": 73929
}

Cazibesi ortada. İstemciler herhangi bir sayfaya atlayabilir. Sunucu toplam sayıyı döndürebilir. Herhangi bir geliştirici bunu bir öğleden sonra inşa edebilir. Küçük bir yönetici tablosu için bu doğru bir yaklaşımdır ve REST API'lerinde paginasyon hakkındaki adım adım rehberimiz, tam bir offset yapısını anlatmaktadır.

Ancak offset, iki yapısal sorun barındırır ve bunların hiçbiri geliştirme aşamasında ortaya çıkmaz. Her ikisi de üretimde kendini gösterir.

Problem 1: sayfa kayması

Offset, sıralanmış sonucun en üstünden satırları sayar. İstemcinin hangi satırları zaten gördüğü hakkında hiçbir şey bilmez. Bu nedenle, istekler arasında satırlar eklendiğinde veya silindiğinde, sayfalar istemcinin altından kayar.

Diyelim ki bir kullanıcı en yeniye göre sıralanmış siparişlerin 1. sayfasını (1'den 25'e kadar satırlar) yüklüyor. Okurken 3 yeni sipariş geliyor. OFFSET 25 olan 2. sayfayı istiyorlar. İlk yanıttaki 23, 24 ve 25 numaralı satırlar şimdi 26'dan 28'e kadar pozisyonlara itilmiş oluyor. Kullanıcı onları tekrar görüyor. Yinelenenler.

Silme işlemi bunu tersine çevirir. Kullanıcı okurken 1. sayfadan 3 satır kaldırın ve OFFSET 25 şimdi kullanıcının hiç görmediği 3 satırı atlar. Sessiz veri kaybı ve kimse bir hata almaz.

Kimsenin gerçek zamanlı olarak kaydırmadığı aylık bir rapor için kayma zararsızdır. Bir etkinlik akışı, bir senkronizasyon bitiş noktası veya yazma işlemleri devam ederken bir betiğin sayfa sayfa gezdiği herhangi bir şey için, kayma yinelenen veya eksik kayıtlar anlamına gelir. Tüketiciler bunu fark eder.

Problem 2: derin offset'ler atladıkları her şeyi tarar

OFFSET 500000, 500.001. satıra ışınlanmaz. Veritabanı dizini yarım milyon girdiye kadar tarar, bunları atar ve sonra sizin 25 satırınızı döndürür. Maliyet derinlikle doğrusal olarak artar: n offset olmak üzere O(n).

Somut sayılar bunu gerçek kılar. 2 milyon satırlı bir Postgres sipariş tablosunda ve created_at üzerinde bir dizinle:

Markus Winand'ın Use The Index, Luke'taki offset'siz yazısı, bu maliyeti sorgu planlarıyla göstermekte ve tamamını okumaya değerdir. Üretimdeki desen, yüksek offset istekleri tarafından domine edilen yavaş sorgu günlükleridir; genellikle genel API'nizin her sayfasını özenle gezen bir tarayıcıdan gelirler. Tek bir istemci ve p99'unuz iki katına çıkar.

İmleç tabanlı paginasyon nasıl çalışır

Anahtar kümesi paginasyonu olarak da adlandırılan imleç tabanlı paginasyon, satır sayacını kaldırır. İstemci "50 satırı atla" yerine "bana bu belirli kayıttan sonraki satırları ver" der. İmleç, istemcinin gördüğü son satırı tanımlar, böylece sunucu doğrudan bir sonraki bloğa geçebilir.

SQL, OFFSET yerine sıralama anahtarında bir satır karşılaştırması kullanır:

SELECT id, customer_id, total_cents, created_at
FROM orders
WHERE (created_at, id) < ('2026-08-30T14:22:07Z', 'ord_8821')
ORDER BY created_at DESC, id DESC
LIMIT 25;

İki sütunlu karşılaştırmaya dikkat edin. Yalnızca created_at benzersiz değildir; iki sipariş aynı milisaniyede gelebilir ve benzersiz olmayan bir sıralama anahtarı, sayfa sınırlarında satırların atlanması veya tekrarlanması anlamına gelir. id'yi bir beraberlik bozucu olarak eklemek, sıralamayı eksiksiz ve paginasyonu tam yapar. (created_at, id) üzerinde bir bileşik dizinle, veritabanı doğrudan sınıra gider ve 25 girdi okur. Sayfa 1 ve sayfa 60.000 aynı maliyete sahiptir.

Ancak API bu ham değerleri açığa çıkarmamalıdır. Gerçek uygulamalar, sıralama anahtarını opak bir belirteç olarak, genellikle base64 ile kodlar:

GET /v1/orders?limit=25&cursor=eyJjcmVhdGVkX2F0IjoiMjAyNi0wOC0zMFQxNDoyMjowN1oiLCJpZCI6Im9yZF84ODIxIn0

Opaklık, kendi başına bir karmaşıklık değil, bir tasarım kararıdır. İmleci ayrıştıramayan istemciler URL'leri manuel olarak oluşturamaz, bu da sıralama anahtarını değiştirmekte, bir parça ipucu eklemekte veya depolama motorlarını kimseyi bozmadan değiştirmekte özgür bırakır. Sözleşme "size verdiğimizi geri verin"den ibaret olur, fazlası değil.

Takas: 47. sayfa yok. Bir imleç sadece "bu satırdan sonra" bilgisini bilir, bu yüzden istemciler her seferinde bir sayfa ileri (veya önceki bir imleç verirseniz geri) ilerler. Toplam sayılar da ücretsiz gelmez; sayma ayrı bir sorgudur. Veri setinin kendisi çok büyük olduğu tasarımlar için, milyonlarca kayıt için API paginasyonu tasarlama rehberimiz ölçeklendirme tarafını daha derinlemesine kapsar.

Bir bakışta ödünleşmeler

Boyut Offset paginasyonu İmleç tabanlı paginasyon
Herhangi bir sayfaya atlama Evet, herhangi bir sayfa numarası Hayır, sadece sıralı gezinme
Toplam sayı / sayfa sayısı Dahil etmesi ucuz Ayrı sayım sorgusu
Derin sayfa performansı O(n), derinlikle kötüleşir Her derinlikte sayfa başına O(1)
Yazma işlemleri altında kararlılık Kaymalar: tekrarlar ve boşluklar Stabil, bir satıra sabitlenmiş
Geliştirme maliyeti Önemsiz Orta: kodlama, beraberlik bozucular, dizin tasarımı
Sıralama gereksinimleri Herhangi bir ORDER BY çalışır Benzersiz, dizinlenmiş bir sıralama anahtarı gerektirir
Sayfa URL'lerini önbelleğe alma Kolay, URL'ler tahmin edilebilir Daha zor, imleçler her gezinmede değişir
İstemci karmaşıklığı Düşük Düşük, zarf temizse

Bu tablodaki bir incelik vurgulanmayı hak ediyor: imleç paginasyonu deterministik bir sıralama gerektirir. Eğer bitiş noktanız istemcilerin status gibi değiştirilebilir, benzersiz olmayan bir sütuna göre sıralamasına izin veriyorsa, anahtar kümesi mantığı hızla acı verici hale gelir. Offset özensiz sıralamayı tolere eder; imleçler bunu cezalandırır.

Hangisini seçmelisiniz?

Stili, verinin nasıl tüketildiğine göre eşleştirin.

Yönetici tabloları ve panoları: offset. Birkaç bin satırlı dahili araçlar, sayfa numaralarına tıklayan insanlar ve görünür "1.848 sonuç" sayısı. Kayma önemli değil, derinlik sığ kalır ve sayfaya atlama gerçek bir özelliktir. Offset, geliştirme maliyetinde kazanır.

Sonsuz kaydırma akışları: imleç. Kimse bir akışın 47. sayfasına atlamaz. Kullanıcılar sadece "daha fazla" yükler, yazma işlemleri sürekli devam eder ve tekrarlar görünür ve utanç vericidir. Bu, ders kitabı niteliğinde bir imleç durumudur.

Herkese açık API'ler: imleç. Tüketicilerinizi kontrol edemezsiniz. Birisi her sayfayı gezen bir döngü yazacaktır ve offset ile derin sayfalar sabaha karşı 3'te sizin sorununuz haline gelir. İmleçler her sayfayı ucuz tutar ve opak belirtecin arkasındaki iç mekanizmaları geliştirmenize olanak tanır. REST API paginasyon rehberimiz URL ve başlık kurallarını ayrıntılı olarak kapsar.

Dışa aktarımlar ve senkronizasyon işleri: imleç. Tüm 2 milyon siparişi çeken bir toplu işin iki garantisi olması gerekir: eşzamanlı yazmalara rağmen kaçırılan satır olmaması ve sayfa başına sabit maliyet. Offset bunların hiçbirini sağlamaz. Bir imleç ayrıca, iş 1.4 milyonuncu satırda durduğunda size ücretsiz bir devam noktası sunar.

Dürüst bir genel kural: küçük, insanlar tarafından gezilen, sayım ağırlıklı arayüzler için offset; büyük, canlı veya herkese açık her şey için imleçler.

Gerçek API'ler bunu nasıl ele alır

Stripe tamamen imleç tabanlıdır. Her liste bitiş noktası starting_after (bir nesne kimliği) ve limit kabul eder ve yanıtlar has_more içerir. Bir sonraki ücret sayfasını almak için, aldığınız son ücretin kimliğini geçirirsiniz. Stripe paginasyon dokümanları bu deseni gösterir; yazma hacimlerinde kasıtlı bir eksiklik olan hiçbir yerde toplam sayının bulunmadığına dikkat edin.

GitHub'ın REST API'si çoğu bitiş noktasında hala page ve per_page'i açığa çıkarır, Link başlıkları sonraki ve son sayfalara işaret eder. Ancak GitHub paginasyon dokümanlarını dikkatlice okuyun: istemcilere sayfa URL'leri oluşturmak yerine Link başlığını kelimesi kelimesine takip etmelerini söylerler ve daha yeni bitiş noktaları imleçlere geçmiştir, çünkü devasa depolarda derin offset gezintileri zarar vericidir.

Slack Web API'sini imleç paginasyonuna geçirdi ve şimdi tüm yeni yöntemlerin kullandığı yaklaşım olarak işaretliyor. conversations.history gibi yöntemler response_metadata.next_cursor döndürür ve boş bir imleç dizgisi, Slack paginasyon dokümanlarında açıklandığı gibi sona ulaştığınız anlamına gelir.

Üç yüksek trafikli API ve gidiş yönü tek bir yola doğru: imleçlere.

Yanıt zarfını tasarlama

Bir imleç API'si, zarfı üzerinde yaşar veya ölür. Onu sıkıcı ve tahmin edilebilir tutun:

{
  "data": [
    {
      "id": "ord_8846",
      "customer_id": "cus_2201",
      "total_cents": 12900,
      "created_at": "2026-08-30T16:01:44Z"
    }
  ],
  "has_more": true,
  "next_cursor": "eyJjcmVhdGVkX2F0IjoiMjAyNi0wOC0zMFQxNjowMTo0NFoiLCJpZCI6Im9yZF84ODQ2In0"
}

Dört kural onu sağlamlaştırır:

Her iki stili Apidog'da test etme

Paginasyon hataları sınırlarda saklanır: son sayfa, boş sayfa, çapa satırı silinmiş imleç. Manuel tıklama bunları yakalamaz, ancak zincirleme bir test senaryosu yakalar ve Apidog iş akışındaki yerini burada kazanır.

İmleç bitiş noktaları için, iki adımlı bir test senaryosu oluşturun:

  1. Bitiş noktasını çağırın ve imleci çıkarın. İlk isteğe $.next_cursor JSONPath'li bir işlem sonrası ekleyin ve bunu nextCursor gibi bir değişkende saklayın. Apidog, JSONPath'i doğrudan yanıt panelinden kopyalamanıza olanak tanır; tam anlatım JSONPath ile iddialar ayarlama ve değişkenler çıkarma bölümündedir.
  2. Sonraki sayfa isteğini döngüye alın. İkinci bir isteği ForEach veya döngü adımına sarın, {{nextCursor}}'ı imleç parametresi olarak geçirin, her yinelemede $.next_cursor'ı yeniden çıkarın ve has_more yanlış olduğunda çıkın. Her geçişte önceki sayfadan hiçbir id'nin tekrarlanmadığını ve sayfa boyutunun asla limit'i aşmadığını doğrulayın.

Offset bitiş noktaları için, aynı yapı bir sayaç değişkeniyle uygulanır: page'i artırın, son sayfaya kadar data uzunluğunun per_page'e eşit olduğunu doğrulayın ve gezinme boyunca total'in tutarlı kaldığını doğrulayın.

Ardından kenar durumlarını kendi adımları olarak ekleyin, her biri açık doğrulamalarla birlikte:

Senaryo yerel olarak geçtiğinde, her birleştirme işleminde CI'da çalıştırın. Apidog'u ücretsiz indirin ve döngüler ve doğrulamalar dahil olmak üzere tam imleçle gezinme senaryosunu yarım saatten kısa sürede çalıştırabilirsiniz.

SSS

İmleç paginasyonu her zaman daha mı iyidir?

Hayır. Kullanıcıların sayfa numaralarına, toplam sayılara ve mütevazı bir veri kümesi üzerinde rastgele erişime ihtiyaç duyduğu durumlarda (ki bu çoğu dahili yönetici aracını tanımlar) offset daha iyi uyar. Veri kümesi büyük olduğunda, yazma işlemleri sık olduğunda veya API herkese açık olduğunda imleçler daha iyidir. Başarısızlık modu, genel bir liste bitiş noktası için varsayılan olarak offset kullanmak ve lansmandan sonra O(n) maliyetini keşfetmektir.

İmleç paginasyonu ile toplam sayıyı nasıl alırım?

Aynı filtrelerle ayrı bir SELECT COUNT(*) çalıştırın; bunu ayrı bir bitiş noktası olarak veya include_count=true gibi isteğe bağlı bir sorgu parametresi olarak yapabilirsiniz. Bunu agresif bir şekilde önbelleğe alın; her dakika yenilenen yaklaşık bir sayım neredeyse her kullanıcı arayüzünü tatmin eder. Stripe, toplam sayıları tamamen atlar ki bu da istemcilerin bunlara ne kadar sıklıkla gerçekten ihtiyaç duyduğunu gösterir.

Tek bir bitiş noktasında her iki paginasyon stilini de sunabilir miyim?

Yapabilirsiniz ve GitHub geçişi sırasında bunu etkin bir şekilde yapar, ancak yeni API'lerde bundan kaçının. İki stil, iki ayrı kenar durum seti, iki test matrisi ve hangisini kullanacağı konusunda istemci karışıklığı anlamına gelir. Bitiş noktası başına birini seçin. Sözleşmeyi sıfırdan tasarlıyorsanız, REST API paginasyon rehberimizdeki desenler, tüm yüzeyinizde parametre adlandırmasını tutarlı tutacaktır.

İmlecin çapa satırı silinirse ne olur?

Anahtar kümesi paginasyonu ile hiçbir şey bozulmaz. WHERE (created_at, id) < (?, ?) karşılaştırması çapa satırının var olmasını gerektirmez; sınır konumuna gider ve devam eder. Bu, "satır araması olarak imleç" tasarımlarına göre gerçek bir avantajdır ve bir tüketicinin sizin için bulmasından önce Apidog test senaryonuzda doğrulamaya değer kenar durumudur.

API Tasarım-Öncelikli Yaklaşımı Apidog'da Uygulayın

API'leri oluşturmanın ve kullanmanın daha kolay yolunu keşfedin