Thuật Toán Phân Trang Trong Websites: Khi Nào Nên Dùng Limit/Offset Hay Cursor?

Dịch vụ SEO

Vincent

13/07/2023

11

Thuật toán phân trang trong website là cách chia một danh sách dữ liệu dài thành nhiều phần nhỏ để hệ thống tải theo từng lượt. Hai phương pháp phổ biến là Limit/Offset và cursor pagination. Cách chọn phụ thuộc vào quy mô dữ liệu, nhu cầu nhảy trang, trải nghiệm người dùng và khả năng Google khám phá và lập chỉ mục các URL tiếp theo.

Trong hệ điều hành, thuật toán phân trang có thể nói về quản lý bộ nhớ và các thuật toán như FIFO, LRU hoặc OPT. Bài viết này sẽ chỉ nói về phân trang dữ liệu trong website, cơ sở dữ liệu và API.

Thuật toán phân trang là gì trong website?

Thuật toán phân trang là logic chia một danh sách dài thành nhiều trang nhỏ, thay vì tải toàn bộ dữ liệu trong một lần. Cách làm này thường xuất hiện ở:

  • danh mục sản phẩm
  • danh sách bài viết
  • bảng đơn hàng
  • thư viện tài liệu
  • kết quả tìm kiếm nội bộ

Ví dụ, một website có 2.000 sản phẩm không nhất thiết phải đưa toàn bộ sản phẩm lên cùng một màn hình. Website có thể hiển thị 20 sản phẩm mỗi lượt, sau đó cho người dùng chuyển sang trang 2, trang 3 hoặc dùng nút tải thêm.

Về mặt giao diện, người dùng thường thấy các nút như “Trước”, “Sau”, “1”, “2”, “3”. Tuy nhiên, phía sau những nút đó là một cơ chế truy vấn dữ liệu. Hệ thống cần biết lấy bao nhiêu bản ghi, bắt đầu từ đâu và sắp xếp chúng theo quy tắc nào.

Ví dụ về giao diện phân trang

Khi nào website nên dùng phân trang?

Website nên dùng phân trang khi danh sách dữ liệu đủ dài để việc tải tất cả cùng lúc làm giảm hiệu suất hoặc mức độ kiểm soát của người dùng. Tuy nhiên, không phải mọi danh sách đều cần hiển thị dưới dạng các trang được đánh số.

Ví dụ, một trang quản trị đơn hàng cần cho phép nhân viên chuyển thẳng đến trang 20. Ngược lại, một feed bài viết mới có thể phù hợp hơn với nút “Tải thêm” hoặc cuộn vô hạn, vì người dùng thường chỉ muốn xem tiếp nội dung mới hơn.

Google cũng ghi nhận rằng phân trang, tải thêm và cuộn vô hạn đều có thể giúp giảm lượng nội dung tải ban đầu. Việc chọn cách nào nên dựa trên hành vi người dùng, quy mô dữ liệu và khả năng kỹ thuật của website.

Thuật toán phân trang hoạt động dựa trên những biến nào?

Mọi cơ chế phân trang đều cần xác định dữ liệu nào sẽ được trả về ở lần tải hiện tại. Với phương pháp truyền thống, hệ thống thường dùng tổng số bản ghi, số lượng bản ghi trên mỗi trang, số trang hiện tại và vị trí bắt đầu lấy dữ liệu.

Thành phần

Ý nghĩa

total

Tổng số bản ghi trong danh sách

limit

Số bản ghi hiển thị mỗi trang

page

Trang mà người dùng đang xem

offset

Số bản ghi cần bỏ qua trước khi lấy dữ liệu

cursor

Mốc dùng để lấy nhóm dữ liệu tiếp theo

ORDER BY

Quy tắc sắp xếp dữ liệu ổn định

Ví dụ, website có 95 bài viết và muốn hiển thị 10 bài mỗi trang. Khi đó, tổng số trang là 10. Trang thứ nhất lấy từ bài 1 đến bài 10. Trang thứ hai bắt đầu từ bài 11. Trang cuối chỉ có 5 bài còn lại.

Công thức cơ bản thường là:

totalPages = ceil(total / limit)

offset = (page – 1) × limit

Ví dụ với 95 bài viết, mỗi trang 10 bài:

totalPages = ceil(95 / 10) = 10

Nếu người dùng đang ở trang 3:

offset = (3 – 1) × 10 = 20

Tức là hệ thống bỏ qua 20 bản ghi đầu và lấy 10 bản ghi tiếp theo.

Vì sao phân trang cần thứ tự sắp xếp ổn định?

Phân trang chỉ đáng tin khi danh sách có thứ tự rõ ràng. Nếu hệ thống lấy dữ liệu bằng LIMIT nhưng không có ORDER BY, cơ sở dữ liệu không cam kết các bản ghi sẽ luôn xuất hiện theo cùng một thứ tự giữa nhiều lần truy vấn.

Điều này có thể tạo ra lỗi khó phát hiện. Một bài viết có thể ở trang 2 trong lần tải đầu tiên, nhưng lại chuyển sang trang 3 khi người dùng quay lại. Với danh sách sản phẩm, lỗi này có thể khiến người dùng thấy một sản phẩm bị lặp hoặc bỏ sót.

Vì vậy, nên sắp xếp theo một trường có ý nghĩa. Đó có thể là ngày đăng đối với bài viết, hoặc ngày tạo và mức độ ưu tiên đối với trang sản phẩm.

Tuy nhiên, một trường duy nhất đôi khi vẫn chưa đủ. Nếu nhiều bản ghi có cùng created_at, nên thêm khóa phụ như id để tạo thứ tự ổn định hơn:

ORDER BY created_at DESC, id DESC

MySQL hỗ trợ dùng ORDER BY để sắp xếp dữ liệu trong truy vấn SELECT, còn LIMIT dùng để giới hạn số lượng bản ghi được trả về.

Limit/Offset là gì và phù hợp khi nào?

Limit/Offset là phương pháp phân trang lấy một số lượng bản ghi cố định sau khi bỏ qua một số bản ghi ở đầu danh sách. Đây là cách phổ biến trong website dùng PHP/MySQL vì dễ hiểu, dễ triển khai và phù hợp với giao diện có đánh số trang.

Ví dụ, người dùng đang xem trang 3 với 20 bài viết mỗi trang. Hệ thống có thể bỏ qua 40 bài đầu tiên, sau đó lấy 20 bài tiếp theo.

SELECT id, title, published_at

FROM articles

ORDER BY published_at DESC, id DESC

LIMIT 20 OFFSET 40;

Trong ví dụ này, LIMIT 20 nghĩa là chỉ lấy 20 bản ghi. OFFSET 40 nghĩa là bỏ qua 40 bản ghi đầu tiên.

Phân trang limit offset

Điểm mạnh của Limit/Offset là người dùng có thể nhảy trực tiếp đến một trang cụ thể. Điều này phù hợp với danh mục sản phẩm hoặc danh sách bài viết có số lượng vừa phải.

Tuy nhiên, phương pháp này không phải lúc nào cũng phù hợp. Khi offset rất sâu, chẳng hạn bỏ qua hàng trăm nghìn hoặc hàng triệu bản ghi, truy vấn có thể cần nhiều tài nguyên hơn. Ngoài ra, dữ liệu mới được thêm hoặc bị xóa giữa lúc người dùng chuyển trang cũng có thể khiến kết quả bị lặp hoặc thiếu.

Cursor pagination là gì và phù hợp khi nào?

Cursor pagination là phương pháp lấy dữ liệu dựa trên một mốc đã biết thay vì bỏ qua số lượng bản ghi ở đầu danh sách. Mốc này thường được gọi là cursor, có thể dựa trên id, ngày tạo hoặc kết hợp nhiều trường dữ liệu.

Ví dụ, thay vì yêu cầu “lấy trang 5”, hệ thống yêu cầu “lấy 20 bài viết tiếp theo sau bài có id 1000”. Cách làm này phù hợp với feed tin tức, API mobile hoặc danh sách thay đổi liên tục.

Một URL hoặc API endpoint có thể có dạng:

/articles?limit=20&cursor=eyJpZCI6MTAwMH0

Cursor thường được mã hóa để phía client không cần biết logic dữ liệu bên trong. Khi hệ thống trả về danh sách hiện tại, nó cũng có thể trả về next_cursor để lấy nhóm dữ liệu tiếp theo.

Ví dụ logic SQL đơn giản:

SELECT id, title, published_at

FROM articles

WHERE (published_at, id) < (:last_published_at, :last_id)

ORDER BY published_at DESC, id DESC

LIMIT 20;

Cursor pagination thường phù hợp khi dữ liệu cập nhật liên tục. Nếu có một bài mới xuất hiện ở đầu danh sách, người dùng đang xem phần tiếp theo vẫn ít gặp tình trạng trùng hoặc thiếu dữ liệu hơn so với cách dựa hoàn toàn vào offset.

Đổi lại, cursor không phù hợp khi người dùng cần nhảy thẳng đến trang 50 hoặc trang 100. Người dùng thường chỉ có thể chọn “Xem thêm”, “Trang trước” hoặc “Trang sau”. Vì vậy, cursor không phải lựa chọn tốt hơn trong mọi tình huống.

Nên chọn Limit/Offset hay Cursor?

Limit/Offset phù hợp khi người dùng cần điều hướng theo số trang. Cursor phù hợp hơn khi hệ thống ưu tiên tải tiếp theo, xử lý dữ liệu lớn hoặc làm việc với danh sách thay đổi thường xuyên.

Tình huống

Phương pháp nên cân nhắc

Danh mục sản phẩm có đánh số trang

Limit/Offset

Bảng quản trị có nhiều trang cụ thể

Limit/Offset

Kết quả tìm kiếm nội bộ

Limit/Offset

Feed tin tức cập nhật liên tục

Cursor

API cho ứng dụng di động

Cursor

Infinite scroll

Cursor hoặc cơ chế tải tiếp theo

Danh sách dữ liệu nhỏ

Limit/Offset

Danh sách rất lớn, offset sâu

Cân nhắc Cursor

Điểm cần cân nhắc không chỉ là tốc độ. Team cũng cần xem người dùng có thực sự cần biết mình đang ở trang nào hay không.

Ví dụ, một khách hàng đang tìm sản phẩm theo ngân sách có thể muốn chuyển thẳng đến trang 10. Trong tình huống này, phân trang đánh số thường dễ dùng hơn. Ngược lại, người dùng đọc feed tin tức thường chỉ cần xem nội dung tiếp theo. Cursor hoặc “Tải thêm” sẽ tự nhiên hơn.

Một số hướng dẫn lập trình mô tả Limit/Offset là dễ triển khai nhưng có thể gặp vấn đề khi danh sách rất lớn hoặc thay đổi liên tục. Cursor có thể hạn chế một số tình huống này, nhưng yêu cầu logic dữ liệu chặt hơn và không hỗ trợ nhảy trực tiếp theo số trang.

Cách phân trang cơ bản trong PHP và MySQL

Với PHP và MySQL, quy trình cơ bản vẫn gồm bốn bước: đếm tổng số bản ghi, xác định trang hiện tại, tính offset và truy vấn dữ liệu theo giới hạn đã chọn. Đây là phần nên giữ từ bài cũ vì nó phù hợp với nhóm người tìm kiếm đang muốn áp dụng pagination vào website PHP.

Trước hết, cần xác định số bản ghi trên mỗi trang:

$limit = 20;

Sau đó, lấy số trang từ URL và kiểm tra giá trị tối thiểu:

$page = max(1, (int) ($_GET[‘page’] ?? 1));

Tiếp theo, hệ thống cần biết tổng số bản ghi. Với ví dụ danh sách bài viết:

SELECT COUNT(*) AS total

FROM articles;

Sau khi có $total, có thể tính tổng số trang:

$totalPages = (int) ceil($total / $limit);

Để tránh người dùng truy cập một trang không tồn tại, nên giới hạn giá trị page:

$page = min($page, max(1, $totalPages));

Sau đó, tính vị trí bắt đầu:

$offset = ($page – 1) * $limit;

Truy vấn dữ liệu nên có ORDER BY rõ ràng. Đồng thời, nên dùng prepared statement thay vì ghép trực tiếp dữ liệu từ URL vào câu SQL:

$sql = “

   SELECT id, title, published_at

   FROM articles

   ORDER BY published_at DESC, id DESC

   LIMIT :limit OFFSET :offset

“;

$stmt = $pdo->prepare($sql);

$stmt->bindValue(‘:limit’, $limit, PDO::PARAM_INT);

$stmt->bindValue(‘:offset’, $offset, PDO::PARAM_INT);

$stmt->execute();

$articles = $stmt->fetchAll();

Khi hiển thị liên kết phân trang, người dùng cần có ít nhất ba yếu tố:

  • biết đang ở trang nào
  • quay lại trang trước được
  • tiếp tục đến trang sau được

Với danh sách quá dài, không cần hiển thị toàn bộ 100 trang cùng lúc. Có thể chỉ hiển thị một nhóm trang gần trang hiện tại, kết hợp dấu ba chấm và liên kết đến trang đầu hoặc cuối.

Pagination, Load more và infinite scroll khác nhau thế nào?

Pagination đánh số, nút “Tải thêm” và cuộn vô hạn đều là cách chia một danh sách dài thành nhiều phần. Khác biệt nằm ở cách người dùng yêu cầu dữ liệu tiếp theo và cách website tổ chức URL phía sau.

Cách hiển thị

Phù hợp khi

Điểm mạnh

Điểm cần lưu ý

Pagination đánh số

Danh mục, bảng dữ liệu, kết quả tìm kiếm

Biết vị trí, nhảy được đến trang cụ thể

Cần URL riêng cho từng trang

Tải thêm

Blog, danh sách nội dung ngắn

Giao diện gọn, ít chuyển trang

Cần có phương án để crawler tìm nội dung phía sau

Cuộn vô hạn

Feed, nội dung khám phá liên tục

Trải nghiệm liền mạch trên mobile

Dễ gây mệt mỏi khi cuộn quá lâu

Google khuyến nghị chọn mô hình phù hợp với trải nghiệm người dùng. Pagination giúp người dùng biết vị trí hiện tại. Tải thêm và cuộn vô hạn có thể giúp giảm lượng dữ liệu tải ban đầu, nhưng không phù hợp với mọi danh sách lớn.

Từ góc độ SEO, vấn đề không nằm ở việc dùng nút “Tải thêm” hay cuộn vô hạn. Vấn đề là liệu Google có thể phát hiện được các URL hoặc nội dung nằm sau thao tác đó hay không.

Phân trang ảnh hưởng đến SEO như thế nào?

Phân trang không tự gây hại cho SEO. Tuy nhiên, website cần triển khai URL, liên kết và canonical đúng để Google có thể tìm thấy nội dung ở các trang tiếp theo và hiểu mối quan hệ giữa chúng.

Mỗi trang phân trang cần có URL riêng

Google xem các URL trong một chuỗi pagination là các trang riêng. Vì vậy, trang 2, trang 3 hoặc trang 4 cần có URL có thể truy cập được, chẳng hạn ?page=2 hoặc cấu trúc URL tương đương.

Không nên dùng fragment như #page-2 để tạo trang tiếp theo. Google bỏ qua phần sau dấu #, vì vậy crawler có thể xem đây là cùng một URL thay vì một trang danh sách khác.

Liên kết đến trang tiếp theo cần crawlable

Google thường tìm URL trong thuộc tính href của thẻ <a>. Googlebot không nhấp vào button và thường không kích hoạt JavaScript yêu cầu người dùng thao tác để thay đổi nội dung đang hiển thị.

Điều này có nghĩa một nút “Tải thêm” chỉ chạy bằng JavaScript có thể không đủ để Google phát hiện nội dung phía sau. Website vẫn nên có URL hoặc liên kết HTML thực để crawler đi đến các trang tiếp theo.

Trong chuỗi pagination, mỗi trang nên có liên kết đến trang kế tiếp. Ngoài ra, có thể liên kết các trang trong cùng danh mục về trang đầu để giúp Google hiểu đâu là điểm bắt đầu của danh sách. Đây cũng là lý do liên kết nội bộ cần được triển khai có cấu trúc, thay vì chỉ chèn ngẫu nhiên.

Pagination nên có URL thật và liên kết HTML, không chỉ dựa vào button JavaScript

Mỗi trang nên tự canonical về chính nó

Một lỗi phổ biến là canonical toàn bộ page 2, page 3 và page 4 về page 1. Cách này không phù hợp khi mỗi trang chứa nhóm sản phẩm hoặc bài viết khác nhau.

Google khuyến nghị mỗi trang trong chuỗi phân trang nên có URL canonical riêng. Trang 2 tự canonical về trang 2. Trang 3 tự canonical về trang 3. Không nên dùng trang đầu tiên làm canonical cho toàn bộ chuỗi.

Canonical chỉ nên dùng để xử lý các phiên bản trùng hoặc rất giống nhau. Vì vậy, nếu website có URL filter, sort hoặc tracking tạo ra nhiều biến thể cùng danh sách, cần kiểm tra nhóm URL nào nên được index và nhóm nào không.

Không cần dựa vào rel=next và rel=prev

Google từng sử dụng rel=”next” và rel=”prev” để nhận diện quan hệ giữa các trang trong chuỗi pagination. Tuy nhiên, Google hiện không còn dùng hai thuộc tính này cho mục đích đó.

Điều quan trọng hơn là URL thật, liên kết <a href> thật và canonical đúng cho từng trang. Nếu hệ thống vẫn dùng rel=next hoặc rel=prev vì lý do tương thích với công cụ khác, điều đó không gây vấn đề. Tuy nhiên, không nên xem đây là giải pháp SEO chính.

Kiểm soát URL filter và sort

Website thương mại điện tử thường có nhiều URL sinh ra từ bộ lọc. Ví dụ, cùng một danh mục có thể được sắp xếp theo giá, mức giảm giá, màu sắc, thương hiệu hoặc tình trạng còn hàng.

Không phải mọi URL filter đều cần index. Nếu Google index hàng loạt các phiên bản gần giống nhau, website có thể tạo ra quá nhiều URL không mang giá trị tìm kiếm riêng. Google khuyến nghị tránh index các URL có bộ lọc hoặc thứ tự sắp xếp thay thế khi chúng chỉ trả về biến thể của cùng một danh sách.

Sitemap cũng nên phản ánh các URL canonical thật sự cần index. Không nên đưa URL filter, URL redirect hoặc URL noindex vào XML sitemap.

Những lỗi phân trang thường gặp

Lỗi phân trang thường không nằm ở nút “1, 2, 3”. Nhiều vấn đề xuất phát từ dữ liệu không ổn định, URL không crawlable hoặc sự không nhất quán giữa database, API và giao diện.

  • Không có ORDER BY: Khi không có thứ tự sắp xếp rõ, dữ liệu giữa các trang có thể thay đổi khó dự đoán.
  • Cho phép page vượt phạm vi: Người dùng vào ?page=9999 có thể nhận trang trắng hoặc lỗi. Website cần xử lý rõ ràng bằng redirect phù hợp, thông báo hoặc giới hạn page hợp lệ.
  • Dùng offset sâu cho danh sách lớn mà không kiểm tra hiệu suất: Khi dữ liệu tăng, truy vấn cần được benchmark thay vì giữ nguyên logic cũ.
  • Canonical page 2 về page 1: Điều này có thể khiến Google khó hiểu rằng các trang sau chứa nhóm nội dung khác.
  • Chỉ dựa vào JavaScript để tải nội dung tiếp theo: Người dùng có thể xem được, nhưng crawler chưa chắc phát hiện hết các phần dữ liệu phía sau.
  • Index tất cả URL filter và sort: Điều này tạo thêm URL trùng hoặc gần trùng mà không mang lại giá trị tìm kiếm riêng.
  • Không kiểm tra trang đầu, trang giữa và trang cuối: Một pagination chỉ được test ở trang 1 thường chưa đủ. Cần kiểm tra dữ liệu có bị lặp, thiếu hoặc sai thứ tự hay không.

Checklist kiểm tra thuật toán phân trang

Trước khi publish hoặc deploy tính năng phân trang, team nên kiểm tra cả logic dữ liệu, trải nghiệm người dùng và khả năng Google truy cập nội dung.

  1. Danh sách có ORDER BY ổn định.
  2. page, limit và offset được kiểm tra hợp lệ.
  3. Không có dữ liệu trùng giữa hai trang liên tiếp.
  4. Trang cuối không tạo lỗi hoặc danh sách rỗng khó hiểu.
  5. Page 2 trở đi có URL riêng.
  6. Liên kết “Trang sau” và số trang dùng <a href>.
  7. Mỗi URL pagination tự canonical về chính nó.
  8. URL filter và sort không index ngoài ý muốn.
  9. Nút “Tải thêm” hoặc infinite scroll có phương án để crawler phát hiện nội dung phía sau.
  10. Sản phẩm hoặc bài viết nằm ở trang sâu vẫn có internal link hoặc sitemap hỗ trợ discovery.

Câu hỏi thường gặp về thuật toán phân trang

Thuật toán phân trang có phải FIFO, LRU hoặc OPT không?

Không. FIFO, LRU và OPT thường thuộc nhóm thuật toán thay trang trong quản lý bộ nhớ của hệ điều hành. Thuật toán phân trang trong website nói về việc chia danh sách dữ liệu như sản phẩm, bài viết hoặc đơn hàng thành nhiều lượt hiển thị.

Limit và Offset là gì?

LIMIT là số lượng bản ghi cần lấy trong một lần truy vấn. OFFSET là số bản ghi cần bỏ qua trước khi lấy dữ liệu. Ví dụ, LIMIT 20 OFFSET 40 thường lấy 20 bản ghi sau khi bỏ qua 40 bản ghi đầu.

Khi nào nên dùng cursor pagination?

Cursor pagination phù hợp khi danh sách cập nhật liên tục, có lượng dữ liệu lớn hoặc người dùng chủ yếu xem tiếp nội dung thay vì nhảy đến một trang cụ thể. Đây là cách thường gặp ở API, ứng dụng di động và feed nội dung.

Vì sao pagination cần ORDER BY?

ORDER BY giúp dữ liệu xuất hiện theo thứ tự ổn định. Nếu không có quy tắc sắp xếp, cùng một bản ghi có thể xuất hiện ở các trang khác nhau giữa nhiều lần truy vấn. Điều này dễ gây lặp hoặc thiếu dữ liệu trong trải nghiệm người dùng.

Page 2 có nên canonical về page 1 không?

Không nên khi page 2 chứa nhóm nội dung khác page 1. Google khuyến nghị mỗi trang trong chuỗi pagination có canonical tự tham chiếu thay vì canonical toàn bộ về trang đầu.

Google có dùng rel=next và rel=prev không?

Google không còn dùng rel=next và rel=prev để nhận diện chuỗi phân trang. Website nên ưu tiên URL riêng cho từng trang, liên kết tuần tự bằng <a href> và canonical đúng cho từng URL.

Infinite scroll có ảnh hưởng đến SEO không?

Infinite scroll không tự gây hại cho SEO. Tuy nhiên, nếu nội dung tiếp theo chỉ xuất hiện sau thao tác cuộn hoặc click JavaScript mà không có URL hoặc liên kết crawlable, Google có thể khó phát hiện các phần dữ liệu phía sau.

Kết luận

Thuật toán phân trang không chỉ là hiển thị các nút 1, 2, 3 ở cuối danh sách. Phần quan trọng hơn là cách database lấy dữ liệu, API gửi dữ liệu, giao diện hỗ trợ người dùng tiếp tục xem nội dung và Google phát hiện các trang tiếp theo.

Với danh mục nhỏ hoặc bảng quản trị, Limit/Offset thường đủ dùng. Khi dữ liệu lớn hoặc thay đổi liên tục, cursor pagination có thể phù hợp hơn. Dù chọn cách nào, website vẫn cần thứ tự dữ liệu nhất quán, URL rõ ràng, liên kết crawlable và canonical đúng để pagination hoạt động tốt cho cả người dùng lẫn công cụ tìm kiếm.

Vincent On
AUTHOR

Vincent On

Vincent On is the Founder & Managing Director of On Digitals. With a background in Information Technology and Information Systems from Deakin University, Melbourne, he connects strategy, data and execution into one accountable growth system — across SEO, content, media, outreach and technology. His articles help marketing leaders turn search and AI visibility into measurable business growth.


Quay lại danh sách

Đọc thêm

    CẦN GIÚP ĐỠ để phát triển kỹ thuật số?
    Hãy cho chúng tôi biết về thách thức kinh doanh của bạn và cùng nhau thảo luận