MULTI/EXEC与乐观锁
本课程将深入讲解事务处理的核心概念和实践应用。主要内容包括:MULTI、EXEC、WATCH、Lua脚本、原子操作。
Redis的事务处理是非常重要的功能模块,在实际开发中有广泛的应用。通过本课程的学习,你将全面掌握事务处理的使用技巧。
Redis事务是一组命令的集合,这些命令会被序列化并按顺序执行,在执行过程中不会被其他客户端的命令干扰。Redis事务具有以下特性:
# 开始事务
MULTI
# 返回: OK
# 执行事务
EXEC
# 返回: [命令执行结果数组]
# 取消事务
DISCARD
# 返回: OK
# 监视键(乐观锁)
WATCH key1 [key2 ...]
# 返回: OK
# 取消监视
UNWATCH
# 返回: OK
# 示例1: 基本事务
MULTI
SET user:1:name "zhangsan"
SET user:1:age 25
EXEC
# 返回: 1) OK
# 2) OK
# 示例2: 取消事务
MULTI
SET user:1:name "zhangsan"
DISCARD
# 返回: OK
GET user:1:name
# 返回: (nil)
# 示例3: 事务中的错误处理
MULTI
SET key1 "value1"
INCR key1 # 类型错误
SET key2 "value2"
EXEC
# 返回: 1) OK
# 2) (error) ERR value is not an integer or out of range
# 3) OK
# 注意: Redis 2.6.5+会继续执行其他命令
WATCH命令用于实现乐观锁,它会监视一个或多个键,如果在事务执行前这些键被其他客户端修改,那么事务会被取消执行。
# 示例: 实现安全的余额扣减
WATCH account:1:balance
balance = GET account:1:balance
if balance >= 100:
MULTI
DECRBY account:1:balance 100
INCRBY account:2:balance 100
result = EXEC
if result:
print("转账成功")
else:
print("转账失败,余额已被修改")
else:
print("余额不足")
UNWATCH
Redis从2.6.0开始支持Lua脚本,通过执行Lua脚本可以实现更复杂的原子操作。Lua脚本具有以下优势:
# 执行Lua脚本
EVAL script numkeys key [key ...] arg [arg ...]
# 执行已缓存的脚本
EVALSHA sha1 numkeys key [key ...] arg [arg ...]
# 缓存脚本
SCRIPT LOAD script
# 返回: "sha1值"
# 查看脚本是否存在
SCRIPT EXISTS sha1 [sha1 ...]
# 清除所有脚本
SCRIPT FLUSH
# 终止正在执行的脚本
SCRIPT KILL
# 示例1: 实现安全的余额扣减
local balance = redis.call('GET', KEYS[1])
local amount = tonumber(ARGV[1])
if balance and tonumber(balance) >= amount then
redis.call('DECRBY', KEYS[1], amount)
redis.call('INCRBY', KEYS[2], amount)
return 1
else
return 0
end
# 执行脚本
EVAL "local balance = redis.call('GET', KEYS[1]) local amount = tonumber(ARGV[1]) if balance and tonumber(balance) >= amount then redis.call('DECRBY', KEYS[1], amount) redis.call('INCRBY', KEYS[2], amount) return 1 else return 0 end" 2 account:1:balance account:2:balance 100
# 示例2: 实现分布式锁
local lock = redis.call('SETNX', KEYS[1], ARGV[1])
if lock == 1 then
redis.call('EXPIRE', KEYS[1], ARGV[2])
end
return lock
Redis提供了许多内置的原子命令,可以直接使用而不需要事务:
# 示例1: 计数器
INCR page:views
# 返回: (integer) 1
# 示例2: 分布式锁
SETNX lock:resource "value" EX 10
# 返回: (integer) 1
# 示例3: 安全的自增
INCRBY user:1:score 10
# 返回: (integer) 25
| 命令 | 功能说明 | 使用场景 |
|---|---|---|
| MULTI | 开始事务 | 需要执行多个命令作为一个原子操作 |
| EXEC | 执行事务 | 提交事务中的所有命令 |
| DISCARD | 取消事务 | 放弃执行事务中的命令 |
| WATCH | 监视键(乐观锁) | 需要实现乐观锁的场景 |
| UNWATCH | 取消监视 | 不再需要监视键时 |
| EVAL | 执行Lua脚本 | 需要执行复杂的原子操作 |
| EVALSHA | 执行已缓存的脚本 | 提高脚本执行性能 |
| SCRIPT LOAD | 缓存脚本 | 准备使用EVALSHA |
| SCRIPT FLUSH | 清除所有脚本 | 释放脚本缓存 |
通过本课程的学习,你已经掌握了Redis事务处理的核心概念和使用方法:
事务处理是Redis的重要功能,它确保了数据操作的原子性和一致性。在实际项目中,你需要根据具体场景选择合适的事务处理方式:对于简单操作,使用内置原子命令;对于复杂操作,使用Lua脚本;对于需要并发控制的场景,使用WATCH命令实现乐观锁。继续深入学习和实践,你将能够更好地运用这些知识解决实际问题,构建高并发、高可靠的Redis应用。