Tair (Redis OSS-compatible) mendukung perintah Lua. Anda dapat menggunakan skrip Lua untuk menangani operasi compare-and-set (CAS) secara efisien, meningkatkan performa instans, serta mengimplementasikan pola yang sulit atau tidak efisien dicapai dengan cara lain. Topik ini menjelaskan sintaks dasar dan panduan penggunaan skrip Lua.
Sintaks dasar
Optimisasi performa
Optimalkan overhead memori dan jaringan
Menyimpan cache sejumlah besar skrip yang fungsinya berulang mengonsumsi memori signifikan dan bahkan dapat memicu error out of memory (OOM). Berikut adalah contoh penggunaan yang salah:
EVAL "return redis.call('set', 'k1', 'v1')" 0
EVAL "return redis.call('set', 'k2', 'v2')" 0Solusi:
Untuk mengurangi pemborosan memori, hindari hardcoding argumen sebagai konstanta dalam skrip Lua.
# Mencapai fungsi yang sama seperti contoh salah tetapi hanya menyimpan cache skrip sekali. EVAL "return redis.call('set', KEYS[1], ARGV[1])" 1 k1 v1 EVAL "return redis.call('set', KEYS[1], ARGV[1])" 1 k2 v2Untuk efisiensi lebih tinggi, kami merekomendasikan pendekatan berikut untuk mengurangi penggunaan memori dan overhead jaringan.
SCRIPT LOAD "return redis.call('set', KEYS[1], ARGV[1])" # Setelah dieksekusi, Redis mengembalikan "55b22c0d0cedf3866879ce7c854970626dcef0c3". EVALSHA 55b22c0d0cedf3866879ce7c854970626dcef0c3 1 k1 v1 EVALSHA 55b22c0d0cedf3866879ce7c854970626dcef0c3 1 k2 v2
Bersihkan memori skrip Lua
Cache skrip Lua mengonsumsi memori instans, yang berkontribusi terhadap metrik used_memory. Jika penggunaan memori instans mendekati atau melebihi maxmemory, hal ini dapat memicu error out of memory (OOM). Contoh error ditunjukkan di bawah ini:
-OOM command not allowed when used memory > 'maxmemory'.Solusi:
Jalankan perintah SCRIPT FLUSH dari client untuk membersihkan cache skrip Lua. Metode yang direkomendasikan bergantung pada versi instans Anda:
Pembersihan asinkron (Direkomendasikan): open source Redis 7.0 atau versi lebih baru dan Tair (Enterprise Edition) 6.0 atau versi lebih baru mendukung perintah
SCRIPT FLUSH ASYNC. Perintah ini membersihkan cache skrip Lua di latar belakang tanpa memblokir instans, sehingga meminimalkan dampak terhadap layanan Anda.SCRIPT FLUSH ASYNCPembersihan sinkron: Untuk versi sebelumnya, perintah
SCRIPT FLUSHberoperasi secara sinkron. Jika instans memiliki banyak skrip Lua yang tersimpan dalam cache, perintah ini dapat memblokir instans dalam waktu lama dan membuatnya tidak tersedia. Gunakan perintah ini dengan hati-hati, sebaiknya pada jam sepi.SCRIPT FLUSH
Mengklik Purge Data di konsol hanya menghapus data dan tidak membersihkan cache skrip Lua.
Selain itu, hindari menulis skrip Lua yang terlalu besar untuk mencegah konsumsi memori tinggi. Jangan melakukan penulisan data massal dalam skrip Lua karena dapat menyebabkan lonjakan penggunaan memori dan berpotensi memicu error OOM. Jika logika bisnis Anda memungkinkan, kami merekomendasikan mengaktifkan eviction data (diaktifkan secara default dengan kebijakan volatile-lru) untuk menghemat ruang memori. Namun, instans tidak akan menghapus cache skrip Lua, terlepas dari apakah eviction data diaktifkan atau tidak.
Penanganan error
Error NOSCRIPT
Saat Anda menggunakan perintah EVALSHA, jika skrip yang sesuai dengan nilai sha1 belum disimpan dalam cache instans, instans akan mengembalikan error NOSCRIPT. Contohnya ditunjukkan di bawah ini:
(error) NOSCRIPT No matching script. Please use EVAL.Solusi:
Gunakan perintah EVAL atau SCRIPT LOAD untuk menyimpan skrip dalam cache instans, lalu coba lagi. Namun, instans tidak menjamin persistensi atau replikasi skrip Lua. Dalam beberapa skenario, seperti migrasi instans atau perubahan konfigurasi, cache skrip Lua mungkin dihapus. Oleh karena itu, aplikasi client Anda harus mampu menangani error ini. Untuk informasi lebih lanjut, lihat Masalah persistensi dan replikasi.
Demo Python berikut menunjukkan contoh cara menangani error NOSCRIPT. Demo ini menggunakan skrip Lua untuk melakukan operasi prepend string.
Anda juga dapat menggunakan library redis-py untuk Python guna menangani jenis error ini. Library tersebut menyediakan kelas Script yang mengenkapsulasi logika tingkat rendah untuk skrip Lua Redis, seperti menangkap error NOSCRIPT.
import redis
import hashlib
# Fungsi ini menerima skrip Lua sebagai string dan mengembalikan digest SHA1-nya.
def calcSha1(strin):
sha1_obj = hashlib.sha1()
sha1_obj.update(strin.encode('utf-8'))
sha1_val = sha1_obj.hexdigest()
return sha1_val
class MyRedis(redis.Redis):
def __init__(self, host="localhost", port=6379, password=None, decode_responses=False):
redis.Redis.__init__(self, host=host, port=port, password=password, decode_responses=decode_responses)
def prepend_inLua(self, key, value):
script_content = """\
local suffix = redis.call("get", KEYS[1])
local prefix = ARGV[1]
local new_value = prefix..suffix
return redis.call("set", KEYS[1], new_value)
"""
script_sha1 = calcSha1(script_content)
if self.script_exists(script_sha1)[0]: # Periksa apakah skrip tersimpan dalam cache Redis.
return self.evalsha(script_sha1, 1, key, value) # Jika tersimpan, jalankan skrip dengan EVALSHA.
else:
return self.eval(script_content, 1, key, value) # Jika tidak, jalankan skrip dengan EVAL, yang juga menyimpannya dalam cache. Atau, Anda bisa menggunakan SCRIPT LOAD lalu EVALSHA.
r = MyRedis(host="r-******.redis.rds.aliyuncs.com", password="***:***", port=6379, decode_responses=True)
print(r.prepend_inLua("k", "v"))
print(r.get("k"))
Error timeout skrip Lua
Karena skrip Lua dieksekusi secara atomik, skrip yang berjalan lambat dapat memblokir instans. Jika skrip berjalan lebih dari 5 detik, instans akan mengembalikan error
BUSYuntuk semua perintah lain hingga skrip selesai dieksekusi.BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE.Solusi:
Anda dapat menggunakan perintah
SCRIPT KILLuntuk menghentikan skrip Lua atau menunggu hingga skrip selesai.CatatanPerintah
SCRIPT KILLtidak berlaku selama 5 detik pertama eksekusi skrip lambat karena instans sedang diblokir.Saat menulis skrip Lua, perkirakan waktu eksekusinya dan periksa masalah seperti infinite loop. Hal ini membantu mencegah skrip berjalan lama yang memblokir instans dan membuat layanan tidak tersedia. Jika perlu, pecah skrip panjang menjadi beberapa skrip yang lebih kecil.
Jika skrip Lua saat ini telah mengeksekusi perintah write, perintah
SCRIPT KILLtidak akan berfungsi. Contoh error ditunjukkan di bawah ini:(error) UNKILLABLE Sorry the script already executed write commands against the dataset. You can either wait the script termination or kill the server in a hard way using the SHUTDOWN NOSAVE command.Solusi:
Di konsol, buka halaman Instances dan klik restart untuk instans yang sesuai.
Masalah persistensi dan replikasi
Instans menyimpan cache skrip Lua yang dieksekusi secara permanen kecuali jika instans direstart atau perintah SCRIPT FLUSH dipanggil. Namun, dalam situasi tertentu, seperti migrasi instans, perubahan konfigurasi, upgrade versi, atau alih bencana HA, instans tidak menjamin persistensi skrip Lua atau replikasinya ke node lain.
Solusi:
Karena instans tidak menjamin persistensi atau replikasi skrip, Anda harus menyimpan semua skrip Lua secara lokal. Jika diperlukan, gunakan perintah EVAL atau SCRIPT LOAD untuk menyimpan kembali skrip dalam cache instans. Hal ini mencegah error NOSCRIPT yang dapat terjadi jika cache skrip dihapus selama operasi seperti restart instans atau alih bencana HA.
Batasan khusus dalam arsitektur kluster
Batasan arsitektur kluster
Untuk menjamin atomicity, skrip Lua tidak dapat dibagi dan harus dieksekusi pada satu shard dalam arsitektur kluster. Instans biasanya menggunakan kunci untuk menentukan shard mana yang akan menerima perintah tersebut. Oleh karena itu, Anda harus menentukan setidaknya satu kunci saat mengeksekusi skrip Lua dalam kluster. Jika skrip mengakses beberapa kunci, semua kunci tersebut harus berada dalam slot yang sama; jika tidak, skrip akan gagal atau mengembalikan hasil yang salah. Perintah tanpa kunci, seperti
KEYS,SCAN, danFLUSHDB, dapat dijalankan tetapi hanya akan mengembalikan data dari satu shard. Batasan ini merupakan bagian inheren dari arsitektur Redis Cluster.Menjalankan perintah
SCRIPT LOADpada satu node tidak menjamin bahwa skrip tersebut disimpan pada node lain.
Kode error dalam mode proxy
Proxy melakukan pemeriksaan sintaks untuk mengidentifikasi secara proaktif kasus-kasus di mana kunci tersebar di beberapa slot. Hal ini membantu mendeteksi masalah lebih awal untuk mempermudah troubleshooting. Metode pemeriksaan proxy berbeda dari mesin virtual Lua, sehingga menimbulkan batasan tambahan saat mengeksekusi perintah Lua dalam mode proxy. Misalnya, perintah UNPACK tidak didukung, dan perintah EVAL, EVALSHA, serta perintah seri SCRIPT tidak dapat digunakan dalam transaksi MULTI/EXEC.
Anda juga dapat menonaktifkan beberapa pemeriksaan sintaks Lua proxy dengan mematikan parameter script_check_enable.
Selain itu, untuk instans pemisahan baca/tulis, jika parameter readonly_lua_route_ronode_enable diaktifkan, proxy memeriksa apakah skrip Lua hanya berisi perintah read-only untuk menentukan apakah skrip tersebut dapat diteruskan ke node read-only. Logika pemeriksaan ini memberlakukan batasan pada sintaks Lua.
Menonaktifkan parameter script_check_enable memiliki efek berikut:
Untuk instans open source Redis 5.0 (versi minor sebelum 5.0.8) atau Redis 4.0 dan versi sebelumnya, kami tidak merekomendasikan menonaktifkan parameter ini. Melakukannya dapat menyebabkan skrip mengembalikan pesan sukses meskipun dieksekusi secara salah.
Untuk versi lainnya, menonaktifkan parameter ini menghentikan proxy dari pemeriksaan sintaks Lua, tetapi node data tetap akan melakukan pemeriksaan sintaks normal mereka.
Berikut adalah kode error spesifik beserta penyebabnya.
Batasan arsitektur Redis Cluster
Kode error:
-ERR for redis cluster, eval/evalsha number of keys can't be negative or zero\r\nDeskripsi: Anda harus menentukan setidaknya satu kunci saat mengeksekusi skrip Lua. Proxy menggunakan kunci tersebut untuk menentukan shard mana yang akan menerima skrip.
# Contoh yang benar EVAL "return redis.call('get', KEYS[1])" 1 fooeval # Contoh yang salah EVAL "return redis.call('get', 'foo')" 0Kode error:
-ERR 'xxx' command keys must in same slotDeskripsi: Semua kunci dalam skrip Lua harus berada dalam slot yang sama.
# Contoh yang benar: EVAL "return redis.call('mget', KEYS[1], KEYS[2])" 2 foo {foo}bar # Contoh yang salah: EVAL "return redis.call('mget', KEYS[1], KEYS[2])" 2 foo foobar
Batasan sintaks Lua proxy
Anda dapat menghindari pemeriksaan sintaks Lua tambahan dari proxy dengan menonaktifkan parameter script_check_enable.
Kode error:
-ERR bad lua script for redis cluster, nested redis.call/redis.pcallDeskripsi: Pemanggilan Redis bersarang tidak didukung. Gunakan variabel lokal sebagai alternatif.
# Contoh yang benar EVAL "local value = redis.call('GET', KEYS[1]); redis.call('SET', KEYS[2], value)" 2 foo bar # Contoh yang salah EVAL "redis.call('SET', KEYS[1], redis.call('GET', KEYS[2]))" 2 foo barKode error:
-ERR bad lua script for redis cluster, first parameter of redis.call/redis.pcall must be a single literal stringDeskripsi: Perintah yang dipanggil dalam redis.call/pcall harus berupa string literal.
# Contoh yang benar eval "redis.call('GET', KEYS[1])" 1 foo # Contoh yang salah eval "local cmd = 'GET'; redis.call(cmd, KEYS[1])" 1 foo
Masalah izin baca/tulis
Kode error:
-ERR Write commands are not allowed from read-only scriptsDeskripsi: Skrip Lua yang dikirim dengan perintah
EVAL_ROtidak boleh berisi perintah write.Kode error:
-ERR bad write command in no write privilegeDeskripsi: Skrip Lua yang dikirim dari akun read-only tidak boleh berisi perintah write.
Perintah yang tidak didukung
Kode error:
-ERR script debug not supportDeskripsi: Proxy saat ini tidak mendukung perintah
SCRIPT DEBUG.Kode error:
-ERR bad lua script for redis cluster, redis.call/pcall unkown redis command xxxDeskripsi: Skrip Lua berisi perintah yang tidak didukung oleh proxy. Untuk informasi lebih lanjut, lihat Batasan perintah pada instans kluster dan pemisahan baca/tulis.
Error sintaks Lua
Kode error:
-ERR bad lua script for redis cluster, redis.call/pcall expect '('atau-ERR bad lua script for redis cluster, redis.call/redis.pcall definition is not complete, expect ')'Catatan: Ini adalah error sintaks Lua karena fungsi
redis.callharus diikuti oleh sepasang tanda kurung lengkap:(dan).Kode error:
-ERR bad lua script for redis cluster, at least 1 input key is needed for ZUNIONSTORE/ZINTERSTOREDeskripsi: Parameter
numkeysdari perintahZUNIONSTOREdanZINTERSTOREharus lebih besar dari 0.Kode error:
-ERR bad lua script for redis cluster, ZUNIONSTORE/ZINTERSTORE key count < numkeysDeskripsi: Jumlah kunci aktual untuk perintah
ZUNIONSTOREatauZINTERSTOREkurang dari nilainumkeysyang ditentukan.Kode error:
-ERR bad lua script for redis cluster, xread/xreadgroup command syntax errorDeskripsi: Sintaks perintah
XREADatauXREADGROUPsalah. Periksa jumlah parameternya.Kode error:
-ERR bad lua script for redis cluster, xread/xreadgroup command syntax error, streams must be specifiedDeskripsi: Perintah
XREADdanXREADGROUPharus menyertakan parameterstreams.Kode error:
-ERR bad lua script for redis cluster, sort command syntax errorDeskripsi: Sintaks perintah
SORTsalah.
FAQ
T: Dapatkah saya mengeksekusi skrip Lua di Data Management (DMS)?
J: Konsol Data Management (DMS) saat ini tidak mendukung perintah terkait Lua. Untuk menggunakan skrip Lua, sambungkan ke instans Anda menggunakan client atau redis-cli.