Mengapa Paginasi SQL Menggunakan OFFSET Bisa Menjadi Lambat ?
Fathullah Munadi
9/7/2024, 12:59:08 AM
Paginasi adalah kebutuhan umum dalam aplikasi web yang perlu menampilkan dataset besar secara efisien. Banyak pengembang menggunakan query SQL dengan klausa ORDER BY
dan LIMIT
, sering kali menggabungkannya dengan offset seperti LIMIT 10 OFFSET 20
. Namun, seiring pertumbuhan dataset, metode ini dapat menyebabkan masalah performa.
Artikel ini akan mengeksplorasi masalah performa dari penggunaan ORDER BY
dalam paginasi dan memperkenalkan solusi yang lebih efisien dengan menggunakan id < current_id LIMIT 10
.
Pendekatan Paginasi Umum: ORDER BY
dan LIMIT
Query SQL yang umum digunakan untuk paginasi daftar record mungkin terlihat seperti ini:
```
SELECT * FROM posts ORDER BY created_at DESC LIMIT 10 OFFSET 20;
```
Di sini, klausa ORDER BY
menyortir hasil berdasarkan kolom created_at
(dalam urutan menurun), dan klausa LIMIT
serta OFFSET
mendefinisikan set baris mana yang akan dikembalikan untuk halaman saat ini.
Masalah Performa dengan OFFSET
Meskipun pendekatan ini berfungsi dengan baik untuk dataset kecil, ia bisa menjadi tidak efisien seiring dengan pertumbuhan jumlah record. Berikut alasannya:
Pemindaian Penuh dengan OFFSET: Basis data perlu memindai dan menyortir semua baris untuk menerapkan klausa
ORDER BY
. Misalnya, jika Anda berada di halaman 100 denganOFFSET 1000
, basis data akan mengambil 1000 baris pertama sebelum membuangnya dan mengembalikan baris 1001–1010. Ini menjadi lambat seiring dengan meningkatnya offset.Waktu Respons yang Meningkat: Seiring dengan meningkatnya offset, waktu untuk mengambil halaman berikutnya juga meningkat. Peningkatan linier dalam waktu eksekusi ini menghasilkan performa yang buruk, terutama untuk dataset besar.
Solusi Paginasi yang Efisien: Menggunakan id < current_id LIMIT 10
Metode alternatif untuk paginasi adalah menggunakan kunci utama (atau kolom terindeks lainnya) untuk menghindari offset yang besar dan penyortiran yang berlebihan. Alih-alih bergantung pada OFFSET
, Anda dapat menggunakan ID dari record terakhir pada halaman sebelumnya sebagai titik awal untuk query berikutnya. Metode ini sering disebut "paginasi kunci" atau "paginasi berbasis kursor".
Query yang dioptimalkan terlihat seperti ini:
```
SELECT * FROM posts WHERE id < :last_id ORDER BY id DESC LIMIT 10;
```
Dalam hal ini:
id
adalah kunci utama (atau kolom unik yang terindeks) yang digunakan untuk menyortir hasil.:last_id
adalah ID dari record terakhir dari halaman sebelumnya, yang memastikan set record berikutnya datang tepat setelah halaman saat ini.LIMIT 10
hanya mengambil 10 baris berikutnya, tanpa memindai baris sebelumnya.
Mengapa id < current_id
Lebih Efisien
Menghindari Pemindaian Penuh: Alih-alih memindai semua baris hingga offset saat ini, basis data hanya melihat baris setelah ID yang diberikan. Ini secara signifikan mengurangi jumlah data yang diproses, terutama pada tabel besar.
Performa Terindeks: Jika kolom
id
terindeks (yang biasanya merupakan kasus untuk kunci utama), eksekusi query sangat cepat karena basis data dapat langsung melompat ke baris yang relevan.Waktu Respons Konsisten: Berbeda dengan pendekatan berbasis
OFFSET
, di mana performa menurun seiring meningkatnya offset, pendekatanid < current_id
memberikan waktu respons yang konsisten terlepas dari seberapa dalam dataset yang Anda telusuri.
Implementasi Contoh dalam Kode
Untuk mengimplementasikan solusi ini, Anda perlu melacak ID dari record terakhir dari halaman sebelumnya. Berikut adalah cara melakukannya dalam aplikasi web yang umum:
Langkah 1: Query halaman pertama
```
SELECT * FROM posts ORDER BY id DESC LIMIT 10;
```
Ini mengambil 10 post terbaru.
Misalkan ID dari post terakhir dalam hasil adalah 150.
Langkah 2: Query halaman berikutnya
```
SELECT * FROM posts WHERE id < 150 ORDER BY id DESC LIMIT 10;
```
Ini mengambil 10 post berikutnya, di mana
id
kurang dari 150.Setelah mengambil ini, Anda menyimpan ID dari record terakhir (misalnya, 140) untuk query halaman berikutnya.
Langkah 3: Ulangi untuk halaman berikutnya Untuk setiap halaman berikutnya, Anda akan melewatkan ID dari record terakhir dari halaman sebelumnya dan terus query dengan id < last_id
.
Menangani Kasus Tepi
Tidak Ada Lagi Hasil: Ketika tidak ada lagi record yang tersisa (misalnya, jika hasil mengembalikan kurang dari 10 baris), aplikasi dapat mendeteksi bahwa tidak ada halaman lagi yang perlu dimuat.
Data Baru Ditambahkan: Jika baris baru ditambahkan sementara pengguna sedang melakukan paginasi, baris baru tersebut akan muncul di halaman-halaman mendatang, menjaga konsistensi.
ID yang Hilang (Kesenjangan dalam ID): Meskipun beberapa record dihapus, metode ini tetap bekerja karena hanya melihat baris di mana
id < current_id
, mengabaikan kesenjangan dalam urutanid
.
Contoh Kasus Penggunaan dalam Aplikasi Web
Berikut adalah contoh dasar dalam PHP menggunakan pendekatan ini untuk paginasi posting blog:
```
// Ambil 'last_id' dari permintaan sebelumnya (untuk halaman 2 dan seterusnya)
$last_id = $_GET['last_id'] ?? null;
// Query halaman pertama (tidak ada last_id yang diberikan)
if ($last_id === null) {
$sql = "SELECT * FROM posts ORDER BY id DESC LIMIT 10";
} else {
// Query untuk halaman berikutnya
$sql = "SELECT * FROM posts WHERE id < ? ORDER BY id DESC LIMIT 10";
$stmt = $conn->prepare($sql);
$stmt->bind_param("i", $last_id);
$stmt->execute();
}
```
Dalam contoh ini, untuk halaman pertama, query mengambil 10 post terbaru. Untuk halaman-halaman berikutnya, query mengambil post-post di mana id
kurang dari ID post terakhir dari halaman sebelumnya.
Kesimpulan
Menggunakan id < current_id LIMIT
untuk paginasi adalah alternatif yang sangat efisien dibandingkan dengan query tradisional ORDER BY ... LIMIT OFFSET
, terutama dalam aplikasi dengan dataset besar. Dengan menghindari dampak performa dari offset yang besar, metode ini memastikan paginasi yang cepat dan konsisten. Ini memanfaatkan pengindeksan kunci utama dan memastikan pengalaman pengguna yang mulus, bahkan saat melakukan paginasi melalui data dalam jumlah besar.
Pendekatan ini sangat berguna ketika menampilkan konten dengan scroll tak berujung atau ketika kecepatan dan skalabilitas adalah prioritas dalam aplikasi berbasis data besar.