Offset Pagination — ofsetli sahifalash — HTTP API orqali ma'lumotlarni sahifalab yuklash usuli. Mijoz offset (boshidan siljish) va limit (sahifa hajmi) parametrlarini uzatadi, server esa offset pozitsiyasidan yozuvlarni qaytaradi. REST API Tutorial ma'lumotlariga ko'ra, bu yondashuv soddaligi tufayli RESTful xizmatlarida keng qo'llaniladi. Biroq katta hajmdagi ma'lumotlarda offset-sahifalash jadvalni kerakli pozitsiyagacha to'liq skanerlash tufayli unumdorligini yo'qotadi.
Asosiy fikrlar
Offset Pagination — bu mijoz so'rovi ikkita parametrni o'z ichiga olgan ma'lumotlarni sahifalarga bo'lish usuli: offset (nechta yozuvni o'tkazib yuborish) va limit (nechta yozuvni qaytarish). Server OFFSET va LIMIT bilan SQL so'rovini bajaradi, belgilangan qatorlar sonini o'tkazib yuboradi va belgilangan o'lchamdagi natijalar to'plamini qaytaradi.
Usul relyatsion ma'lumotlar bazalarida sahifalar bo'ylab navigatsiyani tashkil etishning eng oddiy usuli sifatida paydo bo'lgan va REST arxitekturasining rivojlanishi bilan HTTP API ga o'tkazilgan. Offset Pagination serverda holatni saqlashni talab qilmaydi — har bir so'rov mustaqil va tanlash uchun barcha kerakli ma'lumotlarni o'z ichiga oladi.
Postman (2025) tomonidan API dizayni tadqiqotiga ko'ra, offset-sahifalash umumiy REST API larning 72% ida qo'llaniladi, bu esa uni katta hajmdagi ma'lumotlarda ma'lum unumdorlik cheklovlariga qaramay dominant standartga aylantiradi.
Offset Pagination bilan odatiy REST so'rovi offset va limit so'rov parametrlarini o'z ichiga oladi. Javob kerakli sahifa yozuvlari ro'yxati va navigatsiya interfeysini qurish uchun metama'lumotlarni o'z ichiga oladi.
Limit parametri qaytariladigan yozuvlar sonini cheklaydi va server va mijozni haddan tashqari yukdan himoya qiladi. Odatiy limit qiymatlari ma'lumotlarning murakkabligiga qarab sahifada 10 dan 50 gacha yozuvni tashkil qiladi.
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 va FETCH NEXT (yoki MySQL/SQLite da LIMIT) konstruksiyalari bilan SQL so'roviga aylantiriladi. Ma'lumotlar bazasi serveri jadvalni skanerlaydi, offset ga teng qatorlar sonini o'tkazib yuboradi va keyingi limit qatorini qaytaradi. Offset qanchalik katta bo'lsa, so'rov shunchalik uzoq davom etadi.
Unumdorlik muammosi ma'lumotlar bazasi to'g'ridan-to'g'ri offset pozitsiyasiga o'ta olmasligi bilan bog'liq — u barcha oldingi qatorlarni o'qib, tashlab yuborishi kerak. Offset = 100000 va limit = 20 da DBMS 100020 qatorni o'qib, faqat 20 tasini qaytaradi.
SQL — server offset-sahifalashni bajaradigan til. PostgreSQL va MySQL da LIMIT, SQL Server va Oracle da OFFSET...FETCH ishlatiladi. Turli DBMS lar bu so'rovni o'zicha optimallashtiradi, ammo skanerlashning asosiy muammosi bir xil bo'lib qoladi.
SELECT id, title, created_at
FROM articles
ORDER BY created_at DESC
OFFSET 100000 ROWS
FETCH NEXT 20 ROWS ONLY;
Ma'lumotlar izchilligi — dinamik to'plamlar bilan ishlashda Offset Pagination ning asosiy kamchiligi. Agar foydalanuvchining ikki so'rovi orasida jadval boshiga yangi yozuv qo'shilsa, barcha mavjud yozuvlar siljiydi. Foydalanuvchi dublikatlar yoki o'tkazib yuborishlarni ko'radi.
Limit = 20 bo'lgan 100 ta yozuvli jadvalni tasavvur qiling. 1-sahifada foydalanuvchi 1-20 yozuvlarni ko'radi. Administrator 5 ta yangi yozuv qo'shadi. 2-sahifada foydalanuvchi kutilgan 21-40 o'rniga 26-45 yozuvlarni ko'radi — 21-25 yozuvlari o'tkazib yuborilgan va oldingi to'plamdagi 21-25 yozuvlari 1-sahifada takrorlangan.
Cursor-based pagination — joriy sahifaning oxirgi yozuviga ko'rsatkichdan foydalanadigan Offset Pagination ga muqobil. Raqamli siljish o'rniga mijoz oxirgi olingan yozuvning identifikatorini uzatadi, server esa undan keyingi N yozuvni qaytaradi.
Cursor-based yondashuv izchillik muammosini hal qiladi: kursorning pozitsiyasi qo'shish yoki o'chirishlarda o'zgarmaydi, chunki kursor pozitsiyaga emas, balki aniq yozuvga ishora qiladi. Biroq uni tatbiq etish murakkabroq — noyob saralanadigan maydon talab qilinadi (odatda ID yoki timestamp).
| Parametr | Offset Pagination | Cursor-based Pagination |
|---|---|---|
| Soddalik | Yuqori — ikkita raqamli parametr | O'rta — kursorni kodlash talab qilinadi |
| Izchillik | Past — qo'shilganda dublikatlar | Yuqori — kursor o'zgarishlarga bog'liq emas |
| Unumdorlik | Offset oshgan sari pasayadi | Har qanday hajmda barqaror |
| Sahifaga sakrash | Ha — istalgan sahifaga o'tish mumkin | Yo'q — faqat ketma-ket navigatsiya |
| Mos keladi | <10K yozuv jadvallari, sahifa raqamlari bilan UI | Feed lar, cheksiz skroll, katta to'plamlar |
Yondashuvlar orasidagi tanlov foydalanuvchi interfeysi talablariga bog'liq. Agar sahifa raqamlari va to'g'ridan-to'g'ri o'tish bilan navigatsiya kerak bo'lsa — Offset Pagination soddaroq. Cheksiz skroll yoki yangiliklar feed lari uchun kursorlar afzalroq.
Keyset pagination — OFFSET o'rniga WHERE yordamida noyob kalit bo'yicha filtrlash amalga oshiriladigan cursor-based yondashuvining bir turi. SQL so'rovi WHERE id > lastId shartidan foydalanadi, bu ma'lumotlar bazasiga tashlangan qatorlarni skanerlamasdan indeksdan foydalanishga imkon beradi.
PostgreSQL Wiki ga ko'ra, keyset pagination katta siljishlarda offset so'rovidan 100-1000 marta tezroq bajariladi, chunki indeks skanerlashi jadvalni to'liq aylanib chiqishni almashtiradi. Kamchilik — ketma-ket o'tishsiz ixtiyoriy sahifaga sakrashning imkonsizligi.
Offset Pagination kichik va o'rta hajmdagi ma'lumotlar to'plamlari (10000 yozuvgacha) uchun optimal, bunda foydalanuvchiga sahifa raqamlari bo'lgan interfeys kerak. Odatiy stsenariylar — admin panellar, buyurtmalar ro'yxati, filtrlash va sahifalar bo'yicha sahifalash bilan kataloglar.
Mobil ilovalar uchun offset-sahifalash tarixiy ma'lumotlarni yuklashda mos keladi, bunda yangi yozuvlarni qo'shish kam yoki imkonsiz — masalan, foydalanuvchining buyurtma tarixi, bajarilgan vazifalar ro'yxati, tranzaksiya arxivi. Bu stsenariylarda izchillik muammosi yuzaga kelmaydi.
Tavsiya etilmaydi Offset Pagination dan ijtimoiy tarmoq feed lari, sharhlar ro'yxati, chatlar va tez-tez qo'shiladigan boshqa dinamik to'plamlar uchun foydalanish. Bunday hollarda yozuvlarning o'tkazib yuborilishi va dublikatlari foydalanuvchi tajribasini yomonlashtiradi va mijoz tomonida qo'shimcha deduplikatsiya mantiqini talab qiladi.
Gibrid sahifalash offset va kursorni birlashtiradi: birinchi so'rov boshlang'ich sahifani ko'rsatish uchun offset dan, keyingilari esa cheksiz skroll yuklash uchun kursor dan foydalanadi. Bu yondashuv Instagram va Twitter da tatbiq etilgan, bunda birinchi sahifa kursor orqali yuklanadi, lekin offset oldingi ko'rinishga qaytishda pozitsiyani hisoblash uchun ishlatiladi.
Gibrid yondashuvni tatbiq etish mijoz tomonida foydalanuvchining virtual pozitsiyasini saqlashni va serverda ikkita sahifalash mexanizmini muvofiqlashtirishni talab qiladi. Instagram Engineering blogiga ko'ra, ularning jamoasi boshlang'ich yuklash uchun offset ni almashtiradigan qo'shimcha startCursor maydoni bilan cursor-based sahifalashdan foydalanadi.
Mobil ilovalar Offset Pagination ni Android da Retrofit/OkHttp va iOS da URLSession/Combine bilan birgalikda ishlatadi. Odatiy namuna — RecyclerView.OnScrollListener yoki UICollectionView prefetching orqali ro'yxat oxiriga skroll qilganda keyingi sahifani yuklash.
Mobil mijozda offset-sahifalashni tatbiq etish uchta komponentni o'z ichiga oladi: sahifalash menejeri (joriy offset va hasMore ni saqlaydi), ro'yxat adapteri (elementlar va yuklash ko'rsatkichini ko'rsatadi) va repository (so'rovlarni bajaradi va xatolarni boshqaradi). Android Jetpack ham offset, ham cursor-based sahifalashni qutidan chiqqanday qo'llab-quvvatlaydigan Paging 3 kutubxonasini taklif qiladi.
Paging 3 — ma'lumotlarni sahifalab yuklash uchun Android Jetpack kutubxonasi. U offset kuzatish, yuklash holatini boshqarish va skroll paytida avtomatik yuklashni o'z ichiga olgan sahifalash mantiqini inkapsulyatsiya qiladi. PagingSource keyingi va oldingi sahifalar uchun kalitlarni belgilaydi.
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 sahifalar bo'ylab navigatsiya uchun prevKey va nextKey kalitlarini belgilaydi. Offset-sahifalashda prevKey har doim null (tarixni saqlamasdan oldingi sahifaga o'tish mumkin emas), nextKey esa server hasMore = false qaytarguncha har bir yuklashda limit ga oshadi. Bu mobil ro'yxatlar uchun sodda va bashorat qilinadigan modeldir.
Birinchi xato — saralashsiz yozuvlar tartibiga tayanish. Offset Pagination talab qiladi noyob maydon bo'yicha barqaror ORDER BY saralashni. Busiz DBMS yozuvlarni ixtiyoriy tartibda qaytarishi mumkin, bu esa sahifalar orasida tasodifiy dublikatlar va o'tkazib yuborishlarga olib keladi.
Ikkinchi xato — interfeysda sahifa raqamini hisoblash uchun offset dan foydalanish. page = offset / limit + 1 formulasi faqat yuklashlar orasida hech qanday yozuv o'chirilmagan yoki qo'shilmagan sharti bilan ishlaydi. Dinamik ma'lumotlarda sahifa raqami noto'g'ri bo'ladi va foydalanuvchi xato ma'lumotlarni ko'radi.
Uchinchi xato — katta offset li so'rovlarning vaqt cheklovlarini e'tiborsiz qoldirish. Offset 100000 dan yuqori bo'lganda so'rov o'nlab soniyalar davom etishi, interfeysni bloklashi va server resurslarini iste'mol qilishi mumkin. API darajasida maksimal offset qiymatini (masalan, 10000) o'rnatish va katta hajmlar uchun cursor-based sahifalashdan foydalanish tavsiya etiladi.
To'rtinchi xato — javobga total count qo'shmaslik. Umumiy yozuvlar sonisiz mijoz sahifalar sonini ko'rsata olmaydi va raqamlar bilan sahifalashni tatbiq eta olmaydi. Biroq katta jadvallarda COUNT(*) hisoblash ham qimmat — 100000 yozuvdan yuqori to'plamlar uchun taxminiy baholardan foydalaning yoki maksimal total qiymatini cheklang.
Tez-tez beriladigan savollar
Offset yozuvlarni o'tkazib yuborish uchun raqamli siljishdan (offset) foydalanadi, cursor esa oldingi sahifaning oxirgi yozuviga ko'rsatkichdan. Offset tatbiqda soddaroq, lekin qo'shilganda dublikatlar va katta siljishlarda unumdorlik yo'qotilishidan aziyat chekadi. Cursor ma'lumotlardagi har qanday o'zgarishlarda barqaror.
Offset sahifalash jadvalni to'liq skanerlash tufayli 10000 yozuvdan yuqori offset da samarasiz. Shuningdek, so'rovlar orasida yangi yozuvlar paydo bo'ladigan dinamik to'plamlar (feed lar, chatlar) uchun yaroqsiz — foydalanuvchi navigatsiya paytida o'tkazib yuborishlar va dublikatlarni ko'radi.
Optimal limit yozuv hajmi va tarmoq tezligiga bog'liq — sahifada 10 dan 50 gacha element. Katta rasmlar bilan ro'yxatlar uchun limit = 10-15, matn ma'lumotlari uchun — 20-50 dan foydalaning. Har doim mijozga serverdagi maksimal cheklov bilan (odatda 100) o'z limitini belgilashga ruxsat bering.
Dublikatlar bilan kurashish uchun noyob ID bo'yicha mijoz tomonida deduplikatsiyadan foydalaning, noyob maydon bo'yicha barqaror saralashni qo'llang yoki cursor-based sahifalashga o'ting. Android Paging 3 ro'yxat elementlarini avtomatik deduplikatsiya qilish uchun kalitni qo'llab-quvvatlaydi.
Ha, GraphQL so'rovda offset va limit argumentlari orqali offset-sahifalashni qo'llab-quvvatlaydi, garchi Relay spetsifikatsiyasi cursor-based yondashuvni tavsiya qilsa ham. Apollo GraphQL va Relay kutubxonalari sahifalar holatini avtomatik boshqarish bilan offset-sahifalashning o'rnatilgan qo'llab-quvvatlashini taklif qiladi.
Xulosa
Biz kalit topshirig'i bilan mobil ilovani ishlab chiqamiz
IT Sectr 2017-yildan beri startaplar va korxonalar uchun iOS va Android ilovalarini yaratadi. Biz sizga maslahat beramiz va eng yaxshi yechimni taklif qilamiz.