Saat Anda perlu mengeksekusi beberapa perintah secara atomik dalam satu interaksi database—misalnya untuk mengimplementasikan distributed locks, rate limiter, atau melakukan conditional updates—beberapa round trip jaringan dapat meningkatkan latency dan memperkenalkan risiko race condition. Orca, protokol yang kompatibel dengan Redis, menyediakan fitur skrip Lua yang memungkinkan Anda mengenkapsulasi beberapa perintah kompatibel Redis ke dalam satu skrip. Database mengeksekusi skrip ini secara atomik, sehingga secara efektif menyelesaikan masalah tersebut serta meningkatkan efisiensi eksekusi dan konsistensi data pada operasi kompleks.
Fitur
Fitur skrip Lua pada Orca, protokol yang kompatibel dengan Redis, memungkinkan Anda mengeksekusi skrip Lua sebagaimana di Redis. Keunggulan utamanya adalah atomicity dan high performance.
Atomicity: Semua perintah dalam skrip dieksekusi sebagai satu kesatuan yang tidak dapat dibagi. Tidak ada perintah atau skrip lain yang dapat mengganggu eksekusi skrip tersebut. Hal ini menjamin operasi atomik dan mencegah terjadinya intermediate states akibat eksekusi parsial.
High performance:
Reduced network overhead: Anda dapat mengemas beberapa perintah ke dalam satu skrip dan mengirimkannya ke database sekaligus. Ini secara signifikan mengurangi jumlah round trip jaringan antara client dan server.
Script caching: Anda dapat memuat skrip ke dalam memori kluster terlebih dahulu menggunakan perintah
SCRIPT LOADuntuk mendapatkan checksum SHA1. Selanjutnya, Anda dapat menggunakan perintahEVALSHAuntuk memanggil skrip tersebut hanya dengan mengirim nilai SHA1 pendek ini. Hal ini lebih lanjut mengurangi volume data yang ditransmisikan melalui jaringan.
Lingkup
Versi kluster: Versi kluster harus MySQL 8.0.2, dan versi mesin minor harus 8.0.2.2.33 atau lebih baru.
Endpoint: Anda harus menggunakan ORCA Endpoint dengan Read/Write diatur ke Read/Write (Automatic Read/Write Splitting) untuk mengeksekusi perintah skrip Lua.
Perintah yang tidak didukung: Perintah-perintah berikut tidak dapat dieksekusi dalam skrip Lua menggunakan
redis.call()atauredis.pcall(). Jika Anda mencoba menggunakannya, sistem akan mengembalikan error.Perintah kontrol transaksi:
WATCH,UNWATCH,MULTI,EXEC, atauDISCARDPerintah blocking:
BLPOPatauBRPOPPerintah skrip Lua:
EVAL,EVAL_RO,EVALSHA,EVALSHA_RO, atauSCRIPTPerintah Pub/Sub:
SUBSCRIBE,UNSUBSCRIBE,PSUBSCRIBE, atauPUNSUBSCRIBEPerintah manajemen koneksi:
AUTH,HELLO, atauCLIENT
Sintaks dasar
Berikut adalah perintah inti untuk mengoperasikan skrip Lua. Untuk informasi lebih lanjut, lihat Lua scripting commands dan Scripting with Lua di website resmi Redis.
Perintah | Sintaks | Deskripsi |
|
| Langsung mengeksekusi skrip Lua yang diberikan. Ini adalah metode eksekusi paling dasar. Deskripsi parameter:
|
|
| Menjalankan skrip Lua dalam mode read-only. Ini berlaku untuk skenario read/write splitting, memastikan skrip tidak melakukan operasi write. Jika skrip berisi perintah write, sistem akan mengembalikan error. Deskripsi parameternya sama dengan |
|
| Menjalankan skrip yang telah di-cache menggunakan checksum SHA1-nya. Jika skrip belum di-cache, sistem akan mengembalikan error |
|
| Menjalankan skrip yang telah di-cache menggunakan checksum SHA1-nya dalam mode read-only. Ini berlaku untuk skenario read/write splitting, memastikan skrip tidak melakukan operasi write. Jika skrip berisi perintah write, sistem akan mengembalikan error. Deskripsi parameternya sama dengan |
|
| Memuat skrip ke dalam cache dan mengembalikan checksum SHA1-nya untuk pemanggilan selanjutnya menggunakan |
|
| Memeriksa apakah satu atau beberapa skrip yang sesuai dengan checksum SHA1 ada di cache. Mengembalikan 1 jika ada, 0 jika tidak ada. |
|
| Menghentikan skrip Lua yang sedang dieksekusi. Orca mendukung penghentian skrip apa pun (termasuk skrip write) dan secara otomatis membatalkan perubahan data yang dilakukan oleh skrip tersebut, sehingga menjaga konsistensi data. |
|
| Menghapus semua cache skrip Lua di kluster saat ini.
|
Contoh operasi
Persiapkan data uji:
SET polardb orcaPerintah uji:
EVAL
EVAL "return redis.call('GET', KEYS[1])" 1 polardbContoh hasil:
"orca"EVAL_RO
EVAL_RO "return redis.call('GET', KEYS[1])" 1 polardbContoh hasil:
"orca"SCRIPT LOAD
SCRIPT LOAD "return redis.call('GET', KEYS[1])"Contoh hasil:
"d3c21d0c2b9ca22f82737626a27bcaf5d288f99f"EVALSHA
EVALSHA d3c21d0c2b9ca22f82737626a27bcaf5d288f99f 1 polardbContoh hasil:
"orca"EVALSHA_R
EVALSHA_RO d3c21d0c2b9ca22f82737626a27bcaf5d288f99f 1 polardbContoh hasil:
"orca"SCRIPT EXISTS
SCRIPT EXISTS d3c21d0c2b9ca22f82737626a27bcaf5d288f99f ffffffffffffffffffffffffffffffffffffffffContoh hasil:
1) (integer) 1 2) (integer) 0SCRIPT FLUSH
PeringatanPerintah ini menghapus semua cache skrip Lua di kluster. Cache yang dihasilkan oleh
SCRIPT LOADatauEVALakan dihapus.Setelah cache dihapus, semua panggilan yang bergantung pada
EVALSHAatauEVALSHA_ROmungkin mengembalikan errorNOSCRIPT. Anda harus memuat ulang skrip dan mencoba kembali operasi tersebut.Jika banyak skrip di-cache, perintah
SCRIPT FLUSHdapat memblokir kluster dalam waktu lama. Kami menyarankan Anda menjalankan perintah ini selama jam sepi.Kami menyarankan Anda menyimpan kode sumber skrip di sisi client atau aplikasi dan mengimplementasikan mekanisme pemulihan otomatis untuk error
NOSCRIPT. Misalnya, Anda dapat memuat ulang skrip menggunakanSCRIPT LOADatau mendaftarkannya kembali menggunakanEVAL.
SCRIPT FLUSHContoh hasil:
OK
Skenario umum
Konfigurasikan dan jalankan atomic counter
Anda dapat menghindari race condition yang disebabkan oleh operasi INCR konkuren. Hal ini memastikan bahwa counter benar-benar bertambah dan dapat di-reset secara kondisional.
Tinjauan proses
Tulis skrip Lua.
Muat ke dalam cache kluster.
Gunakan kembali dan jalankan skrip dengan
EVALSHA.
Menggunakan kombinasi SCRIPT LOAD + EVALSHA mengurangi network traffic dan meningkatkan performa serta idempotensi. Konten skrip tidak terekspos dalam badan permintaan.
Prosedur
Tulis skrip dan simpan secara lokal:
-- counter.lua: Jika key tidak ada, atur nilainya ke 0, lalu tambahkan 1; kembalikan nilai baru. if redis.call('EXISTS', KEYS[1]) == 0 then redis.call('SET', KEYS[1], 0) end return redis.call('INCR', KEYS[1])Muat skrip dan dapatkan checksum SHA1:
SCRIPT LOAD "if redis.call('EXISTS', KEYS[1]) == 0 then redis.call('SET', KEYS[1], 0) end return redis.call('INCR', KEYS[1])" -- Hasil pengembalian "b547eabbcde73b25330442e4f4e4dc1783b91241"(Direkomendasikan) Gunakan kembali dan jalankan skrip:
EVALSHA b547eabbcde73b25330442e4f4e4dc1783b91241 1 my_counter -- Hasil pengembalian (integer) 1 -- Jalankan lagi, hasil pengembalian (integer) 2
Jalankan skrip read-only
Pastikan skrip tidak berisi operasi write: Periksa apakah skrip memanggil perintah write apa pun, seperti
SET,DEL,HSET, atauLPUSH. Jika iya, jangan gunakanEVAL_RO.CatatanJika skrip berisi perintah write, sistem akan mengembalikan error
(error) ERR Write commands are not allowed from read-only scripts.Jalankan skrip menggunakan
EVAL_RO:EVAL_RO "return {redis.call('SET', KEYS[1]), redis.call('TTL', KEYS[1])}" 1 polardb -- Hasil pengembalian 1) "orca" 2) (integer) -1
Hentikan skrip Lua yang berjalan abnormal
Anda dapat mencegah skrip yang berjalan lama memblokir kluster. Untuk itu, hentikan eksekusi menggunakan perintah SCRIPT KILL. Sistem secara otomatis membatalkan semua perubahan untuk menjaga konsistensi data.
Anda hanya dapat menghentikan skrip yang sedang dieksekusi. Anda tidak dapat menentukan nilai SHA1 untuk menghentikan skrip tertentu.
SCRIPT KILL
-- Hasil pengembalian
OKPraktik terbaik optimasi performa
Untuk mengurangi dampak pemblokiran skrip Lua terhadap kluster dan mencegah penggunaan memori berlebih akibat caching banyak skrip yang fungsinya redundan, kami merekomendasikan praktik terbaik berikut:
Batas timeout: Timeout eksekusi default untuk satu skrip Lua adalah 300 detik.
Hindari menulis skrip Lua yang terlalu besar untuk mencegah konsumsi memori berlebih.
Hindari operasi write berdurasi panjang atau batch data besar dalam skrip Lua.
Risiko pemblokiran: Skrip Lua dieksekusi secara atomik di kluster. Selama eksekusi, skrip tersebut memblokir perintah dan skrip lainnya. Oleh karena itu, Anda harus mengontrol ukuran batch write dan durasi eksekusi untuk menghindari loop panjang atau traversal data yang luas. Jika perlu, pisahkan logika kompleks menjadi beberapa skrip independen untuk dieksekusi.
Cache skrip non-persisten: Cache skrip Lua tidak dihapus selama runtime kluster. Namun, cache akan dihapus setelah restart kluster, alih bencana high-availability (HA), atau eksekusi perintah
SCRIPT FLUSH. Anda harus mendaftarkan ulang skrip dalam kasus-kasus tersebut.Pedoman penulisan skrip: Gunakan
KEYS[]danARGV[]untuk pass parameter. Hindari hard coding parameter dalam skrip.Kurangi network traffic: Gunakan kombinasi
SCRIPT LOAD + EVALSHAuntuk performa optimal dan pengurangan network traffic.
Contoh operasi
Muat skrip: Saat inisialisasi aplikasi atau saat pertama kali digunakan, muat skrip ke dalam kluster menggunakan
SCRIPT LOADdan dapatkan nilai SHA1-nya. Aplikasi Anda harus menyimpan nilai SHA1 ini secara lokal.# Muat skrip yang mengatur nilai suatu key SCRIPT LOAD "return redis.call('set', KEYS[1], ARGV[1])" # Contoh hasil "55b22c0d0cedf3866879ce7c854970626dcef0c3"Jalankan skrip: Selanjutnya, gunakan perintah
EVALSHAdengan nilai SHA1 yang diperoleh pada langkah sebelumnya untuk menjalankan skrip.# Gunakan skrip yang di-cache untuk mengatur k1 = v1 EVALSHA 55b22c0d0cedf3866879ce7c854970626dcef0c3 1 k1 v1 # Gunakan skrip yang sama untuk mengatur k2 = v2 EVALSHA 55b22c0d0cedf3866879ce7c854970626dcef0c3 1 k2 v2Handle error
NOSCRIPT: JikaEVALSHAmengembalikan errorNOSCRIPT, aplikasi harus menangkap error ini. Kemudian, aplikasi dapat secara otomatis beralih ke penggunaan perintahEVALatauSCRIPT LOADuntuk mengeksekusi skrip sekali dan menyimpannya di cache kluster.EVAL: Secara otomatis menyimpan ulang skrip selama eksekusi.SCRIPT LOAD: Memuat skrip ke dalam kluster dan mendapatkan nilai SHA1-nya.
Bersihkan penggunaan memori skrip Lua: Jika Anda tidak lagi perlu mengeksekusi skrip Lua, Anda dapat menjalankan perintah
SCRIPT FLUSHuntuk menghapus cache skrip Lua. Jika terlalu banyak skrip Lua di-cache di kluster, perintah ini dapat memblokir kluster dalam waktu lama. Kami menyarankan Anda menjalankannya selama jam sepi.
FAQ
Bagaimana cara menangani error
NOSCRIPT No matching script. Please use EVAL.?Penyebab: Error ini menunjukkan bahwa Anda mencoba mengeksekusi skrip yang tidak ada di cache kluster menggunakan perintah
EVALSHAatauEVALSHA_RO. Error ini biasanya terjadi setelah restart kluster, alih bencana high-availability (HA), atau eksekusi perintahSCRIPT FLUSH.
Solusi: Kode client Anda harus memiliki logika penanganan error. Saat Anda menangkap errorNOSCRIPT, Anda dapat beralih ke penggunaan perintahEVALatauSCRIPT LOADuntuk mengeksekusi ulang skrip dan menyimpannya di cache kluster. PanggilanEVALSHAselanjutnya kemudian akan berjalan sebagaimana mestinya.Apa yang harus dilakukan jika eksekusi skrip Lua melebihi timeout?
Timeout skrip default adalah 300 detik. Error timeout biasanya menunjukkan bahwa logika skrip terlalu kompleks atau memproses data terlalu banyak.
Solusi:Optimalkan logika skrip. Hindari loop yang tidak efisien atau traversal data berskala besar dalam skrip.
Jika skrip berjalan terlalu lama, Anda dapat menghentikannya menggunakan perintah
SCRIPT KILL. PolarDB kemudian akan membatalkan semua perubahan yang dilakukan oleh skrip tersebut.Pisahkan logika bisnis kompleks menjadi beberapa skrip yang lebih kecil dan cepat untuk dieksekusi secara bertahap.
Mengapa eksekusi skrip dengan
EVAL_ROmengembalikan errorERR Write commands are not allowed from read-only scripts.?EVAL_ROdanEVALSHA_ROadalah mode read-only. Mode ini secara ketat melarang eksekusi operasi write apa pun, sepertiSET,HSET, atauDEL. Error ini menunjukkan bahwa skrip Anda berisi perintah write.
Solusi:Periksa skrip dan hapus semua perintah write.
Untuk melakukan operasi write, gunakan perintah
EVALatauEVALSHA.