ทำไมต้อง Lua Scripts?
- Performance: รันหลายขั้นตอนใน Redis ได้ในครั้งเดียว (ลด network round-trips)
- Atomicity: คำสั่งทั้งหมดใน script รันแบบ atomic — ไม่มีคำสั่งอื่นแทรกแซงระหว่างรัน
Lua พื้นฐาน
Lua ง่าย ใกล้เคียง C-family ใช้ table (1-based index), string concatenation ด้วย ..
รัน Script ด้วย EVAL
eval "redis.call('set', KEYS[1], ARGV[1])" 1 key:name value
EVALบอก Redis รัน scriptKEYS[1]=key:name(จำนวน keys = 1)ARGV[1]=value
อ่านคืน: eval "return redis.call('get', KEYS[1])" 1 key:name → "value"
Script ซับซ้อน: เรียงข้อมูลจาก Hash ตาม Sorted Set
# Setup
hmset hkeys key:1 value:1 key:2 value:2 key:3 value:3 key:4 value:4 key:5 value:5 key:6 value:6
zadd order 1 key:3 2 key:1 3 key:2
# Script
eval "local order = redis.call('zrange', KEYS[1], 0, -1); return redis.call('hmget', KEYS[2], unpack(order));" 2 order hkeys
# ผล: "value:3" "value:1" "value:2"
SCRIPT LOAD และ EVALSHA
โหลด script ลงหน่วยความจำ คืน SHA1 hash → ใช้ EVALSHA แทน script เต็ม ลด bandwidth
script load "return redis.call('get', KEYS[1])"
# คืน SHA1 เช่น "4e6d8fc8bb01276962cce5371fa795a7763657ae"
evalsha 4e6d8fc8bb01276962cce5371fa795a7763657ae 1 key:name
แก้ไข JSON ใน Redis ด้วย Lua
ปัญหา: GET → parse → modify → SET → race condition + slow
แก้: Lua script ทำใน Redis atomic
set obj '{"a":"foo","b":"bar"}'
EVAL 'local obj = redis.call("get",KEYS[1]);
local obj2 = string.gsub(obj,"(" .. ARGV[1] .. "\":)([^,}]+)", "%1" .. ARGV[2]);
return redis.call("set",KEYS[1],obj2);' 1 obj b bar2
# ผล: {"a":"foo","b":"bar2"}
รายละเอียด: .. = concatenation, % = escape/backreference ใน Lua regex
เมื่อไหร่ควรใช้ Lua?
- พิสูจน์ได้ว่าเร็วกว่า (benchmark ก่อน) <ไม่น่าใช้แค่เพื่อ atomicity → ใช้ Redis Transactions (MULTI/EXEC) แทน
- Script ไม่ควรยาวเกินไป (timeout 5 วินาที) ไม่งั้นกลายเป็น bottleneck
สรุป
Lua script ใน Redis ช่วย performance และ atomicity สำหรับออปเรชันซับซ้อน ใช้จำแค่เมื่อจำเป็น มี library ช่วยสร้าง script อัตโนมัติ (เช่น node.js library ของผู้เขียน)