List adalah urutan nilai string yang tersimpan menurut urutan penyisipannya, dengan penyisipan dan pengambilan yang dapat dilakukan dari kedua ujung. Topik ini membahas penambahan dari kepala dan ekor dengan LPUSH dan RPUSH, pengambilan dari kedua ujung dengan LPOP dan RPOP, pembatasan panjang dengan LTRIM, serta penyusunan antrean sederhana produsen-konsumen dengan bantuan BLPOP. Perbandingan terhadap tipe stream melengkapi pembahasan ini. Semua contoh menggunakan satu key antrean tiket loket agar perubahan isi list dapat diikuti dari awal sampai akhir.
Menambah dan membaca dari kedua ujung#
RPUSH menambahkan nilai di ekor list, sedangkan LPUSH menambahkannya di kepala. Keduanya mengembalikan panjang list setelah penambahan, sehingga keluarannya bukan jumlah elemen yang baru ditambahkan. Kedua perintah menerima beberapa nilai dalam satu pemanggilan, dan penambahan pada salah satu ujung berlangsung dengan biaya yang tetap berapa pun panjang listnya.
LRANGE membaca sebagian atau seluruh isi berdasarkan indeks. Indeks 0 adalah elemen kepala dan indeks -1 adalah elemen ekor, sehingga rentang 0 sampai -1 mengembalikan seluruh isi sesuai urutan penyimpanannya. Susunan ini membuat list cocok untuk kebutuhan yang menyentuh dua ujungnya, misalnya tiket yang diambil dari ujung depan sementara tiket baru masuk dari ujung belakang.
# menghapus list lama agar isi selalu mulai dari kosong, keluarannya diabaikan
redis-cli del antrean:tiket >/dev/null
# pelanggan pertama mengambil nomor
redis-cli rpush antrean:tiket "A-101"
# pelanggan berikutnya
redis-cli rpush antrean:tiket "A-102"
redis-cli rpush antrean:tiket "A-103"
# membaca seluruh isi dari kepala ke ekor
redis-cli lrange antrean:tiket 0 -11
2
3
A-101
A-102
A-103Perintah DEL pada awal contoh membuang isi lama sehingga angka yang dikembalikan RPUSH selalu panjang list yang sama pada setiap eksekusi. Nilai kembalian DEL hanyalah jumlah key yang terhapus dan bukan bagian penting dari pola ini, sehingga keluarannya diabaikan. Contoh berikut menunjukkan penyisipan dari kedua ujung beserta pengambilannya.
# dua tiket masuk dari ekor
redis-cli del antrean:tiket >/dev/null
redis-cli rpush antrean:tiket "A-101" "A-102"
# satu tiket prioritas masuk dari kepala
redis-cli lpush antrean:tiket "A-100"
# kepala kini berisi tiket prioritas
redis-cli lrange antrean:tiket 0 -1
# melayani dari ujung depan
redis-cli lpop antrean:tiket
# membuang dari ujung belakang
redis-cli rpop antrean:tiket
# sisa isi setelah dua pengambilan
redis-cli lrange antrean:tiket 0 -12
3
A-100
A-101
A-102
A-100
A-102
A-101List berbatas dengan LTRIM#
LTRIM hanya menyimpan rentang indeks tertentu dan membuang sisanya, sehingga panjang list dapat dijaga tetap. Rentang yang dipertahankan bersifat inklusif pada kedua batasnya, dan indeks negatif sah digunakan pada kedua batas tersebut. Pola yang lazim adalah LPUSH diikuti LTRIM 0 2, yakni selalu menambah entri terbaru di kepala lalu menyimpan tiga entri paling atas saja. Susunan ini menghasilkan list yang selalu berisi entri terbaru dengan biaya memori yang tetap, cocok untuk papan panggilan yang hanya menampilkan beberapa nomor terakhir. Secara internal, Redis menyimpan list sebagai quicklist, yaitu rangkaian simpul kecil yang masing-masing menampung sekelompok elemen berurutan, sehingga penyisipan pada ujung tidak perlu memindahkan seluruh isi.
LLEN menghitung panjang list tanpa membaca isinya, sedangkan LINDEX membaca satu elemen pada indeks tertentu tanpa menghapusnya. Keduanya melengkapi LRANGE pada kebutuhan pemantauan, misalnya memeriksa sisa entri sebelum menentukan pemangkasan berikutnya. Bentuk balasannya berbeda, yaitu LLEN mengembalikan satu bilangan, LINDEX mengembalikan satu nilai, dan LRANGE mengembalikan banyak nilai sekaligus. LINDEX menelusuri list dari ujung terdekat menuju indeks yang diminta, sehingga pembacaan elemen di tengah list yang panjang lebih lambat daripada pengambilan di ujung.
# menghapus isi lama, keluarannya diabaikan
redis-cli del antrean:tiket >/dev/null
# lima panggilan sesi berikutnya masuk dari kepala
redis-cli lpush antrean:tiket "B-201" "B-202" "B-203" "B-204" "B-205"
# panjang sebelum dipangkas
redis-cli llen antrean:tiket
# menyimpan tiga entri teratas saja
redis-cli ltrim antrean:tiket 0 2
# panjang setelah dipangkas
redis-cli llen antrean:tiket
# isi papan panggilan
redis-cli lrange antrean:tiket 0 -1
# elemen kedua tetap dapat dibaca tanpa dihapus
redis-cli lindex antrean:tiket 15
5
OK
3
B-205
B-204
B-203
B-204Antrean sederhana dengan LPOP dan BLPOP#
Antrean produsen-konsumen paling sederhana pada Redis dibangun dari dua perintah saja. Sisi produsen menambah pekerjaan dengan RPUSH, sedangkan sisi konsumen mengambil pekerjaan dari ujung berlawanan dengan LPOP. Apabila list kosong, LPOP mengembalikan nil tanpa menunggu, sehingga konsumen yang memeriksa berulang kali harus melakukan polling. BLPOP menyelesaikan masalah tersebut dengan menahan pemanggilan sampai ada elemen atau sampai waktu tunggu habis. Nilai kembalian BLPOP terdiri atas nama key dan elemennya, informasi yang berguna ketika satu konsumen mengawasi beberapa antrean sekaligus.
# menghapus antrean lama, keluarannya diabaikan
redis-cli del antrean:tiket >/dev/null
# produsen menambahkan tiga tiket
redis-cli rpush antrean:tiket "C-201" "C-202" "C-203"
# konsumen pertama diambil tanpa menunggu
redis-cli lpop antrean:tiket
# konsumen berikutnya menunggu paling lama satu detik bila kosong
redis-cli blpop antrean:tiket 1
redis-cli blpop antrean:tiket 1
# panjang antrean kini nol
redis-cli llen antrean:tiket3
C-201
antrean:tiket
C-202
antrean:tiket
C-203
0Pengambilan satu elemen berlangsung secara atomik, sehingga dua konsumen yang menunggu pada list yang sama tidak pernah menerima tiket yang sama. Perintah pengambilan menyerahkan elemen kepada satu koneksi pemanggil saja, sedangkan koneksi lain tetap menunggu tiket berikutnya. Arah penyusunannya juga dapat dibalik, misalnya LPUSH untuk menambah dan RPOP untuk mengambil, asalkan kedua ujung yang digunakan berbeda.
Perbandingan list dengan stream#
List menyelesaikan antrean sederhana dengan baik, tetapi perilaku penghapusannya membatasi penggunaan lain. Elemen yang dikeluarkan dengan LPOP atau RPOP langsung hilang dari list, sehingga tidak ada riwayat yang dapat dibaca ulang oleh pembaca lain. Redis menyediakan tipe stream untuk kebutuhan tersebut, yaitu log yang hanya dapat ditambah (append-only) dengan ID unik pada setiap entri. Beberapa pembaca dapat menelusuri isi stream dari titik ID mana pun tanpa menggeser isi bagi pembaca lain, dan consumer group mengatur pembagian pesan antar anggota kelompoknya.
Pilihan antara keduanya bergantung pada rentang hidup data dan perannya. List cocok untuk pekerjaan pendek yang sekali diambil, seperti tiket loket dan pekerjaan latar yang hilang setelah diproses. Stream cocok untuk riwayat peristiwa yang perlu dibaca ulang, misalnya catatan sensor suhu dan log aktivitas aplikasi. Apabila kebutuhannya hanya antrean dengan elemen yang dibuang setelah diambil, list memberikan cara yang paling ringkas, dan BLPOP sudah menutup kebutuhan tunggunya.
Kesalahan umum#
Kesalahan pertama adalah membaca nilai kembalian RPUSH sebagai jumlah elemen yang baru ditambah. Nilai kembaliannya adalah panjang list setelah penambahan, sehingga RPUSH dua nilai pada list yang berisi satu elemen mengembalikan 3. Kesalahan kedua adalah memperlakukan balasan nil dari LPOP sebagai error. Balasan nil hanya berarti list kosong atau tidak ada, dan konsumen cukup mencoba lagi nanti.
Kesalahan ketiga adalah salah memahami batas LTRIM. Rentang yang dipertahankan inklusif pada kedua batas, sehingga LTRIM 0 2 menyimpan tiga elemen dan bukan dua. Indeks negatif juga sering membingungkan, padahal aturannya sederhana: -1 adalah elemen terakhir, -2 elemen sebelum itu, dan seterusnya.
Kesalahan keempat adalah membiarkan list antrean tumbuh tanpa pengawasan. Produsen yang lebih cepat daripada konsumennya menaikkan panjang list terus-menerus, sehingga penggunaan memori naik mengikuti selisih lajunya. LLEN perlu diperiksa secara berkala untuk mendeteksi pertumbuhan tersebut, dan list yang isinya hanya relevan dalam jendela waktu tertentu sebaiknya dibatasi dengan LTRIM atau dihapus setelah tidak diperlukan.
Ringkasan#
- RPUSH menambah di ekor dan LPUSH menambah di kepala, sementara LPOP dan RPOP mengambil dari ujung yang berlawanan.
- LRANGE membaca rentang indeks dengan -1 menandai elemen terakhir, sedangkan LINDEX membaca satu elemen tanpa menghapus.
- LTRIM memangkas list ke rentang inklusif tertentu sehingga pola LPUSH ditambah LTRIM menghasilkan list berbatas.
- BLPOP menunggu elemen sampai batas waktu tertentu dan mengembalikan pasangan nama key beserta elemennya.
Referensi#
- Lists — Redis — Dokumentasi tipe data list beserta pola antrean dan tumpukan.
- LTRIM — Redis — Dokumentasi perintah LTRIM beserta semantik rentang inklusifnya.
- BLPOP — Redis — Dokumentasi perintah BLPOP dengan perilaku pemblokiran dan batas waktunya.