<返回目录     Powered by claud/xia兄

第10课: 事务处理

MULTI/EXEC与乐观锁

课程简介

本课程将深入讲解事务处理的核心概念和实践应用。主要内容包括:MULTI、EXEC、WATCH、Lua脚本、原子操作。

核心知识点

详细内容

Redis的事务处理是非常重要的功能模块,在实际开发中有广泛的应用。通过本课程的学习,你将全面掌握事务处理的使用技巧。

一、Redis事务基础

1. 事务概念

Redis事务是一组命令的集合,这些命令会被序列化并按顺序执行,在执行过程中不会被其他客户端的命令干扰。Redis事务具有以下特性:

2. 基本命令

# 开始事务
MULTI
# 返回: OK

# 执行事务
EXEC
# 返回: [命令执行结果数组]

# 取消事务
DISCARD
# 返回: OK

# 监视键(乐观锁)
WATCH key1 [key2 ...]
# 返回: OK

# 取消监视
UNWATCH
# 返回: OK

3. 事务执行流程

# 示例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命令

1. WATCH原理

WATCH命令用于实现乐观锁,它会监视一个或多个键,如果在事务执行前这些键被其他客户端修改,那么事务会被取消执行。

2. 使用示例

# 示例: 实现安全的余额扣减
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

3. 注意事项

三、Lua脚本

1. Lua脚本简介

Redis从2.6.0开始支持Lua脚本,通过执行Lua脚本可以实现更复杂的原子操作。Lua脚本具有以下优势:

2. 基本命令

# 执行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

3. 使用示例

# 示例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

四、原子操作

1. 内置原子命令

Redis提供了许多内置的原子命令,可以直接使用而不需要事务:

2. 使用示例

# 示例1: 计数器
INCR page:views
# 返回: (integer) 1

# 示例2: 分布式锁
SETNX lock:resource "value" EX 10
# 返回: (integer) 1

# 示例3: 安全的自增
INCRBY user:1:score 10
# 返回: (integer) 25

五、实际应用场景

1. 常见场景

2. 性能优化

3. 常见问题排查

命令参考

命令 功能说明 使用场景
MULTI 开始事务 需要执行多个命令作为一个原子操作
EXEC 执行事务 提交事务中的所有命令
DISCARD 取消事务 放弃执行事务中的命令
WATCH 监视键(乐观锁) 需要实现乐观锁的场景
UNWATCH 取消监视 不再需要监视键时
EVAL 执行Lua脚本 需要执行复杂的原子操作
EVALSHA 执行已缓存的脚本 提高脚本执行性能
SCRIPT LOAD 缓存脚本 准备使用EVALSHA
SCRIPT FLUSH 清除所有脚本 释放脚本缓存
重要提示:

性能优化建议

实践练习

练习任务:
  1. 基础事务:使用MULTI/EXEC执行一个包含多个SET命令的事务
  2. 乐观锁:使用WATCH命令实现一个安全的余额扣减操作
  3. Lua脚本:编写一个Lua脚本实现分布式锁功能
  4. 原子命令:使用INCR系列命令实现一个高并发计数器
  5. 错误处理:测试事务中的错误处理机制
  6. 性能测试:对比事务、Lua脚本和原子命令的性能差异
  7. 实际应用:实现一个简单的秒杀系统,使用事务或Lua脚本确保库存扣减的原子性
  8. 脚本缓存:使用SCRIPT LOAD和EVALSHA命令提高脚本执行性能

总结

通过本课程的学习,你已经掌握了Redis事务处理的核心概念和使用方法:

事务处理是Redis的重要功能,它确保了数据操作的原子性和一致性。在实际项目中,你需要根据具体场景选择合适的事务处理方式:对于简单操作,使用内置原子命令;对于复杂操作,使用Lua脚本;对于需要并发控制的场景,使用WATCH命令实现乐观锁。继续深入学习和实践,你将能够更好地运用这些知识解决实际问题,构建高并发、高可靠的Redis应用。