• Redis为什么不支持回滚


    我们知道Redis是支持事务的,但是它里面的事务为什么不支持回滚呢?

    1.在Redis中,命令只会因为错误的语法而失败,或者是命令用在了错误类型的键上面;

    也就是说,从实用的角度说,失败的命令是由编译错误造成的,而这些错误应该在开发过程中被发现,而不应该出现在生产环境中。

    2.因为不需要对回滚进行支持,所以Redis的内部可以保持简单且快速。

    3.有种观点认为,Redis处理事务的做法会产生bug,但是需要注意的是,通常情况下,回滚并不能解决编程错误而带来的问题。

    例如,如果你本来想通过INCR命令将键的值加1,却不小心加了2,又或者对错误类型的键执行了INCR,回滚是没办法处理这些情况的。

    鉴于没有任何机制能避免程序员自己造成的错误,并且这类错误通常不会在生产环境出现,所以Redis选择了更简单、更快速的无回滚方式处理事务。

    我们都知道,事务有 4 大特性。分别是:原子性(Atomicity)一致性(Consistency)隔离性(Isolation)持久性(Durability)

    原子性(Atomicity)

    原子性是指事务是一个不可分割的工作单位,事务中的操作要么全部成功,要么全部失败。比如在同一个事务中的 SQL 语句,要么全部执行成功,要么全部执行失败。

    然而,Redis 中的事务,如果在执行中间失败了,在事务开始之前到遇到命令执行失败这中间执行的命令不会回滚。

    这就导致了,Redis 的事务没有保证原子性。

    下面看一个例子:

    redis 127.0.0.1:7000> multi
    OK
    redis 127.0.0.1:7000> set a aaa
    QUEUED
    redis 127.0.0.1:7000> set b bbb
    QUEUED
    redis 127.0.0.1:7000> set c ccc
    QUEUED
    redis 127.0.0.1:7000> hhh www.xttblog.com
    QUEUED
    redis 127.0.0.1:7000> exec
    1) OK
    2) OK
    3) OK
    4)-ERR Operation against a key holding the wrong kind of value

    虽然上面这段命令执行过程中会遇到错误,但是不会回滚。

    set a、set b 等命令操作执行成功了。可以通过 get 取到对应的值。具体我就不贴代码了。

     Redis 执行事务过程

    Redis 客户端提供了管道操作。

    管道可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作;

    中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做。

    一致性(Consistency)

    Redis 事务的命令主要是 multi(开启事务) exec(执行事务) discard(丢弃事务)。

    Redis 事务在执行的过程中,不会处理其它命令,而是等所有命令都执行完后,再处理其它命令。

    因此在 Redis 事务在执行过程中发生错误或进程被终结,都能保证数据的一致性。

    隔离性(Isolation)

    前面也说了,Redis 的事务在执行的过程中,不会处理其它命令,而是等所有命令都执行完后,再处理其它命令。

    不管用不用管道,只要执行了 multi,就会阻塞其他操作。因此 Redis 事务是满足隔离性的。

     Redis的事务没有隔离级别

    况且 Redis 是一个单线程的。

    另外需要注意的是:Redis 虽然保证了隔离性,但是它对事务没有隔离级别的概念,

    所以就不会产生我们使用关系型数据库需要关注的脏读,幻读,重复读的问题

    持久性(Durability)

    这个特性可谈可不谈,因为大部分情况下,Redis 是用来做缓存的。很多公司是没有做持久化的,因此可以说 Redis 事务的持久性是不支持的。

    Redis 事务不过是用队列包裹起了一组 Redis 命令,并没有提供任何额外的持久性功能,

    所以事务的持久性由 Redis 所使用的持久化模式决定:

    • 在单纯的内存模式下,事务肯定是不持久的。
    • 在 RDB 模式下服务器可能在事务执行之后、RDB 文件更新之前的这段时间失败,所以 RDB 模式下的 Redis 事务也是不持久的。
    • 在 AOF 的总是 SYNC模式下,事务的每条命令在执行成功之后,都会立即调用 fsync 或 fdatasync 将事务数据写入到 AOF 文件但是,这种保存是由后台线程进行的,主线程不会阻塞直到保存成功,所以从命令执行成功到数据保存到硬盘之间,还是有一段非常小的间隔,所以这种模式下的事务也是不持久的
    • 其他 AOF 模式也和总是 SYNC模式类似,所以它们都是不持久的

    因此,我们可以说 Redis 的事务是不支持持久化的,或者说持久化是有缺陷的。就像 Redis 的分布式锁一样。

    watch 机制实现乐观锁

    虽说 Redis 不支持直接回滚,但我们可以通过 Redis 提供的一个命令来实现回滚。

    这个命令就是 watch,该命令可以为 Redis 事务提供 check-and-set (CAS)行为。

    我们可以使用 watch 命令来监视一个或多个 key,如果被监视的 key 在事务执行前被修改过那么本次事务将会被取消,也就是所谓的回滚。

    只有确保被监视的 key,在事务开始前到执行 这段时间内未被修改过事务才会执行成功(类似乐观锁)

    如果一次事务中存在被监视的 key,无论此次事务执行成功与否,该 key 的监视都将会在执行后失效 也就是说监视是一次性的。

    总结

    总的来说:Redis 事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令。没有用过或了解过 Redis 事务的网友,千万别拿它和数据库事务相比较,否则面试中肯定会吃亏!

    如果你想让几个 Redis 的命令保证原子性,那我建议你使用 Lua 脚本,而不是 Redis 事务!

    参考:https://mp.weixin.qq.com/s/Wqa6lpyiwaldMbt1neScQA

  • 相关阅读:
    移动端调试利器-vConsole
    html手机调试
    vue实现PC端分辨率适配
    uniapp 判断 IOS和Android的GPS是否开启并设置启动
    Fiddler抓包工具使用
    js对象数组多字段排序
    时间戳转换时间格式
    从一个数组中删除另一个数组中存在的元素
    Python_下载二次登录后的页面源码
    Python_ip被封用代理
  • 原文地址:https://www.cnblogs.com/Vincent-yuan/p/15383433.html
Copyright © 2020-2023  润新知