在黑马点评项目实战中,在提到确保分布式锁的获取锁和释放锁的时候提到了Redis的Lua脚本,本文来简单介绍一下Lua语言和简单语法。
Redis 支持通过 Lua 脚本在服务端直接执行一系列操作,这些脚本会被 Redis 解释器原子性地执行(即执行期间不会被其他命令打断)。其核心价值是:
Lua 是一种轻量级、嵌入型、跨平台的脚本语言,由巴西里约热内卢天主教大学(PUC-Rio)的开发团队于 1993 年设计。它的设计目标是“简单、高效、可嵌入”,因此被广泛用于游戏引擎(如《魔兽世界》)、嵌入式系统、数据库(如 Redis)等场景。
table
实现。Lua 的语法简洁,学习成本低。以下是最基础的常用语法:
-- 这是注释
--[[ 这是多行注释 --]]
Lua 是弱类型语言,变量无需声明类型,常用类型包括:
10
, 3.14
)。' '
或双引号 " "
包裹(如 'hello'
, "world"
)。true
或 false
(注意:Lua 没有 nil
以外的“假值”,nil
表示“无值”)。{}
定义(类似 JSON 对象或数组)。var
/let
)。 name = "Redis" -- 全局变量 name
local
声明(推荐,避免污染全局作用域)。 local age = 10 -- 局部变量 age
local score = 85
if score >= 90 then
print("优秀")
elseif score >= 80 then
print("良好")
else
print("加油")
end
-- 遍历数组(索引从 1 开始)
local fruits = {"apple", "banana", "orange"}
for i = 1, #fruits do -- #fruits 是数组长度(3)
print(fruits[i])
end
-- 数值范围循环(i 从 1 到 5)
for i = 1, 5 do
print(i)
end
-- 定义函数(计算两数之和)
local function add(a, b)
return a + b
end
-- 调用函数
print(add(3, 5)) -- 输出 8
-- 匿名函数(赋值给变量)
local multiply = function(a, b)
return a * b
end
print(multiply(2, 4)) -- 输出 8
表是 Lua 中唯一的数据结构,可模拟数组、字典、对象等:
-- 数组(索引从 1 开始)
local arr = {10, 20, 30}
print(arr[1]) -- 输出 10
-- 字典(键值对)
local user = {
name = "张三",
age = 25,
isStudent = false
}
print(user.name) -- 输出 "张三"
-- 混合表(数组+字典)
local info = {
{"北京", "上海"}, -- 索引 1: 数组部分
city = "广州", -- 索引 "city": 字典部分
population = 1500 -- 索引 "population": 字典部分
}
print(info[1][1]) -- 输出 "北京"
print(info.city) -- 输出 "广州"
回到之前的分布式锁场景,用 Lua 脚本实现“原子性解锁”:
-- KEYS[1]: 锁的键(如 "order:userId")
-- ARGV[1]: 当前线程的标识(如 "thread:123")
if redis.call("GET", KEYS[1]) == ARGV[1] then
return redis.call("DEL", KEYS[1]) -- 锁归属匹配,删除锁
else
return 0 -- 锁已被其他线程持有,不操作
end
public void unlock(String lockKey, String threadId) {
// 定义 Lua 脚本
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
" return redis.call('del', KEYS[1]) " +
"else " +
" return 0 " +
"end";
// 执行脚本(KEYS 参数是锁的键,ARGV 参数是线程 ID)
Long result = stringRedisTemplate.execute(
new DefaultRedisScript<>(script, Long.class),
Collections.singletonList(lockKey),
threadId
);
// result 为 1 表示删除成功,0 表示锁已被其他线程持有
}