পেজিনেশন হলো ডেটা পৃষ্ঠায় পৃষ্ঠায় লোড করার একটি কৌশল, যা মোবাইল অ্যাপ্লিকেশন এবং ওয়েব সার্ভিসে বড় রেকর্ড সেট নিয়ে কাজ করতে ব্যবহৃত হয়। Android Developers Documentation (2025)-এর মতে, পেজিনেশনের সঠিক বাস্তবায়ন API-এর লোড কমায়, ট্রাফিক বাঁচায় এবং ব্যবহারকারীর অভিজ্ঞতা উন্নত করে। পৃষ্ঠা ধরে লোডিং অ্যাপ্লিকেশনকে সব ডেটা লোড হওয়ার অপেক্ষা না করে ধীরে ধীরে কন্টেন্ট দেখাতে দেয়।
মূল বিষয়
পেজিনেশন (ল্যাটিন pagination — পৃষ্ঠা বিভাজন) একটি কৌশল যা একটি বড় ডেটাসেটকে ক্রমিক অংশে (পৃষ্ঠা) ভাগ করে। মোবাইল অ্যাপ্লিকেশনে, পেজিনেশন ব্যবহার করা হয় বার্তা তালিকা, নিউজ ফিড, পণ্য ক্যাটালগ, অর্ডার ইতিহাস এবং সম্ভাব্য সীমাহীন সংখ্যক রেকর্ডযুক্ত যেকোনো সংগ্রহ লোড করার সময়।
পেজিনেশন ছাড়া, অ্যাপ্লিকেশনকে একসঙ্গে সব ডেটা লোড করতে বাধ্য হতে হয়, যার ফলে দীর্ঘ অপেক্ষা, উচ্চ ট্রাফিক খরচ এবং দুর্বল ডিভাইসে অস্থির পারফরম্যান্স হয়। পেজিনেশনসহ API অনুরোধ ডেটার শুধু একটি অংশ এবং পরবর্তী অংশ লোড করার জন্য মেটা-তথ্য ফেরত দেয় — এইভাবে, অ্যাপ্লিকেশন প্রাপ্ত তথ্যের পরিমাণ নিয়ন্ত্রণ করে।
পেজিনেশনের প্রধান মেট্রিক: পৃষ্ঠার আকার (page size) — একটি পৃষ্ঠায় রেকর্ডের সংখ্যা (সাধারণত 10–50), এবং পৃষ্ঠা নম্বর বা কার্সর — সেটে বর্তমান অবস্থানের নির্দেশক। পৃষ্ঠার আকারের পছন্দ ডেটার প্রকারের উপর নির্ভর করে: কমপ্যাক্ট উপাদানের (নাম) জন্য 20–30 যথেষ্ট, ছবিযুক্ত কার্ডের জন্য — 10–15।
মোবাইল ডিভাইসের সীমিত সম্পদ আছে: র্যাম, প্রসেসরের গতি ও ট্রাফিক সীমা। পেজিনেশন তিনটি মূল কাজ সমাধান করে: মেমরি খরচ কমানো (শুধু দৃশ্যমান উপাদান মেমরিতে সংরক্ষিত হয়), প্রথম প্রদর্শন ত্বরান্বিত করা (প্রথম অংশ পুরো সেটের চেয়ে দ্রুত লোড হয়), এবং ট্রাফিক সাশ্রয় (ব্যবহারকারী তালিকা স্ক্রোল করলেই কেবল ডেটা লোড হয়)।
পেজিনেশনের চারটি প্রধান প্রকার আছে, প্রতিটি নির্দিষ্ট কাজ সমাধান করে। পদ্ধতি বাছাই ডেটা সামঞ্জস্যের প্রয়োজনীয়তা, API আর্কিটেকচার, স্টোরেজের ধরন এবং ক্লায়েন্ট ও সার্ভারে বাস্তবায়নের গ্রহণযোগ্য জটিলতার উপর নির্ভর করে।
| প্রকার | কাজের নীতি | স্থিতিশীলতা | বড় আয়তনে গতি |
|---|---|---|---|
| Offset | SQL-এ LIMIT + OFFSET | নিম্ন | OFFSET বাড়লে কমে |
| Cursor | WHERE id > last_id | উচ্চ | স্থিতিশীল (O(log n)) |
| Keyset | WHERE key > last_key | উচ্চ | স্থিতিশীল (O(log n)) |
| Time-based | WHERE created_at < last_time | মধ্যম | ইনডেক্সসহ স্থিতিশীল |
Offset পেজিনেশন স্থির বা খুব কমই আপডেট হওয়া ডেটাসেটের জন্য উপযুক্ত, যখন সরল বাস্তবায়ন গুরুত্বপূর্ণ। Cursor ও Keyset ঘন ঘন ইনসার্টযুক্ত গতিশীল ডেটার জন্য। Time-based কালানুক্রমিক ফিডের জন্য যেখানে রেকর্ড তৈরি সময় অনুযায়ী সাজানো হয়। GraphQL মান Relay একমাত্র প্রস্তাবিত পদ্ধতি হিসেবে কার্সর পেজিনেশন ব্যবহার করে।
Offset পেজিনেশন পৃষ্ঠা-ভিত্তিক লোডিংয়ের সবচেয়ে সহজ প্রকার। ক্লায়েন্ট page ও limit (বা offset ও limit) প্যারামিটার পাঠায়, এবং সার্ভার SQL OFFSET ও LIMIT প্রয়োগ করে। উদাহরণস্বরূপ, page=2, limit=20 রেকর্ড 21 থেকে 40 ফেরত দেয়। এই পদ্ধতি স্বজ্ঞাত এবং যেকোনো স্ট্যাকে বাস্তবায়ন করা সহজ।
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 পেজিনেশনের প্রধান ত্রুটি হল হারিয়ে যাওয়া ও ডুপ্লিকেট রেকর্ডের সমস্যা। দুটি অনুরোধের মধ্যে টেবিলে নতুন রেকর্ড যোগ করলে, OFFSET সরে যায়: ব্যবহারকারী একই রেকর্ড দুবার দেখতে পারেন বা একটি নতুন রেকর্ড মিস করতে পারেন। এটি নিউজ ফিড ও চ্যাটের জন্য গুরুত্বপূর্ণ যেখানে সামঞ্জস্য গুরুত্বপূর্ণ।
আরেকটি সমস্যা হল বড় OFFSET মানে পারফরম্যান্সের অবনতি। ফলাফল ফেরত দেওয়ার আগে ডেটাবেসকে প্রথম offset রেকর্ড স্ক্যান ও এড়িয়ে যেতে হয়। offset=100000-এ LIMIT 20 থাকলেও, সার্ভার স্ক্যানিংয়ে উল্লেখযোগ্য সময় ব্যয় করবে। PostgreSQL ও MySQL OFFSET বাড়ার সঙ্গে গতিতে রৈখিক পতন দেখায়।
Offset পেজিনেশন এর জন্য সেরা পছন্দ রয়ে গেছে: প্রশাসনিক প্যানেল (ডেটা খুব কমই পরিবর্তিত হয়, পৃষ্ঠা নেভিগেশন প্রয়োজন), রিপোর্ট ও ঐতিহাসিক লগ (ডেটার নির্দিষ্ট স্ন্যাপশট), ফিল্টারযুক্ত ক্যাটালগ (যেকোনো পৃষ্ঠায় যাওয়া যায়)। Offset ক্লায়েন্টে বাস্তবায়ন করাও সবচেয়ে সহজ — RecyclerView Paging 3-এর সঙ্গে এটি বক্সের বাইরে সমর্থন করে।
Keyset পেজিনেশন রেকর্ড ফিল্টার করতে একটি ইউনিক কী (সাধারণত প্রাথমিক কী) ব্যবহার করে। OFFSET-এর পরিবর্তে, কোয়েরি WHERE id > last_seen_id ব্যবহার করে। এটি রেকর্ড সংখ্যা নির্বিশেষে স্থিতিশীল পারফরম্যান্স এবং ইনসার্টে কোনো ডুপ্লিকেট না হওয়া নিশ্চিত করে, কারণ নতুন রেকর্ডের id সবসময় বড় হয়।
-- Offset পেজিনেশন (সমস্যাপূর্ণ)
SELECT * FROM posts
ORDER BY id
LIMIT 20 OFFSET 100;
-- Keyset পেজিনেশন (স্থিতিশীল)
SELECT * FROM posts
WHERE id > 100
ORDER BY id
LIMIT 20;
Time-based পেজিনেশন (বা সময় অনুযায়ী কার্সর) নেভিগেশনের জন্য created_at টাইমস্ট্যাম্প ব্যবহার করে। ক্লায়েন্ট শেষ লোড করা রেকর্ডের টাইমস্ট্যাম্প পাঠায়, এবং সার্ভার সেই টাইমস্ট্যাম্পের আগে বা পরে তৈরি রেকর্ড ফেরত দেয়। এই পদ্ধতি সোশ্যাল নেটওয়ার্ক ও নিউজ ফিডে জনপ্রিয় যেখানে রেকর্ডের ক্রম প্রকাশনার সময় দ্বারা নির্ধারিত হয়।
Time-based পেজিনেশনের একটি বৈশিষ্ট্য — দুটি রেকর্ড একই মিলিসেকেন্ডে তৈরি হলে ডুপ্লিকেট সম্ভব। এই সমস্যা দূর করতে, একটি ইউনিক id-এর সাথে সময়-ভিত্তিক কী একত্রিত করুন: WHERE (created_at, id) < (last_time, last_id)। এই ধরনের যৌগিক কার্সর প্রতিটি রেকর্ডের স্বতন্ত্রতা এবং সঠিক ক্রম নিশ্চিত করে।
Keyset পেজিনেশনের জন্য একটি ইউনিক এবং একঘেয়েভাবে বাড়তে থাকা মান (অটো-ইনক্রিমেন্ট id, UUID v7)যুক্ত কলাম প্রয়োজন। Time-based created_atযুক্ত যেকোনো টেবিলের জন্য উপযুক্ত, কিন্তু অতিরিক্ত ডুপ্লিকেট হ্যান্ডলিং প্রয়োজন। প্রধান পার্থক্য: Keyset যেকোনো ইনসার্ট অপারেশনে স্থিতিশীলভাবে কাজ করে, যেখানে Time-based অভিন্ন টাইমস্ট্যাম্পের প্রতি সংবেদনশীল।
পেজিনেশনের প্রকার বাছাই ডেটার প্রকৃতি ও ব্যবহারকারীর অভিজ্ঞতার প্রয়োজনীয়তার উপর নির্ভর করে। নিচে মোবাইল ডেভেলপমেন্টে সাধারণ পরিস্থিতির জন্য সুপারিশ দেওয়া হল। কোনো সার্বজনীন সমাধান নেই — প্রতিটি পদ্ধতির একটি ক্ষেত্র আছে যেখানে তা সর্বোত্তম।
Android Paging 3 লাইব্রেরি PagingSource-এর মাধ্যমে সব ধরনের পেজিনেশন সমর্থন করে। Offset-এর জন্য — Int কী (page)সহ PagingSource, Cursor-এর জন্য — String বা Long কী (cursor)সহ। PagingSource স্বয়ংক্রিয়ভাবে লোডিং, ক্যাশিং এবং ত্রুটিতে পুনঃচেষ্টা পরিচালনা করে।
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
}
}
}
পৃষ্ঠার আকার লোডিং গতি ও পারফরম্যান্সের ধারণাকে প্রভাবিত করে। মোবাইল অ্যাপ্লিকেশনের জন্য, সর্বোত্তম সীমা প্রতি পৃষ্ঠায় 10–25 আইটেম। ১০-এর কম — API-তে অত্যধিক অনুরোধ ও থেমে থেমে স্ক্রোল। ২৫-এর বেশি — ধীর নেটওয়ার্কে প্রথম অংশের ধীর লোডিং।
ছবি ও ভিডিওর জন্য, পৃষ্ঠার আকার ৫–১০-এ কমিয়ে দিন, কারণ প্রতিটি উপাদান মিডিয়া লোড করতে অতিরিক্ত সময় নেয়। টেক্সট-ভিত্তিক তালিকার (মন্তব্য, লগ) জন্য, আকার 30–50 রেকর্ড পর্যন্ত বাড়ানো যেতে পারে। API-র মাধ্যমে পৃষ্ঠার আকার কনফিগারযোগ্য করার সুপারিশ করা হয় যাতে ক্লায়েন্ট বিভিন্ন নেটওয়ার্ক অবস্থার সাথে খাপ খাইয়ে নিতে পারে।
সচরাচর জিজ্ঞাসা
পেজিনেশন হলো ডেটা অংশে লোড করা, একসঙ্গে সব না। যেমন বই: আপনি একটি পৃষ্ঠা পড়েন, তারপর পরবর্তীতে যান। অ্যাপে, এর অর্থ হল তালিকা স্ক্রোল করার সময় ডেটার পরবর্তী ব্যাচ লোড হয়, পুরো তালিকা নয়, যা ট্রাফিক ও মেমরি বাঁচায়।
Offset রেকর্ড গণনা করে: “20 এড়িয়ে যাও, পরবর্তী 10 ফেরত দাও।” লোডের মধ্যে নতুন রেকর্ড যোগ করলে, সংখ্যা নষ্ট হয়। Cursor শেষ রেকর্ডের ইউনিক শনাক্তকারী ব্যবহার করে: “ID = 100-এর পরে 10 রেকর্ড ফেরত দাও।” নতুন রেকর্ড অবস্থানকে প্রভাবিত করে না।
মোবাইল অ্যাপ্লিকেশনের জন্য, 10–25 আইটেম সর্বোত্তম। ছবিযুক্ত তালিকার জন্য — 5–10, টেক্সট ফিডের জন্য — 20–30। আকার প্রতিটি উপাদানের গড় আকারের উপর নির্ভর করে: উপাদান যত ভারী, দ্রুত প্রদর্শনের জন্য পৃষ্ঠা তত ছোট হওয়া উচিত।
Android Jetpack-এর Paging 3 লাইব্রেরি ব্যবহার করুন। এটি লোডিংয়ের জন্য PagingSource, রিঅ্যাকটিভ স্ট্রিমের জন্য PagingData এবং স্ক্রোলের সময় স্বয়ংক্রিয় লোডিংয়ের জন্য PagingDataAdapter প্রদান করে। লাইব্রেরি কাস্টম PagingSource-এর মাধ্যমে Offset, Cursor ও Keyset পেজিনেশন সমর্থন করে।
ইনফিনিট স্ক্রোল একটি UI প্যাটার্ন যেখানে তালিকার শেষের কাছে পৌঁছালে ডেটার একটি নতুন ব্যাচ স্বয়ংক্রিয়ভাবে লোড হয়। পেজিনেশন হলো পদ্ধতি ডেটা ব্যাচে লোড করার, আর ইনফিনিট স্ক্রোল তা প্রদর্শনের একটি উপায়। বিকল্প হলো “আরও লোড করুন” বোতাম।
সারাংশ
আমরা একটি মোবাইল অ্যাপ্লিকেশন টার্নকি তৈরি করব
IT Sectr 2017 সাল থেকে স্টার্টআপ এবং ব্যবসার জন্য iOS এবং Android অ্যাপ্লিকেশন তৈরি করে। আমরা আপনাকে পরামর্শ দেব এবং সেরা সমাধান প্রস্তাব করব।
আরও পড়ুন