Redis的线程模型和事务( 三 )


Redis 事务的本质是一组命令的集合 。 事务支持一次执行多个命令 , 一个事务中所有命令都会被序列化 。 在事务执行过程 , 会按照顺序串行化执行队列中的命令 , 其他客户端提交的命令请求不会插入到事务执行命令序列中 。
总结说:redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令 。
Redis事务相关命令 :

  • watch key1 key2 ... : 监视一或多个key,如果在事务执行之前 , 被监视的key被其他命令改动 , 则事务被打断 ( 类似乐观锁 )
  • multi : 标记一个事务块的开始( queued )
  • exec : 执行所有事务块的命令 ( 一旦执行exec后 , 之前加的监控锁都会被取消掉 )
  • discard : 取消事务 , 放弃事务块中的所有命令
  • unwatch : 取消watch对所有key的监控
事务执行过程
multi命令可以将执行该命令的客户端从非事务状态切换至事务状态 , 执行后 , 后续的普通命令(非multi、watch、exec、discard的命令)都会被放在一个事务队列中 , 然后向客户端返回QUEUED回复 。
事务队列是一个以先进先出(FIFO)的方式保存入队的命令 , 较先入队的命令会被放到数组的前面 , 而较后入队的命令则会被放到数组的后面 。
当一个处于事务状态的客户端向服务器发送exec命令时 , 这个exec命令将立即被服务器执行 。 服务器会遍历这个客户端的事务队列 , 执行队列中保存的所有的命令 , 最后将执行命令所得的结果返回给客户端 。
当一个处于事务状态的客户端向服务器发送discard命令时 , 表示事务取消 , 客户端从事务状态切换回非事务状态 , 对应的事务队列清空 。
watch
watch命令可被用作乐观锁 。 它可以在exec命令执行前 , 监视任意数量的数据库键 , 并在exec命令执行时 , 检查监视的键是否至少有一个已经被其他客户端修改过了 , 如果修改过了 , 服务器将拒绝执行事务 , 并向客户端返回代表事务执行失败的空回复 。 而unwatch命令用于取消对所有键的监视 。
要注意 , watch是监视键被其他客户端修改过 , 即其他的会话连接中 。 如果你在同一个会话下自己watch自己改 , 是不生效的 。
4.2. ACID分析在传统关系型数据库中 , 事务都是遵循ACID四个特性的 , 那么Redis的事务遵循吗?
原子性(Atomicity)
原子性是指事务包含的所有操作要么全部成功 , 要么全部失败回滚 。
Redis 开始事务 multi 命令后 , Redis 会为这个事务生成一个队列 , 每次操作的命令都会按照顺序插入到这个队列中 。 这个队列里面的命令不会被马上执行 , 直到 exec 命令提交事务 , 所有队列里面的命令会被一次性 , 并且排他的进行执行 。
但是呢 , 当事务队列里面的命令执行报错时 , 会有两种情况:(1)一种错误类似于Java中的CheckedException , Redis执行器会检测出来 , 如果某个命令出现了这种错误 , 会自动取消事务 , 这是符合原子性的;(2)另一种错误类似于Java中的RuntimeExcpetion , Redis执行器检测不出来 , 当执行报错了已经来不及了 , 错误命令后续的命令依然会执行完毕 , 并不会回滚 , 因此不符合原子性 。
一致性(Consistency)
一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态 , 也就是说一个事务执行之前和执行之后都必须处于一致性状态 。
因为达不成原子性 , 其实严格上来讲 , 也就达不成一致性 。
隔离性(Isolation)
隔离性是当多个用户并发访问数据库时 , 比如操作同一张表时 , 数据库为每一个用户开启的事务 , 不能被其他事务的操作所干扰 , 多个并发事务之间要相互隔离 。
回顾前面的基础 , Redis 因为是单线程依次执行队列中的命令的 , 没有并发的操作 , 所以在隔离性上有天生的隔离机制 。, 当 Redis 执行事务时 , Redis 的服务端保证在执行事务期间不会对事务进行中断 , 所以 , Redis 事务总是以串行的方式运行 , 事务也具备隔离性 。


推荐阅读