Cursor Pagination mobil ishlanmada — bu nima, prinsipi va amalga oshirish

Muallif: IT Sectr Nashr etilgan: 2026-03-11 O'qish vaqti: 9 daq

Cursor Pagination — tartiblangan yozuvlar to'plamida navigatsiya qilish uchun noyob kursor yordamida ma'lumotlarni sahifalash usulidir. GraphQL Specification (2025) ga ko'ra, kursorli sahifalash dinamik ma'lumotlar bilan ishlaydigan API-lar uchun tavsiya etilgan standartdir. Cursor Pagination Offset yondashuvining asosiy kamchiliklarini bartaraf qiladi: qo'shishlardagi beqarorlik va katta siljishlarda ishlashning pasayishi.

Asosiy fikrlar

  • Cursor Pagination — har bir yozuv navigatsiya uchun noyob identifikator-kursorga ega bo'lgan sahifalash usuli.
  • Kursor — ma'lumotlar to'plamidagi pozitsiyaning noyob belgisi (odatda ID, UUID, timestamp), qo'shishlarda o'zgarmaydi.
  • Barqarorlik — so'rovlar orasida qo'shilgan yangi yozuvlar kursorni siljitmaydi, dublikat va bo'shliqlarni yo'q qiladi.
  • Unumdorlik — WHERE id > cursor so'rovi indeksdan samarali foydalanadi, katta to'plamlarda tezlikni yo'qotmaydi.
  • Cheklov — kursorli sahifalash sahifa raqami bo'yicha navigatsiyani qo'llab-quvvatlamaydi (5-sahifaga sakrab bo'lmaydi).

Cursor Pagination nima?

Cursor Pagination (kursorli sahifalash) — server ma'lumotlar bilan birga maxsus ko'rsatkich — kursor qaytaradigan sahifalash usuli. Mijoz keyingi so'rovda ushbu kursor yordamida yozuvlarning keyingi qismini oladi. Kursor joriy sahifaning oxirgi elementining noyob identifikatoridir.

Offset-sahifalashdan farqli o'laroq, bu yerda mijoz “menga 20 yozuvdan 5-sahifani ber” demaydi, kursorli sahifalash boshqacha ishlaydi: “menga ID = 83 bo'lgan yozuvdan keyin 20 yozuv ber”. Server WHERE id > 83 sharti va LIMIT 20 bilan so'rovni bajaradi. Bunday yondashuv har bir yozuv qo'shilishlardan qat'iy nazar aniq bir sahifaga tushishini kafolatlaydi.

Kursorli sahifalash kontseptsiyasi Relay Connection (GraphQL) spetsifikatsiyasi tufayli keng tarqalgan va cursor-based paginationni zamonaviy API-lar uchun standartga aylantirgan. Relay javob formatini belgilaydi: edges (kursorlar bilan yozuvlar massivi), pageInfo (hasNextPage, hasPreviousPage, startCursor, endCursor).

Kelib chiqish tarixi

Cursor-pagination yangi texnika emas — veb paydo bo'lishidan ancha oldin ma'lumotlar bazalarida ishlatilgan. SQLda bu keyset pagination yoki seek method deb ataladi. Usul 2015-yilda Relay spetsifikatsiyasi nashr etilgandan so'ng API-larda ommalashdi va kursor formatini HTTP orqali uzatish uchun base64 kodlangan satr sifatida rasmiylashtirdi.

Kursorli sahifalash qanday ishlaydi

Kursorli sahifalashning asosiy prinsipi — so'rov siljish emas, balki joylashish uchun indekslangan maydonda WHERE shartidan foydalanadi. Oldinga yo'nalish uchun WHERE id > last_id, orqaga yo'nalish uchun WHERE id < first_id qo'llaniladi. B-tree indeksi kursor orqali birinchi yozuvni O(log n) vaqtida topish imkonini beradi, bu barqaror javob vaqtini ta'minlaydi.

sql
-- '83' kursoridan keyin 20 ta yozuv olish
SELECT id, title, created_at
FROM posts
WHERE id < 83
ORDER BY id DESC
LIMIT 20;

-- '83' kursoridan OLDIN 20 ta yozuv olish (orqaga)
SELECT id, title, created_at
FROM posts
WHERE id > 83
ORDER BY id ASC
LIMIT 20;

Kursor formati

Kursor oddiy (ID qiymati) yoki murakkab (bir nechta maydonlardan tashkil topgan) bo'lishi mumkin. Oddiy kursorlar yozuvning asosiy kalitidir, masalan avtomatik o'suvchi id yoki UUID. Murakkab kursorlar noyob bo'lmagan maydonlar bo'yicha saralash uchun ishlatiladi, masalan (created_at, id), bunda id bir xil vaqt belgilarida noyoblikni kafolatlaydi.

Odatdagi API formati — kursor base64 kodlangan satr ko'rinishida. Server kursorni dekodlaydi, qiymatni chiqaradi va SQL so'rovini tuzadi. Base64 kodlash kursorning ichki tuzilishini mijozdan yashiradi va orqaga moslikni yo'qotmasdan formatni o'zgartirish imkonini beradi. Mijoz kursorlarni javobning endCursor maydonida oladi va keyingi so'rovda satr sifatida uzatadi.

Oldinga va orqaga navigatsiya

Kursorli sahifalash ikki tomonlama navigatsiyani qo'llab-quvvatlaydi. Oldinga harakat (next) uchun joriy sahifaning oxirgi elementining kursori, orqaga (previous) uchun — birinchi elementning kursori ishlatiladi. So'rovdagi after va before parametrlari yo'nalishni belgilaydi: after kursor orqali yozuvlarni oladi, before — kursor oldidan.

Cursor vs Offset pagination

Kursor va Offset sahifalash o'rtasidagi tanlov API loyihalashda asosiy arxitektura masalalaridan biridir. Har bir usulning kuchli va zaif tomonlari bor. Cursor-pagination dinamik ma'lumotlar ssenariylarida, Offset — ixtiyoriy navigatsiya ssenariylarida ustunlik qiladi.

XususiyatCursorOffset
Qo'shishlardagi barqarorlikYuqori (dublikatsiz)Past (sahifa siljishi)
Katta to'plamlardagi unumdorlikO(log n) — barqarorO(n) — o'sish bilan kamayadi
Sahifa raqami bo'yicha navigatsiyaYo'qHa (page=5)
Amalga oshirish murakkabligiO'rtaPast
REST qo'llab-quvvatlashicursor/before/afterpage/offset
GraphQL qo'llab-quvvatlashiRelay standartiTavsiya etilmaydi

Nega Offset katta miqyosda yutqazadi

Offset sahifalash OFFSET pozitsiyasigacha to'liq jadvalni skanerlashni amalga oshiradi. Offset=100000 bo'lganda, LIMIT 20 bo'lsa ham, ma'lumotlar bazasi 100000 qatorni o'qiydi va o'tkazib yuboradi. MySQL va PostgreSQL OFFSETni optimallashtira olmaydi — bu SQLda LIMIT/OFFSETni amalga oshirish xususiyati. Cursor-pagination B-tree indeksidan foydalanadi, pozitsiyani O(log n) vaqtida topadi.

Offsetning qo'shimcha muammosi — orqaga sahifalashda yozuvlarning “o'tkazib yuborilishi”. Agar foydalanuvchi 5-sahifani yuklagan bo'lsa va shu vaqtda yangi yozuvlar qo'shilgan bo'lsa, 6-sahifani so'raganda, 5-sahifadagi yozuvni yana ko'radi yoki yangi yozuvlarni o'tkazib yuboradi. Cursor-pagination bu ssenariyni butunlay yo'q qiladi: kursor to'plamdagi aniq joyni ko'rsatadi va qo'shishlar pozitsiyani o'zgartirmaydi.

Cursor Paginationni amalga oshirish

Kursorli sahifalashni backendda (Kotlin + Spring) va mijozda (Android + Retrofit) amalga oshirishni ko'rib chiqamiz. Server after, before, limit parametrlarini qabul qiladi va kursorlar va pageInfo bilan yozuvlar ro'yxatini qaytaradi. Oddiy javob sahifalash UI-ni boshqarish uchun hasNextPage va hasPreviousPage ni o'z ichiga oladi.

kotlin
@GetMapping("/posts")
fun getPosts(
    @RequestParam after: Long?,
    @RequestParam(defaultValue = "20") limit: Int
): CursorResponse<Post> {
    val cursor = after ?: Long.MAX_VALUE
    val posts = repository.findByIdLessThanOrderByIdDesc(
        cursor, PageRequest.of(0, limit)
    )
    val endCursor = posts.lastOrNull()?.id
    return CursorResponse(
        data = posts,
        pageInfo = PageInfo(
            hasNextPage = posts.size == limit,
            endCursor = endCursor
        )
    )
}

Androidda mijoz tomonida amalga oshirish

Mijoz tomonida kursorli sahifalash Paging 3 dan PagingSource orqali amalga oshiriladi, bunda kalit kursor (Long) hisoblanadi. PagingSource.load LoadParams.key — oxirgi yuklangan yozuvning kursorini oladi. LoadResult.Page ma'lumotlarni va nextKey — keyingi sahifa uchun kursorini qaytaradi. NextKey = null bo'lganda — sahifalash tugallanadi.

kotlin
// Retrofit API
interface PostApi {
    @GET("posts")
    suspend fun getPosts(
        @Query("after") after: Long?,
        @Query("limit") limit: Int = 20
    ): CursorResponse<Post>
}

// PagingSource kursor kaliti bilan
class PostPagingSource(
    private val api: PostApi
) : PagingSource<Long, Post>() {

    override suspend fun load(
        params: LoadParams<Long>
    ): LoadResult<Long, Post> = try {
        val response = api.getPosts(
            after = params.key,
            limit = params.loadSize
        )
        val nextKey = response.pageInfo.endCursor
        LoadResult.Page(
            data = response.data,
            prevKey = null,
            nextKey = nextKey
        )
    } catch (e: Exception) {
        LoadResult.Error(e)
    }
}

Relay orqali GraphQL amalga oshirish

GraphQLda kursorli sahifalash Relay Connection namunasi orqali amalga oshiriladi. Har bir tur Connection (pageInfo va edges bilan) va Edge (node + cursor) ga ega. So'rov first, after, last, before parametrlarini uzatadi. Server qaytaradi kursorlar bilan edges massivi va hasNextPage/hasPreviousPage bilan pageInfo.

Cursor Pagination qachon ishlatilishi kerak

Cursor-pagination yozuvlar tez-tez qo'shiladigan yoki o'chiriladigan dinamik ma'lumotlar bilan ishlaydigan API-lar uchun tavsiya etiladi. Klassik misollar: ijtimoiy tarmoqdagi yangiliklar lentasi, chat xabarlari, tranzaksiyalar tarixi, post sharhlari. Barcha bu ssenariylarda izchillik va dublikatlarning yo'qligi muhimdir.

  • Chatlar va messenjerlar — har bir yangi xabar ro'yxatning yuqorisiga qo'shiladi. Offset sahifalash har bir yangi xabarda buziladi.
  • Ijtimoiy tarmoqlar va lentalar — postlar uzluksiz nashr qilinadi. Kursorli sahifalash foydalanuvchi hech qanday postni o'tkazib yubormasligini kafolatlaydi.
  • Buyurtma va tranzaksiyalar tarixi — ma'lumotlar kamroq o'zgaradi, ammo moliyaviy hisobotlar uchun izchillik muhimdir.
  • Katta hajmdagi ma'lumotli API-lar — millionlab yozuvlar. Cursor-pagination Offset sekinlasha boshlagan joyda unumdorlikni saqlaydi.
  • GraphQL API — Relay standarti spetsifikatsiyaga muvofiq cursor-based paginationdan foydalanishni talab qiladi.
  • Cheksiz skroll bilan mobil ilovalar — foydalanuvchi pastga skrollab, yangi qismlarni yuklaydi. Kursor yondashuvi dublikatsiz silliq UX ta'minlaydi.

Cursor-pagination qachon mos kelmaydi

Offset sahifalash qulayroq bo'lgan ssenariylar mavjud: sahifa raqami bo'yicha navigatsiya kerak bo'lgan ma'muriy panellar; natijalar o'zgarishi mumkin bo'lgan sahifalash bilan qidirish; 5-sahifaga barqaror havola kerak bo'lgan hisobotlar va analitika. Bunday hollarda kursorning afzalliklari amalga oshirish murakkabligidan ustun kelmaydi.

Kursorli sahifalash ixtiyoriy sahifaga “sakrashni” qo'llab-quvvatlamaydi — foydalanuvchi “5-Sahifa” tugmasini bosib, unga o'ta olmaydi. Bu arxitektura cheklovidir: sahifalarning umumiy sonini hisoblash uchun alohida COUNT so'rovi talab qilinadi, bu katta jadvallar uchun qimmat bo'lishi mumkin. Bunday hollarda gibrid yondashuv: ma'lumotlar uchun cursor + sahifalash uchun count.

Tez-tez beriladigan savollar

Cursor Paginationda kursor nima?

Kursor — ma'lumotlar to'plamida pozitsiyani ko'rsatadigan yozuvning noyob identifikatoridir. Oddiy (yozuv IDsi) yoki murakkab (bir nechta maydon) bo'lishi mumkin. Mijoz sahifaning oxirgi yozuvi kursorini oladi va keyingi qismni olish uchun uni keyingi so'rovda uzatadi.

Cursor Pagination Offsetdan qanday yaxshi?

Cursor-pagination yangi yozuvlar qo'shilganda siljishga duch kelmaydi — har bir element aniq bir sahifaga tushadi. Bundan tashqari, katta hajmlarda indekslardan foydalanish orqali tezlikni saqlaydi. Offset oddiyroq, lekin dinamik ma'lumotlar uchun barqaror emas.

Kursorli sahifalashni GraphQLsiz amalga oshirish mumkinmi?

Ha, kursorli sahifalash GraphQLga bog'liq emas. Uni istalgan REST APIda kursorni so'rov parametri sifatida uzatish orqali amalga oshirish mumkin ?after=83&limit=20. Javob endCursor va hasNextPage bilan pageInfo ni o'z ichiga olishi kerak — bu mijozga kursorning ichki tuzilishini bilmasdan yuklashni boshqarish imkonini beradi.

Qaysi kursor ishlatilishi kerak — ID, UUID yoki timestamp?

Avtomatik o'suvchi ID — optimal tanlov: monotonik o'sadi, o'zgarmaydi, samarali indekslanadi. UUID v7 (vaqt bo'yicha tartiblangan) ham mos keladi. Timestamp bir xil vaqtda dublikatlar berishi mumkin, shuning uchun ID bilan birlashtiriladi: (created_at, id) kursorning noyobligini kafolatlash uchun.

Kursorli sahifalashda umumiy sahifalar sonini qanday bilish mumkin?

Kursorli sahifalash umumiy sahifalar sonini bermaydi — bu uning cheklovidir. Total haqida ma'lumot kerak bo'lsa, bir xil filtrlar bilan alohida COUNT so'rovini bajaring. Katta jadvallar uchun EXPLAIN orqali taxminiy hisoblash yoki analitikadan keshlangan totaldan foydalaning.

Xulosa

  • Cursor Pagination — siljish o'rniga yozuvning noyob identifikatori orqali navigatsiya qiluvchi sahifalash usuli.
  • Kursor qo'shishlarda to'plam barqarorligini kafolatlaydi: yangi yozuvlar allaqachon yuklangan sahifalarni siljitmaydi.
  • Unumdorlik B-tree indekslaridan foydalanish tufayli katta ma'lumotlar hajmlarida yuqori (O(log n)) qoladi.
  • Cursor-pagination dinamik ma'lumotlar uchun mos: chatlar, yangiliklar lentalari, tranzaksiyalar, sharhlar.
  • Asosiy cheklov — sahifa raqami bo'yicha navigatsiyaning yo'qligi va ixtiyoriy sahifaga sakrab bo'lmasligi.
  • Amalga oshirish WHERE id kursor orqali, after/before parametrlari va javobda pageInfo dan foydalanadi.
  • Standart — Relay Connection GraphQL, ammo kursor parametrlari bilan REST API ham keng tarqalgan.

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.

Loyihani muhokama qilish

Shuningdek o'qing