Offset Pagination — অফসেট সহ পেজিনেশন — HTTP API এর মাধ্যমে ডেটা পৃষ্ঠায় লোড করার একটি পদ্ধতি। ক্লায়েন্ট offset (শুরু থেকে অফসেট) এবং limit (পৃষ্ঠার আকার) প্যারামিটার পাঠায়, এবং সার্ভার offset অবস্থান থেকে রেকর্ড ফেরত দেয়। REST API Tutorial অনুসারে, এই পদ্ধতিটি বাস্তবায়নের সরলতার কারণে RESTful সার্ভিসে ব্যাপকভাবে ব্যবহৃত হয়। তবে, বড় ডেটা ভলিউমে, অফসেট-পেজিনেশন প্রয়োজনীয় অবস্থান পর্যন্ত সম্পূর্ণ টেবিল স্ক্যানিংয়ের কারণে কর্মক্ষমতা হারায়।
মুখ্য বিষয়
Offset Pagination ডেটা পৃষ্ঠায় বিভাজনের একটি পদ্ধতি যেখানে ক্লায়েন্ট অনুরোধে দুটি প্যারামিটার থাকে: offset (কতগুলি রেকর্ড এড়িয়ে যেতে হবে) এবং limit (কতগুলি রেকর্ড ফেরত দিতে হবে)। সার্ভার OFFSET এবং LIMIT সহ SQL কোয়েরি কার্যকর করে, নির্দিষ্ট সংখ্যক সারি এড়িয়ে যায় এবং একটি নির্দিষ্ট আকারের ফলাফল সেট ফেরত দেয়।
পদ্ধতিটি রিলেশনাল ডেটাবেসে পৃষ্ঠা নেভিগেশন সংগঠিত করার সহজতম উপায় হিসেবে উদ্ভূত হয়েছিল এবং REST আর্কিটেকচারের বিকাশের সাথে HTTP API তে স্থানান্তরিত হয়েছিল। Offset Pagination সার্ভারে অবস্থা সংরক্ষণের প্রয়োজন হয় না — প্রতিটি অনুরোধ স্বাধীন এবং কোয়েরির জন্য সমস্ত প্রয়োজনীয় তথ্য ধারণ করে।
Postman (2025) এর API ডিজাইন রিপোর্ট অনুসারে, অফসেট পেজিনেশন 72% পাবলিক REST API তে ব্যবহৃত হয়, যা বড় ডেটা সেটে পরিচিত কর্মক্ষমতা সীমাবদ্ধতা সত্ত্বেও এটিকে প্রভাবশালী মানদণ্ড করে তোলে।
Offset Pagination সহ একটি সাধারণ REST অনুরোধে ক্যোয়ারী প্যারামিটার offset এবং limit অন্তর্ভুক্ত থাকে। প্রতিক্রিয়ায় অনুরোধকৃত পৃষ্ঠার রেকর্ড তালিকা এবং নেভিগেশন ইন্টারফেস তৈরির জন্য মেটাডেটা থাকে।
Limit প্যারামিটার ফেরত দেওয়া রেকর্ডের সংখ্যা সীমাবদ্ধ করে এবং সার্ভার ও ক্লায়েন্টকে অতিরিক্ত লোড থেকে রক্ষা করে। ডেটার জটিলতার উপর নির্ভর করে limit এর সাধারণ মান 10 থেকে 50 রেকর্ড প্রতি পৃষ্ঠা।
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 এবং FETCH NEXT (বা MySQL/SQLite এ LIMIT) কাঠামো সহ SQL কোয়েরিতে রূপান্তরিত হয়। ডেটাবেস সার্ভার টেবিল স্ক্যান করে, অফসেটের সমান সংখ্যক সারি এড়িয়ে যায় এবং পরবর্তী limit সারি ফেরত দেয়। অফসেট যত বড় হবে, কোয়েরি তত ধীর হবে।
কর্মক্ষমতা সমস্যা এই সত্যের সাথে সম্পর্কিত যে ডেটাবেস সরাসরি অফসেট অবস্থানে যেতে পারে না — তাকে আগের সমস্ত সারি পড়তে এবং বাতিল করতে হবে। offset = 100000 এবং limit = 20 এ, DBMS 100,020 টি সারি পড়ে এবং মাত্র 20 টি ফেরত দেয়।
SQL — যে ভাষায় সার্ভার অফসেট পেজিনেশন কার্যকর করে। PostgreSQL এবং MySQL LIMIT ব্যবহার করে, যখন SQL Server এবং Oracle OFFSET...FETCH ব্যবহার করে। বিভিন্ন DBMS এই কোয়েরি ভিন্নভাবে অপ্টিমাইজ করে, কিন্তু মৌলিক স্ক্যানিং সমস্যা একই থাকে।
SELECT id, title, created_at
FROM articles
ORDER BY created_at DESC
OFFSET 100000 ROWS
FETCH NEXT 20 ROWS ONLY;
ডেটা সামঞ্জস্য গতিশীল সেটের সাথে কাজ করার সময় Offset Pagination এর প্রধান ত্রুটি। যদি দুটি ব্যবহারকারী অনুরোধের মধ্যে টেবিলের শুরুতে একটি নতুন রেকর্ড যোগ করা হয়, তবে সমস্ত বিদ্যমান রেকর্ড স্থানান্তরিত হয়। ব্যবহারকারী ডুপ্লিকেট বা ফাঁক দেখে।
মনে করুন limit = 20 সহ 100 রেকর্ডের একটি টেবিল। পৃষ্ঠা 1 এ, ব্যবহারকারী রেকর্ড 1-20 দেখে। একজন প্রশাসক 5 টি নতুন রেকর্ড যোগ করে। পৃষ্ঠা 2 এ, ব্যবহারকারী প্রত্যাশিত 21-40 এর পরিবর্তে রেকর্ড 26-45 দেখে — রেকর্ড 21-25 বাদ পড়েছে, এবং আগের সেটের রেকর্ড 21-25 পৃষ্ঠা 1 এ ডুপ্লিকেট হয়েছে।
Cursor-based পেজিনেশন Offset Pagination এর একটি বিকল্প যা বর্তমান পৃষ্ঠার শেষ রেকর্ডে পয়েন্টার ব্যবহার করে। সংখ্যাগত অফসেটের পরিবর্তে, ক্লায়েন্ট শেষ প্রাপ্ত রেকর্ডের শনাক্তকারী পাঠায়, এবং সার্ভার তার পরে পরবর্তী N রেকর্ড ফেরত দেয়।
Cursor-based পদ্ধতি সামঞ্জস্য সমস্যা সমাধান করে: কার্সারের অবস্থান সন্নিবেশ বা মুছে ফেলার সময় পরিবর্তিত হয় না কারণ কার্সার একটি অবস্থানের পরিবর্তে একটি নির্দিষ্ট রেকর্ডকে নির্দেশ করে। তবে, এটি বাস্তবায়নে আরও জটিল — এর জন্য একটি অনন্য বাছাইযোগ্য ফিল্ড (সাধারণত ID বা timestamp) প্রয়োজন।
| প্যারামিটার | Offset Pagination | Cursor-based Pagination |
|---|---|---|
| সরলতা | উচ্চ — দুটি সংখ্যাগত প্যারামিটার | মধ্যম — কার্সার এনকোডিং প্রয়োজন |
| সামঞ্জস্য | নিম্ন — সন্নিবেশে ডুপ্লিকেট | উচ্চ — কার্সার পরিবর্তনে অপ্রভাবিত |
| কর্মক্ষমতা | বড় অফসেটে হ্রাস পায় | যেকোনো ভলিউমে স্থিতিশীল |
| পৃষ্ঠায় লাফ | হ্যাঁ — যেকোনো পৃষ্ঠায় যেতে পারেন | না — শুধুমাত্র অনুক্রমিক নেভিগেশন |
| উপযুক্ত | <10K রেকর্ডের টেবিল, পৃষ্ঠা নম্বর সহ UI | ফিড, অসীম স্ক্রল, বড় সেট |
পদ্ধতির মধ্যে পছন্দ ব্যবহারকারীর ইন্টারফেস প্রয়োজনীয়তার উপর নির্ভর করে। যদি পৃষ্ঠা নম্বর এবং সরাসরি লাফ দিয়ে নেভিগেশনের প্রয়োজন হয় — Offset Pagination সহজ। অসীম স্ক্রল বা নিউজ ফিডের জন্য, কার্সার পছন্দনীয়।
Keyset পেজিনেশন cursor-based পদ্ধতির একটি বৈকল্পিক যেখানে OFFSET এর পরিবর্তে WHERE ব্যবহার করে অনন্য কী-তে ফিল্টারিং করা হয়। SQL কোয়েরি WHERE id > lastId এর মতো শর্ত ব্যবহার করে, যা ডেটাবেসকে বাতিল করা সারি স্ক্যান না করেই সূচক ব্যবহার করতে দেয়।
PostgreSQL Wiki অনুসারে, keyset পেজিনেশন বড় অফসেটে অফসেট কোয়েরির চেয়ে 100-1000 গুণ দ্রুত চলে কারণ সূচক স্ক্যান সম্পূর্ণ টেবিল স্ক্যান প্রতিস্থাপন করে। অসুবিধা হল অনুক্রমিক পথ না নিয়ে যে কোনো পৃষ্ঠায় লাফ দিতে অক্ষমতা।
Offset Pagination ছোট থেকে মাঝারি ডেটা সেটের (10,000 রেকর্ড পর্যন্ত) জন্য সর্বোত্তম যেখানে ব্যবহারকারীর পৃষ্ঠা নম্বর ইন্টারফেস প্রয়োজন। সাধারণ পরিস্থিতিতে অ্যাডমিন প্যানেল, অর্ডার তালিকা এবং পৃষ্ঠা-ভিত্তিক পেজিনেশন সহ ফিল্টার করা ক্যাটালগ অন্তর্ভুক্ত।
মোবাইল অ্যাপ্লিকেশনের জন্য, অফসেট পেজিনেশন ঐতিহাসিক ডেটা লোড করার জন্য উপযুক্ত যেখানে নতুন সন্নিবেশ বিরল বা অসম্ভব — উদাহরণস্বরূপ, ব্যবহারকারীর অর্ডার ইতিহাস, সম্পূর্ণ কাজের তালিকা, বা লেনদেন সংরক্ষণাগার। এই পরিস্থিতিতে, সামঞ্জস্য সমস্যা দেখা দেয় না।
সুপারিশ করা হয় না সোশ্যাল মিডিয়া ফিড, মন্তব্য তালিকা, চ্যাট এবং ঘন ঘন সন্নিবেশ সহ অন্যান্য গতিশীল সেটের জন্য। এই ক্ষেত্রে, ফাঁক এবং ডুপ্লিকেট রেকর্ড ব্যবহারকারীর অভিজ্ঞতা নষ্ট করে এবং ক্লায়েন্টে অতিরিক্ত ডিডুপ্লিকেশন লজিক প্রয়োজন।
হাইব্রিড পেজিনেশন offset এবং cursor একত্রিত করে: প্রথম অনুরোধ প্রাথমিক পৃষ্ঠা দেখানোর জন্য offset ব্যবহার করে, যখন পরবর্তী অনুরোধগুলি অসীম স্ক্রল লোডিংয়ের জন্য cursor ব্যবহার করে। এই পদ্ধতি Instagram এবং Twitter এ ব্যবহৃত হয়, যেখানে প্রথম পৃষ্ঠা cursor এর মাধ্যমে লোড হয়, কিন্তু পূর্ববর্তী দৃশ্যে ফিরে আসার সময় অবস্থান গণনার জন্য offset ব্যবহার করা হয়।
হাইব্রিড পদ্ধতি বাস্তবায়নের জন্য ক্লায়েন্টে ব্যবহারকারীর ভার্চুয়াল অবস্থান সংরক্ষণ এবং সার্ভারে দুটি পেজিনেশন প্রক্রিয়ার সমন্বয় প্রয়োজন। Instagram Engineering ব্লগ অনুসারে, তাদের টিম প্রাথমিক লোডিংয়ের জন্য offset প্রতিস্থাপনকারী অতিরিক্ত startCursor ফিল্ড সহ cursor-based পেজিনেশন ব্যবহার করে।
মোবাইল অ্যাপ্লিকেশন Android এ Retrofit/OkHttp এবং iOS এ URLSession/Combine এর সাথে Offset Pagination ব্যবহার করে। সাধারণ প্যাটার্ন হল RecyclerView.OnScrollListener বা UICollectionView prefetching এর মাধ্যমে তালিকার শেষ পর্যন্ত স্ক্রল করার পরবর্তী পৃষ্ঠা লোড করা।
মোবাইল ক্লায়েন্টে অফসেট পেজিনেশন বাস্তবায়নে তিনটি উপাদান অন্তর্ভুক্ত: একটি পেজিনেশন ম্যানেজার (বর্তমান offset এবং hasMore সংরক্ষণ করে), একটি তালিকা অ্যাডাপ্টার (আইটেম এবং লোডিং নির্দেশক প্রদর্শন করে), এবং একটি রিপোজিটরি (অনুরোধ কার্যকর করে এবং ত্রুটিগুলি পরিচালনা করে)। Android Jetpack Paging 3 লাইব্রেরি সরবরাহ করে, যা বক্সের বাইরে offset এবং cursor-based উভয় পেজিনেশন সমর্থন করে।
Paging 3 পৃষ্ঠায় ডেটা লোড করার জন্য Android Jetpack লাইব্রেরি। এটি অফসেট ট্র্যাকিং, লোডিং অবস্থা পরিচালনা এবং স্ক্রলে স্বয়ংক্রিয় প্রিফেচিং সহ পেজিনেশন লজিক আবদ্ধ করে। PagingSource পরবর্তী এবং পূর্ববর্তী পৃষ্ঠাগুলির জন্য কী সংজ্ঞায়িত করে।
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 পৃষ্ঠা নেভিগেশনের জন্য prevKey এবং nextKey কী সংজ্ঞায়িত করে। অফসেট পেজিনেশনের সাথে, prevKey সর্বদা null হয় (ইতিহাস সংরক্ষণ না করে পূর্ববর্তী পৃষ্ঠায় যেতে পারে না), যখন nextKey প্রতিটি লোডের সাথে limit দ্বারা বৃদ্ধি পায় যতক্ষণ না সার্ভার hasMore = false ফেরত দেয়। এটি মোবাইল তালিকার জন্য একটি সহজ এবং পূর্বাভাসযোগ্য মডেল।
প্রথম ভুল — বাছাই ছাড়া রেকর্ডের ক্রমের উপর নির্ভর করা। Offset Pagination প্রয়োজন একটি অনন্য ফিল্ডে স্থিতিশীল ORDER BY বাছাই। এটি ছাড়া, DBMS ইচ্ছামত ক্রমে রেকর্ড ফেরত দিতে পারে, যার ফলে পৃষ্ঠাগুলির মধ্যে এলোমেলো ডুপ্লিকেট এবং ফাঁক হয়।
দ্বিতীয় ভুল — UI তে পৃষ্ঠা নম্বর গণনার জন্য offset ব্যবহার করা। সূত্র page = offset / limit + 1 কেবল তখনই কাজ করে যদি লোডের মধ্যে কোনো রেকর্ড মুছে বা যোগ না করা হয়। গতিশীল ডেটার সাথে, পৃষ্ঠা নম্বর ভুল হয়ে যায় এবং ব্যবহারকারী ভুল তথ্য দেখে।
তৃতীয় ভুল — বড় offset সহ কোয়েরির টাইমআউট উপেক্ষা করা। 100,000 এর বেশি offset এ, কোয়েরি কয়েক দশ সেকেন্ড নিতে পারে, UI ব্লক করতে পারে এবং সার্ভার সংস্থান গ্রাস করতে পারে। API স্তরে সর্বাধিক offset মান (যেমন, 10,000) সেট করা এবং বড় ভলিউমের জন্য cursor-based পেজিনেশন ব্যবহার করার পরামর্শ দেওয়া হয়।
চতুর্থ ভুল — প্রতিক্রিয়ায় total count অন্তর্ভুক্ত না করা। রেকর্ডের মোট সংখ্যা ছাড়া, ক্লায়েন্ট পৃষ্ঠা সংখ্যা প্রদর্শন করতে পারে না এবং সংখ্যাযুক্ত পেজিনেশন বাস্তবায়ন করতে পারে না। তবে, বড় টেবিলে COUNT(*) ব্যয়বহুল — 100,000 রেকর্ডের বেশি সেটের জন্য, আনুমানিক অনুমান ব্যবহার করুন বা সর্বাধিক মোট মান সীমিত করুন।
প্রায়শই জিজ্ঞাসিত প্রশ্ন
Offset রেকর্ড এড়িয়ে যাওয়ার জন্য সংখ্যাগত অফসেট ব্যবহার করে, যখন cursor পূর্ববর্তী পৃষ্ঠার শেষ রেকর্ডে পয়েন্টার ব্যবহার করে। Offset বাস্তবায়ন করা সহজ কিন্তু সন্নিবেশে ডুপ্লিকেট এবং বড় অফসেটে কর্মক্ষমতা হ্রাসে ভোগে। Cursor ডেটার যেকোনো পরিবর্তনে স্থিতিশীল।
অফসেট পেজিনেশন 10,000 রেকর্ডের বেশি অফসেটে সম্পূর্ণ টেবিল স্ক্যানিংয়ের কারণে অদক্ষ। এটি গতিশীল সেটের (ফিড, চ্যাট) জন্যও অনুপযুক্ত যেখানে অনুরোধের মধ্যে নতুন রেকর্ড দেখা দেয় — ব্যবহারকারী নেভিগেশনের সময় ফাঁক এবং ডুপ্লিকেট রেকর্ড দেখে।
সর্বোত্তম limit রেকর্ড আকার এবং নেটওয়ার্ক গতির উপর নির্ভর করে — প্রতি পৃষ্ঠায় 10 থেকে 50 আইটেম। বড় ছবি সহ তালিকার জন্য, limit = 10-15 ব্যবহার করুন; টেক্সট ডেটার জন্য, 20-50। সর্বদা ক্লায়েন্টকে সার্ভারে সর্বাধিক সীমা (সাধারণত 100) সহ নিজস্ব limit নির্দিষ্ট করার অনুমতি দিন।
ডুপ্লিকেট মোকাবেলায়, অনন্য ID দ্বারা ক্লায়েন্ট-সাইড ডিডুপ্লিকেশন ব্যবহার করুন, অনন্য ফিল্ডে স্থিতিশীল বাছাই প্রয়োগ করুন, অথবা cursor-based পেজিনেশনে সুইচ করুন। Android Paging 3 তালিকা আইটেমের স্বয়ংক্রিয় ডিডুপ্লিকেশনের জন্য key সমর্থন করে।
হ্যাঁ, GraphQL কোয়েরিতে offset এবং limit আর্গুমেন্টের মাধ্যমে অফসেট পেজিনেশন সমর্থন করে, যদিও Relay নির্দেশিকা cursor-based পদ্ধতির সুপারিশ করে। Apollo GraphQL এবং Relay লাইব্রেরিগুলি স্বয়ংক্রিয় পৃষ্ঠা অবস্থা পরিচালনার সাথে অফসেট পেজিনেশনের জন্য অন্তর্নির্মিত সমর্থন সরবরাহ করে।
সারাংশ
আমরা একটি মোবাইল অ্যাপ্লিকেশন টার্নকি তৈরি করব
IT Sectr 2017 সাল থেকে স্টার্টআপ এবং ব্যবসার জন্য iOS এবং Android অ্যাপ্লিকেশন তৈরি করে। আমরা আপনাকে পরামর্শ দেব এবং সেরা সমাধান প্রস্তাব করব।
আরও পড়ুন