Offset Pagination — ofset sayfalama — HTTP API üzerinden verilerin sayfa sayfa yüklenmesi yöntemi. İstemci offset (başlangıçtan itibaren ofset) ve limit (sayfa boyutu) parametrelerini gönderir, sunucu offset konumundan itibaren kayıtları döndürür. REST API Tutorial'a göre, bu yaklaşım uygulama basitliği sayesinde RESTful hizmetlerinde yaygın olarak kullanılır. Ancak büyük veri hacimlerinde, istenen konuma kadar tüm tablonun taranması nedeniyle ofset sayfalama performans kaybeder.
Önemli Noktalar
Offset Pagination, istemci isteğinin iki parametre içerdiği bir veri sayfalama yöntemidir: offset (kaç kaydın atlanacağı) ve limit (kaç kaydın döndürüleceği). Sunucu OFFSET ve LIMIT ile bir SQL sorgusu yürütür, belirtilen sayıda satırı atlar ve sabit boyutlu bir sonuç kümesi döndürür.
Yöntem, ilişkisel veritabanlarında sayfa gezintisini düzenlemenin en basit yolu olarak ortaya çıkmış ve REST mimarisinin gelişmesiyle birlikte HTTP API'lerine aktarılmıştır. Offset Pagination sunucuda durum depolaması gerektirmez — her istek bağımsızdır ve sorgu için gerekli tüm bilgileri içerir.
Postman (2025) API tasarım raporuna göre, ofset sayfalama, büyük veri kümelerindeki bilinen performans sınırlamalarına rağmen, genel REST API'lerinin %72'sinde kullanılmakta ve baskın standart haline gelmektedir.
Offset Pagination ile tipik bir REST isteği, sorgu parametreleri offset ve limit'i içerir. Yanıt, istenen sayfanın kayıt listesini ve gezinme arayüzü oluşturmak için meta verileri içerir.
limit parametresi döndürülen kayıt sayısını sınırlar ve sunucu ile istemciyi aşırı yükten korur. Veri karmaşıklığına bağlı olarak tipik limit değerleri sayfa başına 10 ila 50 kayıt arasındadır.
data class PageRequest(
val offset: Int,
val limit: Int
)
data class PageResponse<T>(
val items: List<T>,
val total: Int,
val hasMore: Boolean
)
fun RetrofitApi.fetchPage(request: PageRequest): Call<PageResponse<Item>>
Offset Pagination, OFFSET ve FETCH NEXT (veya MySQL/SQLite'da LIMIT) yapılarıyla bir SQL sorgusuna dönüştürülür. Veritabanı sunucusu tabloyu tarar, ofsete eşit sayıda satırı atlar ve sonraki limit satırlarını döndürür. Ofset ne kadar büyükse sorgu o kadar uzun sürer.
Performans sorunu, veritabanının doğrudan ofset konumuna atlayamamasından kaynaklanır — önceki tüm satırları okumalı ve atmalıdır. offset = 100000 ve limit = 20'de, DBMS 100.020 satır okur ve yalnızca 20 satır döndürür.
SQL — sunucunun ofset sayfalama yürüttüğü dil. PostgreSQL ve MySQL LIMIT kullanırken, SQL Server ve Oracle OFFSET...FETCH kullanır. Farklı DBMS'ler bu sorguyu farklı şekilde optimize eder, ancak temel tarama sorunu aynı kalır.
SELECT id, title, created_at
FROM articles
ORDER BY created_at DESC
OFFSET 100000 ROWS
FETCH NEXT 20 ROWS ONLY;
Veri tutarlılığı, dinamik kümelerle çalışırken Offset Pagination'ın ana dezavantajıdır. İki kullanıcı isteği arasında tablonun başına yeni bir kayıt eklenirse, mevcut tüm kayıtlar kayar. Kullanıcı kopyalar veya boşluklar görür.
limit = 20 ile 100 kayıtlık bir tablo düşünelim. Sayfa 1'de kullanıcı 1-20 kayıtlarını görür. Bir yönetici 5 yeni kayıt ekler. Sayfa 2'de kullanıcı beklenen 21-40 yerine 26-45 kayıtlarını görür — 21-25 kayıtları atlanır ve önceki kümedeki 21-25 kayıtları sayfa 1'de tekrarlanır.
Cursor-based sayfalama, geçerli sayfanın son kaydına bir işaretçi kullanan Offset Pagination'a bir alternatiftir. Sayısal bir ofset yerine, istemci alınan son kaydın tanımlayıcısını gönderir ve sunucu ondan sonraki N kaydı döndürür.
Cursor-based yaklaşım tutarlılık sorununu çözer: imleç konumu ekleme veya silme işlemlerinde değişmez çünkü imleç bir konum yerine belirli bir kaydı referans alır. Ancak uygulanması daha karmaşıktır — benzersiz sıralanabilir bir alan (genellikle ID veya zaman damgası) gerektirir.
| Parametre | Offset Pagination | Cursor-based Pagination |
|---|---|---|
| Basitlik | Yüksek — iki sayısal parametre | Orta — imleç kodlaması gerekli |
| Tutarlılık | Düşük — eklemelerde kopyalar | Yüksek — imleç değişikliklerden etkilenmez |
| Performans | Büyük ofsette düşer | Her hacimde sabit |
| Sayfaya atlama | Evet — herhangi bir sayfaya gidilebilir | Hayır — yalnızca sıralı gezinme |
| Uygun | <10K kayıt tabloları, sayfa numaralı UI | Akışlar, sonsuz kaydırma, büyük kümeler |
Yaklaşımlar arasındaki seçim, kullanıcının arayüz gereksinimlerine bağlıdır. Sayfa numaraları ve doğrudan atlama ile gezinme gerekiyorsa — Offset Pagination daha basittir. Sonsuz kaydırma veya haber akışları için imleçler tercih edilir.
Keyset sayfalama, OFFSET yerine WHERE kullanarak benzersiz bir anahtarda filtreleme yapılan cursor-based yaklaşımın bir çeşididir. SQL sorgusu WHERE id > lastId gibi bir koşul kullanır ve veritabanının atılan satırları taramadan bir dizin kullanmasına olanak tanır.
PostgreSQL Wiki'ye göre, keyset sayfalama büyük ofsetlerde ofset sorgularından 100-1000 kat daha hızlı çalışır çünkü dizin taraması tam tablo taramasının yerini alır. Dezavantajı, sıralı gezinme olmadan rastgele bir sayfaya atlayamamasıdır.
Offset Pagination, kullanıcının sayfa numarası arayüzüne ihtiyaç duyduğu küçük ve orta ölçekli veri kümeleri (10.000 kayda kadar) için idealdir. Tipik senaryolar arasında yönetim panelleri, sipariş listeleri ve sayfa tabanlı sayfalama ile filtrelenmiş kataloglar bulunur.
Mobil uygulamalar için, yeni eklemelerin nadir veya imkansız olduğu geçmiş verileri yüklerken ofset sayfalama uygundur — örneğin, kullanıcı sipariş geçmişi, tamamlanmış görev listeleri, işlem arşivleri. Bu senaryolarda tutarlılık sorunu ortaya çıkmaz.
Sosyal medya akışları, yorum listeleri, sohbetler ve sık eklemeli diğer dinamik kümeler için önerilmez. Bu durumlarda, boşluklar ve yinelenen kayıtlar kullanıcı deneyimini bozar ve istemcide ek yineleme önleme mantığı gerektirir.
Karma sayfalama, offset ve imleci birleştirir: ilk istek başlangıç sayfasını göstermek için offset kullanırken, sonraki istekler sonsuz kaydırma yüklemesi için imleç kullanır. Bu yaklaşım Instagram ve Twitter'da kullanılır; ilk sayfa imleç ile yüklenir, ancak önceki bir görünüme dönerken konumu hesaplamak için offset kullanılır.
Karma bir yaklaşım uygulamak, istemcide kullanıcının sanal konumunu depolamayı ve sunucuda iki sayfalama mekanizmasını koordine etmeyi gerektirir. Instagram Engineering bloguna göre, ekipleri ilk yükleme için offset'in yerini alan ek bir startCursor alanıyla cursor-based sayfalama kullanmaktadır.
Mobil uygulamalar, Android'de Retrofit/OkHttp ve iOS'ta URLSession/Combine ile birlikte Offset Pagination kullanır. Tipik desen, RecyclerView.OnScrollListener veya UICollectionView önceden getirme yoluyla listenin sonuna kaydırıldığında sonraki sayfayı yüklemektir.
Mobil istemcide ofset sayfalama uygulaması üç bileşen içerir: bir sayfalama yöneticisi (mevcut offset ve hasMore'u depolar), bir liste bağdaştırıcısı (öğeleri ve yükleme göstergesini görüntüler) ve bir depo (istekleri yürütür ve hataları işler). Android Jetpack, hem offset hem de cursor-based sayfalamayı kutudan çıkar çıkmaz destekleyen Paging 3 kütüphanesini sunar.
Paging 3, sayfalı veri yüklemesi için bir Android Jetpack kütüphanesidir. Ofset takibi, yükleme durumu yönetimi ve kaydırma sırasında otomatik önceden getirme dahil olmak üzere sayfalama mantığını kapsüller. PagingSource, sonraki ve önceki sayfalar için anahtarlar tanımlar.
class OffsetPagingSource(
private val api: ApiService,
private val limit: Int = 20
) : PagingSource<Int, Item>() {
override suspend fun load(
params: LoadParams<Int>
): LoadResult<Int, Item> {
val offset = params.key ?: 0
return try {
val response = api.getItems(offset, limit)
LoadResult.Page(
data = response.items,
prevKey = null,
nextKey = if (response.hasMore) offset + limit else null
)
} catch (e: Exception) {
LoadResult.Error(e)
}
}
}
PagingSource, sayfa gezinmesi için prevKey ve nextKey anahtarlarını tanımlar. Ofset sayfalamada prevKey her zaman null'dur (geçmiş kaydedilmeden önceki sayfaya gidilemez), nextKey ise sunucu hasMore = false döndürene kadar her yüklemede limit kadar artar. Bu, mobil listeler için basit ve öngörülebilir bir modeldir.
Birinci hata — sıralama olmadan kayıt sırasına güvenmek. Offset Pagination, benzersiz bir alanda sabit ORDER BY sıralaması gerektirir. Bu olmadan, DBMS kayıtları rastgele sırada döndürebilir ve bu da sayfalar arasında rastgele kopyalara ve boşluklara yol açar.
İkinci hata — kullanıcı arayüzünde sayfa numarasını hesaplamak için offset kullanmak. page = offset / limit + 1 formülü yalnızca yüklemeler arasında hiçbir kayıt silinmemiş veya eklenmemişse çalışır. Dinamik verilerde sayfa numarası hatalı hale gelir ve kullanıcı yanlış bilgiler görür.
Üçüncü hata — büyük offsetli sorguların zaman aşımlarını göz ardı etmek. 100.000'in üzerinde offsette sorgu onlarca saniye sürebilir, arayüzü bloke edebilir ve sunucu kaynaklarını tüketebilir. API düzeyinde maksimum offset değeri (örneğin 10.000) belirlenmesi ve büyük hacimler için cursor-based sayfalama kullanılması önerilir.
Dördüncü hata — yanıta toplam sayıyı eklememek. Toplam kayıt sayısı olmadan istemci sayfa sayısını görüntüleyemez ve numaralı sayfalama uygulayamaz. Ancak büyük tablolarda COUNT(*) maliyetlidir — 100.000 kaydı aşan kümeler için yaklaşık tahminler kullanın veya maksimum toplam değeri sınırlayın.
Sıkça sorulan sorular
Offset, kayıtları atlamak için sayısal bir ofset kullanırken, cursor önceki sayfanın son kaydına bir işaretçi kullanır. Offset uygulaması daha basittir ancak eklemelerde kopyalar ve büyük ofsetlerde performans kaybı yaşar. Cursor, veri değişikliklerinde kararlıdır.
Ofset sayfalama, tam tablo taraması nedeniyle 10.000 kaydın üzerindeki ofsetlerde verimsizdir. Ayrıca istekler arasında yeni kayıtların göründüğü dinamik kümeler (akışlar, sohbetler) için uygun değildir — kullanıcı gezinme sırasında boşluklar ve yinelenen kayıtlar görür.
İdeal limit, kayıt boyutuna ve ağ hızına bağlıdır — sayfa başına 10 ila 50 öğe. Büyük resimli listeler için limit = 10-15 kullanın; metin verileri için 20-50. İstemcinin, sunucuda maksimum sınırla (genellikle 100) kendi limitini belirlemesine her zaman izin verin.
Kopyalarla başa çıkmak için benzersiz kimliğe göre istemci tarafında yineleme önleme kullanın, benzersiz bir alanda kararlı sıralama uygulayın veya cursor-based sayfalamaya geçin. Android Paging 3, liste öğelerinin otomatik yineleme önlemesi için key desteği sağlar.
Evet, GraphQL, Relay spesifikasyonu cursor-based yaklaşımı önermesine rağmen, sorguda offset ve limit argümanları aracılığıyla ofset sayfalamayı destekler. Apollo GraphQL ve Relay kütüphaneleri, otomatik sayfa durumu yönetimi ile ofset sayfalama için yerleşik destek sunar.
Özet
Anahtar teslim bir mobil uygulama geliştireceğiz
IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.
Ayrıca okuyun