Redis University RU101 - Minggu Ke-2

Redis University RU101 - Minggu Ke-2

Ini adalah catatan minggu ke-2 dari kelas RU101 (Introduction to Redis Data Structure) di Redis University. Buat yang belum tau apa itu Redis University, saya sudah bahas di catatan minggu pertama.

Pada minggu pertama yang dibahas adalah tentang data struktur apa saja yang terdapat pada Redis, pada minggu ke-2 ini pembahasannya adalah:

  1. Cardinality and Capped Collections
  2. Set Operations
  3. Faceted Search
  4. Performance & Big O Notation

1. Cardinality and Capped Collections

Cardinality

Untuk mengecek cardinality dari data di Redis, perintah yang dapat digunakan adalah:

  • LLEN: untuk list
  • SCARD: untuk set
  • ZCARD: untuk sorted set

Contoh:

RPUSH mylist 1 2 3 4 5  # set 5 item ke mylist
LLEN mylist             # mengambil banyak item di mylist
(integer) 5 

SADD myset foo bar baz qux qux # menambahkan item ke myset
SCARD myset                    # mengambil banyak item di myset
(integer) 4 # hasilnya 4 karena set hanya menyimpan string unik, jadi qux ke2 tidak ditambahkan

ZADD mysortedset 1 foo 2 bar 3 baz 4 qux 5 qux # menambahkan item ke mysortedset
ZCARD mysortedset                              # mengambil banyak item di mysortedset
(integer) 4 # hasilnya 4 karena alasan yg sama dengan set

Capped Collections

Capped collections adalah list/sorted set yang sebagian nilainya telah dihapus/dipotong.

Ada beberapa perintah untuk memotong sebagian item didalam list dan sorted set:

  • LTRIM: untuk mengambil item di range index tertentu pada list (sisanya dihapus).
  • ZREMRANGEBYRANK: untuk menghapus item di range index tertentu pada sorted set.

Contoh LTRIM:

RPUSH numbers 1 2 3 4 5 6 7 8 9 10 # set numbers
LTRIM numbers 0 -3                 # ambil dari index 0 sampai dengan index terakhir - 3
LRANGE numbers 0 -1                # ambil semua numbers (setelah dipotong)
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "6"
7) "7"
8) "8"

LTRIM -5 -1 # mengambil 5 item terakhir
LRANGE numbers 0 -1
1) "4"
2) "5"
3) "6"
4) "7"
5) "8"

Contoh ZREMRANGEBYRANK:

ZADD letters 1 a 2 b 3 c 4 d 5 f 6 g 7 h  # set letters
ZREMRANGEBYRANK letters -2 -1             # hapus 2 item terakhir
ZRANGE letters 0 -1
1) "a"
2) "b"
3) "c"
4) "d"
5) "f"

ZREMRANGEBYRANK letters 0 2 # hapus 3 item pertama (index: 0, 1, 2)
1) "d"                                                                                                             2) "f"

2. Set Operations with Sets and Sorted Sets

Pada bagian ini dicontohkan cara menggunakan ZINTERSTORE dan ZETUNIONSTORE untuk menyimpan hasil intersect dan union kedalam key lain.

Contoh ZINTERSTORE:

ZADD l1 1 a 2 b 3 c 4 d 5 f # buat sorted set l1
ZADD l2 1 a 2 b 3 e 5 x 6 z # buat sorted set l2

ZINTERSTORE l3 2 l1 l2      # simpan hasil intersect l1 dan l2 kedalam l3
ZRANGE l3 0 -1 WITHSCORES # melihat semua isi l3
1) "a" # nilai i(0)
2) "2" # score i(0)
3) "b" # nilai i(1)
4) "4" # score i(1)

Sebagai catatan l3 2 l1 l2, 2 yang dimaksud disitu adalah banyak key yang mau di-intersect, karena disitu yang di-intersect adalah l1 dan l2, berarti banyak keynya adalah 2.

Dokumentasi selengkapnya ZINTERSTORE bisa dilihat disini.

Contoh ZUNIONSTORE:

ZADD l1 1 a 2 b 3 c 4 d 5 f # buat sorted set l1
ZADD l2 1 a 2 b 3 e 5 x 6 z # buat sorted set l2

ZUNIONSTORE l3 2 l1 l2      # simpan hasil union l1 dan l2 kedalam l3
ZRANGE l3 0 -1 WITHSCORES # melihat semua isi l3
 1) "a"
 2) "2" # skor si a, jadi 2 karena skornya ditambahkan antara skor a di l1 dan skor a di l2
 3) "c"
 4) "3" # skor si c
 5) "e"
 6) "3" # skor si d
 7) "b"
 8) "4" # skor si b
 9) "d"
10) "4" # skor si d
11) "f"
12) "5" # skor si f
13) "x"
14) "5" # skor si x
15) "z"
16) "6" # skor si y

ZUNIONSTORE l3 2 l1 l2 AGGREGATE MIN  # simpan hasil union l1 dan l2 kedalam l3
ZRANGE l3 0 -1 WITHSCORES # melihat semua isi l3
 1) "a"
 2) "1" # skor si a, paling kecilnya 1
 3) "b"
 4) "2" # skor si b
 5) "c"
 6) "3" # skor si c
 7) "e" 
 8) "3" # skor si e
 9) "d"
10) "4" # skor si d
11) "f"
12) "5" # skor si f
13) "x"
14) "5" # skor si x
15) "z"
16) "6" # skor si z

Dokumentasi selengkapnya ZUNIONSTORE bisa dilihat disini.

Selanjutnya pembahasa menuju ke faceted search. Untuk definisi faceted search sendiri nggak begitu dijelaskan disini, jadi silahkan aja ke wikipedia. Yang dibahas disini hanya contoh kasus menerapkan teknik faceted search pada Redis menggunakan bahasa pemrograman tertentu, untuk mengurangi time complexity saat melakukan pencarian.

Sebagai contoh, kita memiliki data customers berupa JSON sebagai berikut:

[
    {
        "ID": "001",
        "name": "John Doe",
        "age": 20,
        "city": "Jakarta",
        "sex": "male"
    },
    {
        "ID": "002",
        "name": "Jane Doe",
        "age": 20,
        "city": "Bandung",
        "sex": "female"
    }
]

Jika kita ingin melakukan pencarian terhadap kolom age, city, dan sex. Selain menyimpan data customer tersebut, kita juga meyimpan indeks untuk age, city, dan sex berupa sets.

Sebagai contoh:

# simpan data lengkap
SET customer:001 "{json string data john doe}"
SET customer:002 "{json string data jane doe}"

SADD fs:customer:age:20 001 002     # menambahkan 001 dan 002 kedalam set dengan age 20
SADD fs:customer:city:"Jakarta" 001 # menambahkan customer 001 kedalam set dengan city Jakarta
SADD fs:customer:city:"Bandung" 002 # menambahkan customer 002 kedalam set dengan city Bandung
SADD fs:customer:sex:male 001
SADD fs:customer:sex:female 002

Prefix 'fs' disana maksudnya faceted search. Bisa diganti dengan apapun.

Dengan begitu, jika kita ingin melakukan pencarian dengan kriteria umur 20 dan jenis kelamin pria, hal yang pertama kita lakukan adalah mengambil intersect daftar ID dari fs:customer:age:20 dan fs:customer:sex:male. Setelah ID didapat, barulah kita mengambil masing-masing data menggunakan GET.

Contoh:

SINTER fs:customer:age:20 fs:customer:sex:male
1) "001"

GET customer:001
"json string data john doe"

Sebetulnya di kelas pakainya python, jadi hasil dari SINTER di looping untuk mendapatkan detail masing-masing datanya, tapi disini saya catat konsepnya aja. Pokoknya kayak gitulah tekniknya.

Hashed Key

Materi selanjutnya adalah membandingkan faceted search diatas dengan tambahan hashed key. Jadi pada teknik ini, time complexity lebih rendah karena kita tidak perlu melakukan SINTER ke banyak kriteria, karena kombinasi kriterianya dibungkus menjadi hash.

Sebagai contoh pada data JSON sebelumnya, jika kita ingin melakukan pencarian terhadap age, city, dan sex. Kita perlu membuat sets sebanyak kombinasi dari ke-3 kolom tersebut. Untuk algoritma hashnya sendiri bisa pakai apapun, entah md5, sha1, sha256, dsb.

Contoh aja nih ya, dari data JSON diatas kita akan membuat list kurang lebih seperti ini:

# indeks untuk John Doe (001)
RPUSH "hfs:customer:md5(20|Jakarta|male)" 001
RPUSH "hfs:customer:md5(20|*|male)" 001
RPUSH "hfs:customer:md5(20|Jakarta|*)" 001
RPUSH "hfs:customer:md5(*|Jakarta|male)" 001
RPUSH "hfs:customer:md5(20|*|*)" 001
RPUSH "hfs:customer:md5(*|Jakarta|*)" 001
RPUSH "hfs:customer:md5(*|*|male)" 001

# indeks untuk Jane Doe (002)
RPUSH "hfs:customer:md5(20|Bandung|female)" 002
RPUSH "hfs:customer:md5(20|*|female)" 002
RPUSH "hfs:customer:md5(20|Bandung|*)" 002
RPUSH "hfs:customer:md5(*|Bandung|female)" 002
RPUSH "hfs:customer:md5(20|*|*)" 002
RPUSH "hfs:customer:md5(*|Bandung|*)" 002
RPUSH "hfs:customer:md5(*|*|female)" 002

Itu md5(age,city,sex) contoh aja ya, sebetulnya itu seharusnya hasil dari md5-nya, bukan plain text kayak gitu.

Intinya pada teknik hashed key ini kita menyimpan seluruh kombinasi key yang dimungkinkan, sehingga suatu saat kita ingin mencari data pria umur 20, kita bisa gunakan LRANGE sebagai berikut:

LRANGE "hfs:customer:md5(20|*|male)" 0 -1
1) "001"

Dari hasil itu baru deh di loop pakai bahasa pemrograman buat dapatin detail masing-masing datanya. Menggunakan teknik seperti ini akan mengurangi time complexity untuk melakukan pencarian karena tidak perlu melakukan intersect, hanya saja semakin banyak kombinasi valuenya, keynya akan buanyak banget, dan proses maintain indeksnya jadi lebih rumit.

4. Performance & Big O Notation

Sebagai programmer yang main weaponnya PHP dan Javascript saya sebetulnya agak gimana gitu di pembahasan minggu ini. Redis kan memang diciptakan untuk masalah performa, itu kenapa dia menghadirkan solusi in-memory database ini. Tapi di minggu ini dia bahas masalah performa seakan menggunakan bahasa C dan RAM sebagai penyimpanan masih kurang meyakinkan buat mereka hahhha.

Tapi setelah saya pikir-pikir lagi, justru karena fokus mereka di performa, makanya mereka menekankan masalah performa sampai sebegininya.

Pada bagian ini, pembahasannya adalah tentang performa dan big O notation untuk beberapa perintah Redis. Sebetulnya pada dokumentasi Redis, setiap perintah itu udah dicantumin big O notationnya, tapi disini mereka menjelaskan lebih detail untuk beberapa perintah.

Ini contoh perhitungan Big O Notation untuk beberapa perintah Redis:

SET foo 10 # O(1)
SET bar 12 # O(1)

MSET foo 10 bar 12 baz 100 # O(N), N = 3 (foo, bar , dan baz)

DEL foo # O(1)
DEL bar baz # O(N), N = 2

SADD myset 1 2 3 4 5 # O(N), N = 5
DEL myset # O(M), M = 5

SADD set:a 1 2 3 4 5 # O(5)
SADD set:b 1 2 3 4 5 6 7 8 9 # O(9)

SINTER set:b set:a # O(N * M), N = 5 (count set terkecil), M = 2 (set:a dan set:b) => O(5 * 2) => O(10)

RPUSH numbers 1 2 3 4 5 6 7 8 9 0 # O(10)
LRANGE numbers 3 5 # O(S + N), S = 4 (start + 1), N = 3 (end - start + 1) => O(4 + 3) => O(7) 

Begitulah pertemuan minggu ke-2. Dah ya, saya mau lanjut ke RU102J.

Suka artikel ini?

Saya biasanya share artikel-artikel terbaru via facebook atau fanpage foobarology saya. Kalau mau dapat updatenya, di add friend/like/follow aja link-link dibawah ini 😃

Facebook Foobarology