1. 首页 > Redis教程 > 正文

Redis教程FG020-Redis事务与Lua脚本实战

本文档风哥主要介绍Redis事务与Lua脚本实战,包括事务概念、Lua脚本概念、使用场景、事务规划、Lua脚本规划、性能考虑、事务操作、Lua脚本操作、脚本加载、脚本管理以及实战案例等内容,风哥教程参考Redis官方文档Transaction和Scripting等内容编写,适合DBA人员和开发人员在生产环境中使用。

Part01-基础概念与理论知识

1.1 事务概念

Redis事务是一组命令的集合,它可以保证这些命令的原子性执行。Redis事务的特点:

  • 原子性:事务中的命令要么全部执行,要么全部不执行
  • 隔离性:事务执行过程中,其他客户端无法看到中间状态
  • 持久性:事务执行后,修改会被持久化
  • 一致性:事务执行前后,数据保持一致

1.2 Lua脚本概念

Redis支持使用Lua脚本执行复杂的业务逻辑,Lua脚本在Redis服务器端执行,具有原子性。Lua脚本的特点:

  • 原子性:脚本执行过程中不会被其他命令打断
  • 高性能:减少网络开销,提高执行效率
  • 灵活性:支持复杂的业务逻辑
  • 可重用:脚本可以被缓存和重用

1.3 使用场景

事务与Lua脚本的使用场景:

  • 原子操作:需要保证多个命令的原子性执行
  • 复杂业务逻辑:需要执行复杂的业务逻辑
  • 速率限制:实现API调用速率限制
  • 缓存失效:实现复杂的缓存失效策略
  • 计数器:实现原子计数器
  • 分布式锁:实现基于Redis的分布式锁

更多视频教程www.fgedu.net.cn

Part02-生产环境规划与建议

2.1 事务规划

生产环境事务规划:

  • 事务大小:控制事务的大小,避免过大的事务
  • 事务时间:控制事务的执行时间,避免长时间占用Redis
  • 错误处理:合理处理事务执行过程中的错误
  • 重试机制:实现合理的重试机制

2.2 Lua脚本规划

Lua脚本规划:

  • 脚本大小:控制脚本的大小,避免过大的脚本
  • 脚本复杂度:控制脚本的复杂度,避免过于复杂的逻辑
  • 脚本缓存:使用脚本缓存,提高执行效率
  • 错误处理:在脚本中实现合理的错误处理
  • 参数传递:合理传递参数,避免硬编码

2.3 性能考虑

# 性能考虑

## 1. 事务性能
– 事务中的命令会被批量执行,减少网络开销
– 事务执行过程中会阻塞其他命令的执行
– 避免在事务中执行耗时较长的命令

## 2. Lua脚本性能
– Lua脚本在服务器端执行,减少网络开销
– 脚本执行过程中会阻塞其他命令的执行
– 避免在脚本中执行耗时较长的操作
– 使用脚本缓存,提高执行效率

## 3. 最佳实践
– 对于简单的原子操作,使用事务
– 对于复杂的业务逻辑,使用Lua脚本
– 控制事务和脚本的执行时间
– 监控事务和脚本的执行情况

学习交流加群风哥QQ113257174

Part03-生产环境项目实施方案

3.1 事务操作

# 事务操作

## 1. 开始事务
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 multi

# 输出示例
OK

## 2. 执行命令
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 set user:1001:name “张三”
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 set user:1001:age “25”
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 set user:1001:email “zhangsan@example.com”

# 输出示例
QUEUED
QUEUED
QUEUED

## 3. 提交事务
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 exec

# 输出示例
1) OK
2) OK
3) OK

## 4. 放弃事务
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 multi
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 set user:1002:name “李四”
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 discard

# 输出示例
OK
QUEUED
OK

## 5. 检查事务结果
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 get user:1001:name
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 get user:1001:age
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 get user:1001:email
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 get user:1002:name

# 输出示例
“张三”
“25”
“zhangsan@example.com”
(nil)

3.2 Lua脚本操作

# Lua脚本操作

## 1. 执行简单的Lua脚本
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 eval “return redis.call(‘set’, KEYS[1], ARGV[1])” 1 test:key “test value”

# 输出示例
OK

## 2. 执行带参数的Lua脚本
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 eval “return redis.call(‘get’, KEYS[1])” 1 test:key

# 输出示例
“test value”

## 3. 执行复杂的Lua脚本
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 eval “\nlocal counter = redis.call(‘get’, KEYS[1])\nif counter then\n counter = tonumber(counter) + 1\nelse\n counter = 1\nend\nredis.call(‘set’, KEYS[1], counter)\nreturn counter\n” 1 user:counter

# 输出示例
(integer) 1

## 4. 再次执行脚本
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 eval “\nlocal counter = redis.call(‘get’, KEYS[1])\nif counter then\n counter = tonumber(counter) + 1\nelse\n counter = 1\nend\nredis.call(‘set’, KEYS[1], counter)\nreturn counter\n” 1 user:counter

# 输出示例
(integer) 2

## 5. 检查结果
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 get user:counter

# 输出示例
“2”

3.3 脚本加载

# 脚本加载

## 1. 加载脚本
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 script load “\nlocal counter = redis.call(‘get’, KEYS[1])\nif counter then\n counter = tonumber(counter) + 1\nelse\n counter = 1\nend\nredis.call(‘set’, KEYS[1], counter)\nreturn counter\n”

# 输出示例
“a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0”

## 2. 使用加载的脚本
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 evalsha “a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0” 1 user:counter

# 输出示例
(integer) 3

## 3. 再次使用脚本
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 evalsha “a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0” 1 user:counter

# 输出示例
(integer) 4

## 4. 检查结果
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 get user:counter

# 输出示例
“4”

3.4 脚本管理

# 脚本管理

## 1. 检查脚本是否存在
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 script exists “a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0”

# 输出示例
1) (integer) 1

## 2. 刷新脚本缓存
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 script flush

# 输出示例
OK

## 3. 再次检查脚本是否存在
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 script exists “a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0”

# 输出示例
1) (integer) 0

## 4. 杀死正在执行的脚本
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 script kill

# 输出示例
OK

风哥提示:Redis接口限流是保护系统的重要机制,合理的限流策略可以防止系统过载,确保系统的稳定性和可用性。在实际应用中,需要根据具体业务场景和数据特点,选择合适的限流算法和策略。

Part04-生产案例与实战讲解

4.1 原子操作

# 原子操作

## 1. 使用事务实现原子操作
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 multi
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 incr stock:product:1001
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 decr stock:product:1002
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 exec

# 输出示例
OK
QUEUED
QUEUED
1) (integer) 1
2) (integer) -1

## 2. 使用Lua脚本实现原子操作
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 eval “\nlocal stock1 = redis.call(‘incr’, KEYS[1])\nlocal stock2 = redis.call(‘decr’, KEYS[2])\nreturn {stock1, stock2}\n” 2 stock:product:1001 stock:product:1002

# 输出示例
1) (integer) 2
2) (integer) -2

## 3. 检查结果
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 get stock:product:1001
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 get stock:product:1002

# 输出示例
“2”
“-2”

4.2 复杂业务逻辑

# 复杂业务逻辑

## 1. 实现用户注册逻辑
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 eval “\nlocal user_id = KEYS[1]\nlocal username = ARGV[1]\nlocal email = ARGV[2]\n\n– 检查用户是否已存在\nlocal exists = redis.call(‘exists’, ‘user:’ .. user_id .. ‘:name’)\nif exists == 1 then\n return {err = ‘User already exists’}\nend\n\n– 注册用户\nredis.call(‘set’, ‘user:’ .. user_id .. ‘:name’, username)\nredis.call(‘set’, ‘user:’ .. user_id .. ‘:email’, email)\nredis.call(‘sadd’, ‘users’, user_id)\n\nreturn {ok = ‘User registered successfully’}\n” 1 1003 “王五” “wangwu@example.com”

# 输出示例
1) “ok”
2) “User registered successfully”

## 2. 检查用户注册结果
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 get user:1003:name
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 get user:1003:email
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 smembers users

# 输出示例
“王五”
“wangwu@example.com”
1) “1001”
2) “1003”

## 3. 尝试注册已存在的用户
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 eval “\nlocal user_id = KEYS[1]\nlocal username = ARGV[1]\nlocal email = ARGV[2]\n\n– 检查用户是否已存在\nlocal exists = redis.call(‘exists’, ‘user:’ .. user_id .. ‘:name’)\nif exists == 1 then\n return {err = ‘User already exists’}\nend\n\n– 注册用户\nredis.call(‘set’, ‘user:’ .. user_id .. ‘:name’, username)\nredis.call(‘set’, ‘user:’ .. user_id .. ‘:email’, email)\nredis.call(‘sadd’, ‘users’, user_id)\n\nreturn {ok = ‘User registered successfully’}\n” 1 1003 “王五” “wangwu@example.com”

# 输出示例
1) “err”
2) “User already exists”

4.3 速率限制

# 速率限制

## 1. 实现API调用速率限制
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 eval “\nlocal key = KEYS[1]\nlocal limit = tonumber(ARGV[1])\nlocal window = tonumber(ARGV[2])\n\nlocal current = redis.call(‘get’, key)\nif current then\n if tonumber(current) >= limit then\n return 0\n else\n redis.call(‘incr’, key)\n return 1\n end\nelse\n redis.call(‘set’, key, 1, ‘EX’, window)\n return 1\nend\n” 1 rate:limit:user:1001 5 60

# 输出示例
(integer) 1

## 2. 多次调用API
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 eval “\nlocal key = KEYS[1]\nlocal limit = tonumber(ARGV[1])\nlocal window = tonumber(ARGV[2])\n\nlocal current = redis.call(‘get’, key)\nif current then\n if tonumber(current) >= limit then\n return 0\n else\n redis.call(‘incr’, key)\n return 1\n end\nelse\n redis.call(‘set’, key, 1, ‘EX’, window)\n return 1\nend\n” 1 rate:limit:user:1001 5 60

$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 eval “\nlocal key = KEYS[1]\nlocal limit = tonumber(ARGV[1])\nlocal window = tonumber(ARGV[2])\n\nlocal current = redis.call(‘get’, key)\nif current then\n if tonumber(current) >= limit then\n return 0\n else\n redis.call(‘incr’, key)\n return 1\n end\nelse\n redis.call(‘set’, key, 1, ‘EX’, window)\n return 1\nend\n” 1 rate:limit:user:1001 5 60

$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 eval “\nlocal key = KEYS[1]\nlocal limit = tonumber(ARGV[1])\nlocal window = tonumber(ARGV[2])\n\nlocal current = redis.call(‘get’, key)\nif current then\n if tonumber(current) >= limit then\n return 0\n else\n redis.call(‘incr’, key)\n return 1\n end\nelse\n redis.call(‘set’, key, 1, ‘EX’, window)\n return 1\nend\n” 1 rate:limit:user:1001 5 60

$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 eval “\nlocal key = KEYS[1]\nlocal limit = tonumber(ARGV[1])\nlocal window = tonumber(ARGV[2])\n\nlocal current = redis.call(‘get’, key)\nif current then\n if tonumber(current) >= limit then\n return 0\n else\n redis.call(‘incr’, key)\n return 1\n end\nelse\n redis.call(‘set’, key, 1, ‘EX’, window)\n return 1\nend\n” 1 rate:limit:user:1001 5 60

# 输出示例
(integer) 1
(integer) 1
(integer) 1
(integer) 1

## 3. 达到速率限制
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 eval “\nlocal key = KEYS[1]\nlocal limit = tonumber(ARGV[1])\nlocal window = tonumber(ARGV[2])\n\nlocal current = redis.call(‘get’, key)\nif current then\n if tonumber(current) >= limit then\n return 0\n else\n redis.call(‘incr’, key)\n return 1\n end\nelse\n redis.call(‘set’, key, 1, ‘EX’, window)\n return 1\nend\n” 1 rate:limit:user:1001 5 60

# 输出示例
(integer) 0

## 4. 检查速率限制状态
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 get rate:limit:user:1001
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 ttl rate:limit:user:1001

# 输出示例
“5”
(integer) 55

4.4 缓存失效

# 缓存失效

## 1. 实现缓存失效逻辑
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 eval “\nlocal product_id = KEYS[1]\nlocal stock = tonumber(ARGV[1])\n\n– 更新库存\nredis.call(‘set’, ‘stock:product:’ .. product_id, stock)\n\n– 失效相关缓存\nredis.call(‘del’, ‘product:’ .. product_id .. ‘:info’)\nredis.call(‘del’, ‘product:’ .. product_id .. ‘:details’)\nredis.call(‘del’, ‘products:list’)\n\nreturn {ok = ‘Stock updated and cache invalidated’}\n” 1 1001 100

# 输出示例
1) “ok”
2) “Stock updated and cache invalidated”

## 2. 检查缓存失效结果
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 get stock:product:1001
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 get product:1001:info
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 get product:1001:details
$ /redis/app/bin/redis-cli -h 192.168.1.100 -p 6379 -a fgedu@2026 get products:list

# 输出示例
“100”
(nil)
(nil)
(nil)

更多学习教程公众号风哥教程itpux_com

Part05-风哥经验总结与分享

5.1 最佳实践

Redis事务与Lua脚本实战最佳实践:

  • 事务使用:对于简单的原子操作,使用事务,学习交流加群风哥微信: itpux-com
  • Lua脚本使用:对于复杂的业务逻辑,使用Lua脚本
  • 脚本缓存:使用脚本加载和缓存,提高执行效率
  • 错误处理:在脚本中实现合理的错误处理
  • 性能监控:监控事务和脚本的执行情况
  • 资源管理:控制事务和脚本的执行时间,避免占用过多资源

5.2 常见问题

常见问题及解决:

  • 事务阻塞:避免在事务中执行耗时较长的命令
  • 脚本执行时间过长:控制脚本的复杂度和执行时间
  • 脚本缓存溢出:定期刷新脚本缓存
  • 错误处理:在脚本中实现合理的错误处理
  • 参数传递:合理传递参数,避免硬编码

5.3 优化技巧

风哥提示:Redis事务和Lua脚本是实现原子操作和复杂业务逻辑的强大工具。在实际应用中,需要根据业务需求选择合适的方法,合理设计事务和脚本,确保系统的可靠性和性能。

# 优化技巧

## 1. 事务优化
– 控制事务的大小,避免过大的事务
– 控制事务的执行时间,避免长时间占用Redis
– 合理处理事务执行过程中的错误
– 使用管道批量执行事务命令

## 2. Lua脚本优化
– 控制脚本的大小和复杂度
– 使用脚本缓存,提高执行效率
– 合理传递参数,避免硬编码
– 在脚本中实现合理的错误处理
– 避免在脚本中执行耗时较长的操作

## 3. 性能优化
– 监控事务和脚本的执行情况
– 优化脚本的执行效率
– 合理使用事务和Lua脚本
– 避免在高峰期执行复杂的脚本

## 4. 安全优化
– 避免在脚本中执行危险的操作
– 合理控制脚本的权限
– 定期审计脚本的执行情况

## 5. 监控优化
– 监控事务和脚本的执行时间
– 监控脚本缓存的使用情况
– 设置合理的告警机制
– 定期分析事务和脚本的使用情况

通过本文档的学习,您应该掌握了Redis事务与Lua脚本实战,能够在生产环境中合理使用事务和Lua脚本实现原子操作、复杂业务逻辑、速率限制、缓存失效等功能。在实际应用中,需要根据具体业务场景选择合适的方法,确保系统的可靠性和性能。

风哥提示:Redis事务和Lua脚本是实现原子操作和复杂业务逻辑的强大工具。在实际应用中,需要根据业务需求选择合适的方法,合理设计事务和脚本,确保系统的可靠性和性能。

from Redis视频:www.itpux.com

本文由风哥教程整理发布,仅用于学习测试使用,转载注明出处:http://www.fgedu.net.cn/10327.html

联系我们

在线咨询:点击这里给我发消息

微信号:itpux-com

工作日:9:30-18:30,节假日休息