Desain halaman pagination di API

Bisa jadi sulit bagi API untuk mengembalikan semua hasil kueri, terutama jika jumlahnya ribuan. Hal ini menciptakan beban di server, di klien, di jaringan dan seringkali tidak diperlukan. Oleh karena itu, kami membuat pagination.



Cara paging biasa adalah dengan offset atau nomor halaman. Anda membuat permintaan seperti ini:



GET /api/products?page=10
{"items": [...100 products]}
      
      





lalu ini:



GET /api/products?page=11
{"items": [...another 100 products]}
      
      





Dalam kasus perpindahan sederhana, ternyata ?offset=1000



dan ?offset=1100



- telur yang sama, hanya dalam profil. Di sini kita langsung ke kueri SQL dari jenisnya OFFSET 1000 LIMIT 100



, atau mengalikannya dengan ukuran halaman (nilai LIMIT



). Ini bukanlah solusi yang optimal, karena setiap database harus melewati 1000 baris tersebut. Dan untuk melewatinya, Anda perlu mengidentifikasinya. Tidak masalah apakah itu PostgreSQL, ElasticSearch, atau MongoDB, ia harus memesan, menghitung ulang, dan membuangnya.



Ini pekerjaan yang tidak perlu. Tapi itu berulang berulang kali, karena desain ini mudah diimplementasikan - Anda langsung memetakan API Anda ke permintaan database.



Apa yang kemudian harus diselesaikan? Kita bisa melihat bagaimana database bekerja! Mereka memiliki konsep kursor - ini adalah penunjuk ke string. Jadi Anda dapat memberi tahu database, "Kembalikan 100 baris setelah ini ." Dan kueri seperti itu jauh lebih nyaman untuk database, karena ada kemungkinan besar Anda mengidentifikasi baris per bidang dengan indeks. Dan Anda tidak perlu mengambil dan melewati garis-garis ini, Anda akan berjalan tepat di dekatnya.



Contoh:



GET /api/products
{"items": [...100 products],
 "cursor": "qWe"}
      
      





API mengembalikan string (buram), yang kemudian dapat digunakan untuk mendapatkan halaman berikutnya:



GET /api/products?cursor=qWe
{"items": [...100 products],
 "cursor": "qWr"}
      
      





Dari segi implementasi, ada banyak pilihan. Biasanya, Anda memiliki beberapa kriteria kueri, seperti id produk. Dalam hal ini, Anda menyandikannya dengan beberapa algoritme yang dapat dibalik (katakanlah pengenal hash ). Dan saat Anda menerima kueri dengan kursor, Anda mendekodekannya dan menghasilkan kueri seperti WHERE id > :cursor LIMIT 100



.



Perbandingan kinerja kecil. Berikut adalah hasil dari offsetnya: Dan inilah hasil operasinya : Selisih beberapa kali lipat! Tentu saja, angka sebenarnya bergantung pada ukuran tabel, filter, dan implementasi penyimpanan. Ini artikel yang bagus



=# explain analyze select id from product offset 10000 limit 100;

QUERY PLAN

---------------------------------------------------------------------------------------------------------------------------------

Limit (cost=1114.26..1125.40 rows=100 width=4) (actual time=39.431..39.561 rows=100 loops=1)

-> Seq Scan on product (cost=0.00..1274406.22 rows=11437243 width=4) (actual time=0.015..39.123 rows=10100 loops=1)

Planning Time: 0.117 ms

Execution Time: 39.589 ms








where







=# explain analyze select id from product where id > 10000 limit 100;

QUERY PLAN

------------------------------------------------------------------------------------------------------------------------------

Limit (cost=0.00..11.40 rows=100 width=4) (actual time=0.016..0.067 rows=100 loops=1)

-> Seq Scan on product (cost=0.00..1302999.32 rows=11429082 width=4) (actual time=0.015..0.052 rows=100 loops=1)

Filter: (id > 10000)

Planning Time: 0.164 ms

Execution Time: 0.094 ms








untuk informasi lebih teknis, lihat slide 42 untuk perbandingan kinerja.





Tentu saja, tidak ada yang menanyakan produk berdasarkan ID - mereka biasanya ditanyai untuk beberapa jenis relevansi (dan kemudian ID sebagai parameter yang menentukan ). Di dunia nyata, memilih solusi membutuhkan melihat data tertentu. Permintaan dapat diurutkan berdasarkan pengenal (karena meningkat secara monoton). Item dari daftar pembelian di masa mendatang juga dapat diurutkan dengan cara ini - pada saat daftar tersebut disusun. Dalam kasus kami, produk dimuat dari ElasticSearch, yang secara alami mendukung kursor tersebut.



Kelemahannya adalah Anda tidak dapat membuat tautan Halaman Sebelumnya menggunakan API tanpa negara. Dalam kasus penomoran halaman pengguna, tidak ada cara untuk mengatasi masalah ini. Jadi jika penting untuk memiliki tombol untuk halaman sebelumnya / berikutnya dan "Langsung ke halaman 10", maka Anda harus menggunakan metode lama. Namun dalam kasus lain, metode kursor by cursor dapat meningkatkan kinerja secara signifikan, terutama pada tabel yang sangat besar dengan penomoran halaman yang sangat dalam.



All Articles