Redis之事务

事务

关系型数据库通常会通过事务保证ACID,即原子性、隔离性、持久性、一致性,Redis虽然也有事务,但是没法完全支持事务的这几种特性,

Redis是单线程的,所以符合事务的隔离性,同时Redis具备备份功能,也符合持久性,但是一致性和原子性是没有的。

基本用法

关系型数据库的事务指令一般是begin(开始)、commit(提交)、rollback(回滚)三者。

begin();
try {
    command1();
    command2();
    commit();
} catch (Exception e) {
    rollback();
}
复制代码

redis也有三个与之对应,multi(开始)、exec(执行)、discard(丢弃)。


127.0.0.1:6379> multi

OK

127.0.0.1:6379(TX)> incr test

QUEUED

127.0.0.1:6379(TX)> incr test

QUEUED

127.0.0.1:6379(TX)> exec

1) (integer) 1

2) (integer) 2

复制代码

当事务multi开始时,之后的所有指令都将被存在服务端的一个事务队列中,最终服务端收到exec指令时,才开始执行整个事务队列,执行完毕后一次性返回所有的运行结果,

因为redis的单线程特性,不用担心事务指令队列执行时被其他指令打搅,所以redis事务是可以保证隔离性的。

下图是事务执行的完成过程,multi开启一个事务,服务端返回OK,后续的操作服务端都返回QUEUE,这意味着指令都被放入到队列中,等到exec时,服务端开始执行并返回结果。

image.png

redis事务没有原子性

事务的原子性是指事务要么完全成功,要么全部失败,而Redis是不支持事务原子性的可以用下面的demo演示,

通过结果可以看到,事务中最后一个指令执行失败了,因为data的值并不是一个数值,所以无法自增,根据原子性此时应该失败,但是下面的将data设置为hi依然执行了,并且事务结束后我们获取到的也是hi,

redis事务执行时出现执行失败后续的指令依然会得到执行,所以说redis事务是没有原子性的。

127.0.0.1:6379> get data

"nihao"

127.0.0.1:6379> multi

OK

127.0.0.1:6379(TX)> set data hello

QUEUED

127.0.0.1:6379(TX)> incr data

QUEUED

127.0.0.1:6379(TX)> set data hi

QUEUED

127.0.0.1:6379(TX)> exec

1) OK

2) (error) ERR value is not an integer or out of range

3) OK

127.0.0.1:6379> get data

"hi"

复制代码

discard (放弃执行)

discard指令用于丢弃事务缓存的指令队列里的所有指令,当然要在exec指令前执行,可以看到下面的demo,discard后,meta依然是nil,并没有发生指令执行的情况。

127.0.0.1:6379> get meta

(nil)

127.0.0.1:6379> multi

OK 

127.0.0.1:6379(TX)> incr meta

QUEUED

127.0.0.1:6379(TX)> incr meta

QUEUED

127.0.0.1:6379(TX)> discard

OK

127.0.0.1:6379> get meta

(nil)

复制代码

使用管道优化

Redis事务每次发送指令也会造成一次网络读写,当事务的指令较多,也会造成网络耗时的开销,所以一般事务可以搭配管道技术一起使用,节约网络开销,使程序更快。

watch指令

watch指令用在事务开始之前,监控一个变量,当事务开始执行时,发现该变量的值和watch时的值不一样,将会终止执行,并返回一个null给客户端。

一般业务场景中可以用做乐观锁,例如需要读取redis中的某个值在内存中做计算,最后把计算的值放回去,因为这个过程不是原子性的,很有可能读写过程中有别的线程把这个值改了并存到了redis中,这会造成混乱,

使用redis锁可以防止这个问题,但是分布式锁是悲观锁,如果并没有人在这期间修改该值,也造成了获取锁和释放锁的资源浪费,根据上面的描述watch比较适合做这个事情,

在事务开始前watch住该值,如果事务exec时发现该值没有改变,则正常执行,否则返回null告诉客户端执行失败,客户端做重试或其他业务逻辑处理。

需要注意的是redis进制在multi和exec执行watch,否则会出错 ERR WATCH inside MULTI is not allowed

在下方demo中,使用了watch来对redis中的一个key进行自增操作,最终的值正确为10,watch在其中配合事务做出了乐观锁的效果,颇有CAS的味道。

image.png

image.png

代码地址:github.com/qiaomengnan…

有帮助帮忙点下链接谢谢: Redis之事务-阿里云开发者社区

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值