当前位置: 首页 > article >正文

说一下redis事务底层原理

Redis事务

1. 事务的基本流程

Redis 事务通过 MULTIEXECWATCH 等命令实现,底层原理可以分为以下几个步骤:

(1) MULTI 命令
  • 当客户端发送 MULTI 命令时,Redis 会将客户端标记为“事务模式”。
  • 在事务模式下,客户端发送的所有命令不会立即执行,而是被放入一个队列(命令队列)中。
(2) 命令入队
  • MULTIEXEC 之间,客户端发送的所有命令都会被追加到事务队列中。
  • 这些命令不会立即执行,而是等待 EXEC 命令的触发。
(3) EXEC 命令
  • 当客户端发送 EXEC 命令时,Redis 会依次执行事务队列中的所有命令。
  • 执行过程中,所有命令是原子的,不会被其他客户端的命令打断。
(4) WATCH 命令
  • WATCH 命令用于实现乐观锁。
  • 当客户端对一个或多个键执行 WATCH 后,如果在 EXEC 执行之前,这些键被其他客户端修改,则当前事务会失败(返回 nil

watch我们可以指定监听一个键和多个键,然后exec批量执行

WATCH key [key ...]

2. 事务的原子性

  • Redis 事务的原子性是通过单线程模型实现的。
  • Redis 是单线程的,所有命令都是顺序执行的。在 EXEC 执行时,事务队列中的命令会连续执行,不会被其他客户端的命令打断。

3. 事务的一致性

  • Redis 事务的一致性是通过 WATCH 机制实现的。
  • 如果 WATCH 的键在事务执行期间被修改,事务会失败,从而保证数据的一致性。

4. 事务的局限性

  • 不支持回滚:如果事务中的某个命令失败,其他命令仍然会执行,Redis 不会自动回滚。
  • 部分失败问题:事务中的命令可能会部分成功、部分失败。
  • 性能开销WATCH 机制会增加额外的性能开销。

Redis 7 对事务的优化

Redis 7 在事务机制上并没有完全改变底层实现,但引入了一些优化和改进:

1. 性能优化

  • Redis 7 对事务的执行流程进行了优化,减少了事务模式下的性能开销。
  • 通过改进命令队列的处理方式,提高了事务的执行效率。

2. Lua 脚本的增强

  • Redis 7 对 Lua 脚本的支持进行了增强,使得 Lua 脚本可以更好地与事务结合使用。
  • Lua 脚本在 Redis 7 中的执行效率更高,同时支持更多的 Redis 命令。

3. 更好的错误处理

  • Redis 7 改进了事务中的错误处理机制,使得事务失败时的错误信息更加清晰。
  • 如果事务中的某个命令失败,Redis 7 会返回更详细的错误信息,方便排查问题。

4. 功能增强

  • Redis 7 引入了更多的命令和功能,可以与事务结合使用。
  • 例如,Redis 7 支持更多的数据类型和操作,使得事务可以处理更复杂的场景。

Redis 事务的底层实现细节

1. 命令队列

  • 在事务模式下,Redis 会为每个客户端维护一个命令队列。
  • 所有在 MULTIEXEC 之间发送的命令都会被追加到队列中。

2. 事务执行

  • EXEC 命令被触发时,Redis 会依次执行命令队列中的所有命令。
  • 执行过程中,Redis 会保证命令的原子性,不会被其他客户端的命令打断。

3. WATCH 机制

  • WATCH 命令会监视一个或多个键。
  • 如果在 EXEC 执行之前,这些键被其他客户端修改,则当前事务会失败。
  • WATCH 的实现基于 Redis 的键空间通知机制。

总结

  • Redis 事务的底层原理 是基于 MULTI/EXEC/WATCH 机制,通过命令队列和乐观锁实现原子性和一致性。
  • Redis 7 在事务机制上进行了性能优化和功能增强,但底层实现并没有本质变化。
  • Redis 事务的局限性 包括不支持回滚、部分失败问题和性能开销。
  • 如果需要更强大的事务支持,可以结合 Lua 脚本或使用支持 ACID 事务的数据库。

Redis+Lua脚本实现手动回滚补偿

我们每一步执行失败,我们就依次撤销前面的操作

可惜这个并不是真正的acid,我们的mysql执行事务的时候宕机了,它的事务没有提交所以数据并不会进到mysql里面

而redis是人为控制的,所以我们执行lua脚本的时候宕机了,我们之前事务中执行的操作数据仍然进去了,这个是我们无法解决的

local key1 = KEYS[1]
local key2 = KEYS[2]
local key3 = KEYS[3]
local value = ARGV[1]

-- 记录原始值
local original_value1 = redis.call('GET', key1)
local original_value2 = redis.call('GET', key2)
local original_value3 = redis.call('GET', key3)

-- 第一步操作
redis.call('SET', key1, value)

-- 第二步操作
if redis.call('EXISTS', key2) == 0 then
    -- 手动回滚第一步操作
    redis.call('SET', key1, original_value1)
    return "Key2 does not exist"
end
redis.call('SET', key2, value)

-- 第三步操作
if redis.call('EXISTS', key3) == 0 then
    -- 手动回滚前两步操作
    redis.call('SET', key1, original_value1)
    redis.call('SET', key2, original_value2)
    return "Key3 does not exist"
end
redis.call('SET', key3, value)

return "Transaction successful"

http://www.kler.cn/a/572199.html

相关文章:

  • element-plus中添加全局样式
  • 大模型应用开发需要的知识和工具
  • 如何判断https使用了哪个版本的TLS?
  • MySQL-简介与基本命令
  • 从 Milvus 中导出数据到 JSON 文件的实践
  • 【第12节】C++设计模式(结构型模式)-Proxy(代理)模式
  • stm32移植LCD2002驱动
  • 数据结构理论
  • 解码未来!安徽艾德未来智能科技有限公司荣获“GAS消费电子科创奖-产品创新奖”!
  • 爬虫基础:一文掌握网页基础和爬虫原理
  • Javaweb中的过滤器
  • Compose Multiplatform开发记录之文本输入框
  • Svelte vs Vue:前端框架的深度对比与应用场景分析
  • RabbitMQ — 数据持久化实现MQ可靠性
  • 机场网络安全安全运营体系
  • 数据开发方向经过15年的发展再一次走到了十字路口
  • 【JQuery—前端快速入门】JQuery 操作元素
  • 火语言RPA--PDF提取文本
  • windows 上删除 node_modules
  • 紧跟 Web3 热潮,RuleOS 如何成为行业新宠?