zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    Django(app的概念、ORM介绍及编码错误问题)
    Django(完整的登录示例、render字符串替换和redirect跳转)
    Construct Binary Tree from Preorder and Inorder Traversal
    Single Number II
    Single Number
    Binary Tree Level Order Traversal II
    Binary Tree Level Order Traversal
    Binary Tree Zigzag Level Order Traversal
    Recover Binary Search Tree
    Add Binary
  • 原文地址:https://www.cnblogs.com/Vincent-yuan/p/15383433.html
Copyright © 2011-2022 走看看