Redis对事务处理详解

  • Redis事务的概念

  关于事务的概念不用多说了,大家都懂,关于DBMS中的事务可以参考一下我的这篇博客:https://blog.csdn.net/Leccen/article/details/106583721
  我们知道redis的理念是追求简单和高效,因此它的事务并没有像关系数据库那样复杂。redis的事务是一组命令的集合,一个事务中所有命令都会序列化,其他客户端提交的命令请求不会插入到事务执行命令序列。redis事务包含以下的命令:
(1)multi: 标记一个事务块的开始
(2)exec: 执行所有事务块的命令
(3)discard: 取消事务,放弃执行事务块内的所有命令
(4)watch: 监视一个或者多个key,如果在事务执行前(多个key的任意一个)key被其他命令所改动,那么事务将被打断
(5)unwatch: 取消对所有key的监视

  • multi

  它用于开启一个事务,返回为ok。multi 执行之后, 客户端可以继续向服务器发送任意多条命令, 这些命令不会立即被执行, 而是被放到一个队列中, 当 exec 命令被调用时, 所有队列中的命令才会被执行。

> multi
OK
> incr foo
QUEUED

> incr bar
QUEUED
> Eexec
1) (integer) 1
2) (integer) 1
  • exec

  exec 命令的回复是一个数组, 数组中的每个元素都是执行事务中的命令所产生的回复。 其中, 回复元素的先后顺序和命令发送的先后顺序一致。当客户端处于事务状态时, 所有传入的命令都会返回一个内容为 QUEUED 的状态回复, 这些被入队的命令将在 exec 命令被调用时执行。
  在一个redis事务中可能会出现一些错误。假如是在执行exec之前,一些语法上的问题,例如像你的code在编译阶段发生的编译错误,那么在这种情况下,服务器会对命令入队失败的情况进行记录,并在客户端调用 EXEC 命令时,拒绝执行并自动放弃这个事务。对于在调用exec命令之后失败,例如像事务中的命令可能处理了错误类型的键,比如将列表命令用在了字符串键上面,诸如此类。那么这中情况下,即使是事务中的某些命令在执行时出错,其他命令仍然会执行。也就是说Redis中,单条命令是原子性执行的,但事务不保证原子性,且没有回滚。事务中任意命令执行失败,其余的命令仍会被执行。
在这里插入图片描述
  我刚开始的时候对redis的这种做法是感觉非常奇怪的,既然你支持事务为什么却没有回滚机制呢?其实官方有给出解释:Redis 命令只会因为错误的语法而失败(并且这些问题不能在入队时发现),或是命令用在了错误类型的键上面:这也就是说,从实用性的角度来说,失败的命令是由编程错误造成的,而这些错误应该在开发的过程中被发现,而不应该出现在生产环境中。因为不需要对回滚进行支持,所以 Redis 的内部可以保持简单且快速。保证了初衷。

  • discard

  当执行 discard 命令时, 事务会被放弃, 事务队列会被清空, 并且客户端会从事务状态中退出。

redis> get name
"Lessen"
redis> multi
OK
redis> set name Paul
QUEUED
redis> discard
OK
redis> get name
"Lessen"
  • watch & unwatch

  watch命令用于监视一个或多个 key ,如果在事务执行之前这个或这些 key 被其他命令所改动,那么事务将被打断。watch需要配置multi事务来使用。一般是先watch key,然后开启事务对key操作。当 exec 被调用时, 不管事务是否成功执行, 对所有键的监视都会被取消。另外, 当客户端断开连接时, 该客户端对键的监视也会被取消。 对于一些需要改动多个键的事务, 有时候程序需要同时对多个键进行加锁, 然后检查这些键的当前值是否符合程序的要求。 当值达不到要求时, 就可以使用 unwatch 命令来取消目前对键的监视, 中途放弃这个事务, 并等待事务的下次尝试。

redis>set money 100
OK
redis>watch key
OK
redis>multi	# 开启事务
OK
redis>incr money	# 自增
QUEUED
redis>exec	# 执行前我打开另一个终端修改了money的值为200
(nil)
redis>get money
"200"

使用无参数的 unwatch 命令可以手动取消对所有键的监视。

redis>set name Lessen
OK
redis>watch name	# 监视name
OK
redis>set name Paul		# 修改name的值
OK
redis>get name		# 修改成功,此时开启事务必然无效
"Paul"
redis>unwatch	# 取消监视
OK
redis>watch name	# 重新监视
OK
redis>multi	# 开启事务
OK
redis>set name LessenPaul
QUEUED
redis>exec		# 成功修改
OK
redis>get name
"LessenPaul"

exec 被调用时, 不管事务是否成功执行, 对所有键的监视都会被取消。再次强调一下这个,一个watch对应一个事务。看以下这个例子:

redis>set name Lessen
OK
redis>watch name
OK
redis>set name Paul
OK
redis>multi
OK
redis>exec	# 就开启一个事务,什么命令都不加,然后执行事务
(nil)
redis>multi
OK
redis>set name LessenPaul
QUEUED
redis>exec
OK
redis>get name
"LessenPaul"
  • 乐观锁和悲观锁

  我们这里提一点题外话,关于锁的。mysql里面的"表锁"和"行锁"。“表锁"就是为了保证数据的一致性,将整张表锁上,这样就只能一个人修改。举个例子,你在商场理突然尿急上卫生间,进去之后就把最外面门锁上了,这样就导致你一个人本来只需要一个坑位但是你把其他坑位也给Block了。虽然数据的一致性、安全性好,但是并发性会极差。于是就出现了"行锁”,"行锁"在mysql中,就类似于表中有一个版本号的字段,假设有一条记录的版本号为1,A和B同时修改这条记录,那么一旦提交,就会改变那个版本号,假设变为2。如果A先提交了,那么数据库中对应记录的版本号已经变了,但是B对应的版本号还是之前的,那么提交之后会立即报错,这样就知道这条记录被人修改了,需要重新获取对应版本号的记录。
  悲观锁(pessimistic lock),顾名思义,就是很悲观,每次拿数据的时候都会认为别人会修改,所以每次拿数据的时候都会上锁,这样别人想拿到这个数据就会block住,直到拿到锁。
  乐观锁(optimistic lock),顾名思义,就是很乐观,每次拿数据的时候都会认为别人不会修改,所以每次拿数据的时候都不会上锁。但是在更新数据的时候会判断一下在此期间别人有没有去更新这条数据,可以使用版本号等机制。乐观锁使用于多读的应用类型,这样可以提高吞吐量。乐观锁策略就是:提交版本必须大于记录的当前版本才能更新。
  讲这个什么意思呢?watch机制为Redis 事务提供 check-and-set (CAS)行为。在程序中就可以用watch来实现乐观锁。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值