Sayfalama, mobil uygulamalarda ve web servislerinde büyük kayıt kümeleriyle çalışmak için kullanılan, verileri sayfa sayfa yükleme tekniğidir. Android Developers Documentation (2025)'a göre, doğru sayfalama uygulaması API yükünü azaltır, trafikten tasarruf sağlar ve kullanıcı deneyimini iyileştirir. Sayfa sayfa yükleme, uygulamanın tüm verilerin yüklenmesini beklemeden içeriği kademeli olarak görüntülemesine olanak tanır.
Önemli Noktalar
Sayfalama (Latince pagination — sayfa bölme), büyük bir veri kümesini sıralı parçalara (sayfalara) ayırma tekniğidir. Mobil uygulamalarda, sayfalama mesaj listeleri, haber akışları, ürün katalogları, sipariş geçmişi ve potansiyel olarak sınırsız sayıda kayıt içeren herhangi bir koleksiyon yüklenirken kullanılır.
Sayfalama olmadan, uygulama tüm verileri aynı anda yüklemek zorunda kalır, bu da uzun bekleme sürelerine, yüksek trafik tüketimine ve zayıf cihazlarda kararsız performansa yol açar. Sayfalama içeren bir API isteği verilerin yalnızca bir kısmını ve bir sonrakini yüklemek için meta bilgileri döndürür — böylece uygulama aldığı bilgi miktarını kontrol eder.
Sayfalamanın temel ölçütleri: sayfa boyutu (page size) — sayfa başına kayıt sayısı (genellikle 10–50) ve sayfa numarası veya imleç — küme içindeki mevcut konumun göstergesi. Sayfa boyutu seçimi veri türüne bağlıdır: kompakt öğeler (isimler) için 20–30 yeterlidir, resimli kartlar için 10–15.
Mobil cihazların sınırlı kaynakları vardır: RAM, işlemci hızı ve trafik limitleri. Sayfalama üç temel görevi çözer: bellek tüketiminin azaltılması (yalnızca görünür öğeler bellekte tutulur), ilk görüntülemenin hızlandırılması (ilk bölüm tüm kümeye göre daha hızlı yüklenir) ve trafik tasarrufu (veriler yalnızca kullanıcı listeyi kaydırdığında yüklenir).
Her biri belirli görevleri çözen dört ana sayfalama türü vardır. Yöntem seçimi, veri tutarlılığı gereksinimlerine, API mimarisine, depolama türüne ve istemci ile sunucu taraflarında kabul edilebilir uygulama karmaşıklığına bağlıdır.
| Tür | Çalışma prensibi | Kararlılık | Büyük hacimlerde hız |
|---|---|---|---|
| Offset | SQL'de LIMIT + OFFSET | düşük | OFFSET büyüdükçe düşer |
| Cursor | WHERE id > last_id | yüksek | kararlı (O(log n)) |
| Keyset | WHERE key > last_key | yüksek | kararlı (O(log n)) |
| Time-based | WHERE created_at < last_time | orta | dizinle kararlı |
Offset sayfalama, basit uygulamanın önemli olduğu statik veya nadiren güncellenen veri kümeleri için uygundur. Cursor ve Keyset, sık eklemeler olan dinamik veriler içindir. Zamana dayalı sayfalama, kayıtların oluşturulma zamanına göre sıralandığı kronolojik akışlar içindir. GraphQL standardı Relay, önerilen tek yöntem olarak imleç sayfalamayı kullanır.
Offset sayfalama, sayfa tabanlı yüklemenin en basit türüdür. İstemci page ve limit (veya offset ve limit) parametrelerini gönderir ve sunucu SQL OFFSET ve LIMIT uygular. Örneğin, page=2, limit=20, 21'den 40'a kadar olan kayıtları döndürür. Bu yöntem sezgiseldir ve herhangi bir yığında uygulanması kolaydır.
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items")
async def get_items(
page: int = Query(default=1, ge=1),
limit: int = Query(default=20, le=100)
):
offset = (page - 1) * limit
items = await fetch_items(offset, limit)
total = await count_items()
return {
"items": items,
"total": total,
"page": page,
"pages": (total + limit - 1) // limit
}
Offset sayfalamanın ana dezavantajı, kayıp ve yinelenen kayıt sorunudur. İki istek arasında tabloya yeni kayıtlar eklenirse, OFFSET kayar: kullanıcı aynı kaydı iki kez görebilir veya yeni bir kaydı kaçırabilir. Bu, tutarlılığın önemli olduğu haber akışları ve sohbetler için kritiktir.
Diğer bir sorun, büyük OFFSET değerlerinde performans düşüşüdür. Veritabanı, sonucu döndürmeden önce ilk offset kaydı taramak ve atlamak zorundadır. offset=100000'de LIMIT 20 ile bile, sunucu taramada kayda değer zaman harcar. PostgreSQL ve MySQL, OFFSET büyüdükçe hızda doğrusal bir düşüş gösterir.
Offset sayfalama şunlar için en iyi seçim olmaya devam eder: yönetim panelleri (veriler nadiren değişir, sayfa gezintisi gereklidir), raporlar ve geçmiş günlükleri (verilerin sabit anlık görüntüsü), filtreli kataloglar (herhangi bir sayfaya gidilebilir). Offset ayrıca istemci taraflı uygulaması en kolay olanıdır — RecyclerView, Paging 3 ile kutudan çıkar çıkmaz destekler.
Keyset sayfalama, kayıtları filtrelemek için benzersiz bir anahtar (genellikle birincil anahtar) kullanır. OFFSET yerine sorgu WHERE id > last_seen_id kullanır. Bu, kayıt sayısından bağımsız olarak kararlı performans ve eklemelerde yinelenen kayıt olmamasını sağlar, çünkü yeni kayıtlar her zaman daha büyük bir id'ye sahiptir.
-- Offset sayfalama (sorunlu)
SELECT * FROM posts
ORDER BY id
LIMIT 20 OFFSET 100;
-- Keyset sayfalama (kararlı)
SELECT * FROM posts
WHERE id > 100
ORDER BY id
LIMIT 20;
Zamana dayalı sayfalama (veya zamana göre imleç), gezinme için created_at zaman damgasını kullanır. İstemci, yüklenen son kaydın zaman damgasını gönderir ve sunucu, bu zaman damgasından önce veya sonra oluşturulan kayıtları döndürür. Bu yöntem, kayıt sırasının yayınlanma zamanına göre belirlendiği sosyal ağlar ve haber akışlarında popülerdir.
Zamana dayalı sayfalamanın bir özelliği, iki kayıt aynı milisaniyede oluşturulursa yinelenen kayıtların oluşabilmesidir. Bu sorunu ortadan kaldırmak için, zamana dayalı bir anahtarı benzersiz bir id ile birleştirin: WHERE (created_at, id) < (last_time, last_id). Böyle bir bileşik imleç, her kaydın benzersizliğini ve kesin sıralamayı garanti eder.
Keyset sayfalama, benzersiz ve monoton artan bir değere (otomatik artan id, UUID v7) sahip bir sütun gerektirir. Zamana dayalı sayfalama, created_at içeren herhangi bir tablo için uygundur ancak ek yinelenen kayıt işleme gerektirir. Temel fark: Keyset herhangi bir ekleme işleminde kararlı bir şekilde çalışırken, zamana dayalı sayfalama aynı zaman damgalarına karşı hassastır.
Sayfalama türü seçimi, verilerin doğasına ve kullanıcı deneyimi gereksinimlerine bağlıdır. Aşağıda mobil geliştirmedeki tipik senaryolar için öneriler bulunmaktadır. Evrensel bir çözüm yoktur — her yöntemin optimal olduğu bir alan vardır.
Android Paging 3 kütüphanesi, PagingSource aracılığıyla tüm sayfalama türlerini destekler. Offset için — Int anahtarlı (page) PagingSource, Cursor için — String veya Long anahtarlı (cursor) PagingSource. PagingSource, yüklemeyi, önbelleğe almayı ve hatalarda yeniden denemeyi otomatik olarak yönetir.
class PostPagingSource(
private val api: PostApi
) : PagingSource<Long, Post>() {
override suspend fun load(
params: LoadParams<Long>
): LoadResult<Long, Post> {
val cursor = params.key ?: Long.MAX_VALUE
return try {
val response = api.getPosts(cursor, params.loadSize)
LoadResult.Page(
data = response.items,
prevKey = null,
nextKey = response.items.lastOrNull()?.id
)
} catch (e: Exception) {
LoadResult.Error(e)
}
}
override fun getRefreshKey(state: PagingState<Long, Post>): Long? {
return state.anchorPosition?.let {
state.closestItemToPosition(it)?.id
}
}
}
Sayfa boyutu, yükleme hızını ve performans algısını etkiler. Mobil uygulamalar için en uygun aralık sayfa başına 10–25 öğedir. 10'dan az, API'ye çok fazla istek ve kesintili kaydırmaya neden olur. 25'ten fazla, yavaş ağlarda ilk bölümün yavaş yüklenmesine neden olur.
Görseller ve videolar için, her öğe medyayı yüklemek için ek süre gerektirdiğinden sayfa boyutunu 5–10'a düşürün. Metin tabanlı listeler (yorumlar, günlükler) için boyut 30–50 kayda kadar artırılabilir. İstemcinin farklı ağ koşullarına uyum sağlayabilmesi için sayfa boyutunun API aracılığıyla yapılandırılabilir yapılması önerilir.
Sıkça Sorulan Sorular
Sayfalama, verileri hepsi bir anda değil, parçalar halinde yüklemektir. Bir kitap gibi: bir sayfayı okursunuz, sonra diğerine geçersiniz. Bir uygulamada bu, bir listeyi kaydırırken bir sonraki veri yığınının yüklendiği, tüm listenin yüklenmediği anlamına gelir, bu da trafik ve bellek tasarrufu sağlar.
Offset kayıtları sayar: “20'yi atla, sonraki 10'u döndür.” Yüklemeler arasında yeni bir kayıt eklenirse, numaralandırma kayar. Cursor, son kaydın benzersiz tanımlayıcısını kullanır: “ID = 100'den sonra 10 kayıt döndür.” Yeni kayıtlar konumu etkilemez.
Mobil uygulamalar için 10–25 öğe idealdir. Resimli listeler için 5–10, metin akışları için 20–30. Boyut, her öğenin ortalama boyutuna bağlıdır: öğe ne kadar ağırsa, hızlı görüntüleme için sayfa o kadar küçük olmalıdır.
Android Jetpack'ten Paging 3 kütüphanesini kullanın. Yükleme için PagingSource, reaktif akışlar için PagingData ve kaydırma sırasında otomatik yükleme için PagingDataAdapter sağlar. Kütüphane, özel bir PagingSource aracılığıyla Offset, Cursor ve Keyset sayfalamayı destekler.
Sonsuz kaydırma, listenin sonuna yaklaşıldığında yeni bir veri yığınının otomatik olarak yüklendiği bir UI modelidir. Sayfalama, verileri yığınlar halinde yükleme mekanizmasıdır ve sonsuz kaydırma bunu görüntülemenin bir yoludur. Bir alternatif ise “Daha fazla yükle” düğmesidir.
Ö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