Skip to content

Redis 事务与Lua脚本

事务(Transaction)

基本命令

bash
# 开启事务
MULTI

# 命令入队
SET key1 value1
GET key1
INCR counter

# 执行事务
EXEC

# 取消事务
DISCARD

事务特性

Redis事务保证:

  1. 原子性:事务中的命令要么全部执行,要么都不执行
  2. 顺序性:按FIFO顺序执行
  3. 隔离性:执行期间不会被其他客户端打断

注意:Redis事务不支持回滚!

WATCH命令

bash
WATCH mykey
GET mykey  # 假设值为100
SET mykey 200
MULTI
SET mykey 300
EXEC  # 如果mykey被其他客户端修改,返回nil

原理

  • WATCH在事务开始前监控key
  • EXEC时检查被监控的key是否被修改
  • 如果修改,返回nil(事务不执行)

事务缺陷

  1. 不支持回滚
  2. 不支持原子性保证(某个命令失败后继续执行)
  3. 不支持隔离级别

Lua脚本

基本使用

bash
# 执行脚本
EVAL "return 'hello'" 0
EVAL "return redis.call('get', 'key')" 1 key

# 脚本缓存
SCRIPT LOAD "return ARGV[1]"
EVALSHA "c69d20ceca4713fe9a2bb93ce2cfdde38c2e2d2a" 0

# 脚本管理
SCRIPT FLUSH  # 清空脚本缓存
SCRIPT EXISTS <sha1>

Lua语法

lua
-- 变量
local name = "world"

-- 条件
if name == "world" then
    return "hello " .. name
else
    return "hi"
end

-- 循环
for i = 1, 3 do
    print(i)
end

-- 函数
function getValue(key)
    return redis.call('get', key)
end

常用场景

lua
-- 原子计数器
local key = KEYS[1]
local current = redis.call('get', key)
if current then
    return redis.call('incr', key)
else
    redis.call('set', key, 1)
    return 1
end

面试考点

Q1: Redis事务与MySQL事务的区别?

答案

特性RedisMySQL
回滚不支持支持
隔离级别ACID
实现方式乐观锁(WATCH)MVCC + 锁
原子性全执行/全不执行可部分回滚

Q2: Lua脚本的优势?

答案

  1. 原子性:脚本执行期间不会被其他命令打断
  2. 减少网络往返:多个命令打包执行
  3. 可自定义复杂逻辑
  4. 服务器端执行,减少延迟

Q3: 为什么Redis不支持事务回滚?

答案

  1. Redis定位是简单KV存储,设计上追求高性能
  2. 命令失败通常是编程错误,应该早点发现
  3. 回滚会增加复杂度,影响性能
  4. 大多数分布式系统也不支持回滚(如Zookeeper)

Q4: WATCH的原理?

答案

  1. WATCH记录监控的key
  2. 每次修改key时,Redis会记录version
  3. EXEC时检查version是否变化
  4. 变化则返回nil,否则执行事务

最后更新: