【Redis进阶】事务

1. Redis与MySQL的事务差别

相信一谈到事务,大家马上就能联想到MySQL的事务,其事务具有ACID四大特性,但是Redis的事务相比较于MySQL,那就是个"弟中弟",下面我们就来简单对比两者的事务特性:

  • 原子性:关于Redis事务是否具备"原子性"是存在争议的,Redis的原子性最原始的含义就是指"把一组操作打包在一起执行,要么全都执行;要么全都不执行"(但是Redis并没有回滚操作,即若中间有操作执行失败,Redis也会继续执行下去),但是对于MySQL来说,其提供的"原子性"特性保证这组操作要么全都执行成功,要么全都不执行(即MySQL事务带有回滚机制)
  • 一致性:Redis的事务不存在一致性,因为没有提供回滚机制,因此若中间操作执行失败,就可能存在数据不一致的情况
  • 持久性:Redis中的事务不存在持久性,尽管Redis含有持久化机制,但是这与事务特性无关
  • 隔离性:Redis中的事务不涉及到隔离性,因为Redis是一个单线程的服务器模型,所有的请求/事务都是串行执行的

2. Redis事务概述

目的 :Redis中的事务最根本的目的就是将一组操作打包在一起执行,中间不允许有别的客户端插队执行

举一个形象生动的例子,我和我的朋友一起去吃烧烤,我一开始点了一堆五花肉、羊肉、牛肉、猪肉等菜品,但是我的朋友还没到,于是我让老板先存单不着急烤,当我的朋友到了以后他又点了一堆猪腰子、生蚝等菜品。此时我们告诉老板可以烤了(在这里我们前后两堆菜品一定是需要放在一起烤的,不会出现我们的菜品跟别的客人放一起端上来的情况)
实现方式:Redis服务器为每个客户端维护了一个队列(先进先出),当"事务开启"之后,客户端发送的命令就会加入到服务端对应的队列上而不是立即执行,如果遇到相应客户端的"执行事务"命令则按顺序将队列中的操作进行执行,并且根据原子性,只有处理完一个客户端的队列操作后才能执行下一个客户端的事务

💡 思考一下:为什么Redis中的事务如此简单,不设计的跟MySQL一样强大呢?

  1. 在空间上,需要额外的空间存储相应的更新操作(如undolog、redolog等等)
  2. 在时间上,回滚操作也需要额外的执行性能开销

而我们Redis是基于内存的数据库,追求性能!

应用场景:我们用到Redis中的事务操作往往在一些需要将一组操作放在一起执行的场景中,比如说秒杀场景中,提供下列伪代码,如果不加以任何限制让多个客户端并发执行,就可能存在超卖的问题!在Redis中我们就可以通过事务的方式来解决这种问题!

java 复制代码
get count;
if (count > 0) {
    // 执行下单操作
    decr count;
}

我们借助事务操作就可以实现类似如下功能:

java 复制代码
开启事务
get count;
if (count > 0) {
    // 执行下单操作
    decr count;
}
执行事务;

这样一来,当服务器接收到第二个客户端的"执行事务"命令时,第一个客户端的事务已经执行完毕,此时第二个客户端获取到的count就是正确的,也就避免了超卖问题。但是这里我们还需要想一想,Redis如何支持if条件判断语句呢?由于Redis支持原生Lua脚本,可以搭配Lua脚本进行if条件判断(感兴趣的小伙伴自行了解)

3. Redis事务操作

开启事务: MULTI
执行事务: EXEC
放弃当前事务: DISCARD

我们使用MULTI开启事务,并添加了两个key,但是并没有执行事务,此时如果使用第二个客户端进行访问,获取不到k1与k2的值:

但是当我们使用EXEC执行事务后,第二个客户端就可以进行访问了

温馨提示:如果在事务开启之后,突然断电,相当于执行了DISCARD丢弃当前事务操作!

4. watch监视原理

我们可以使用WATCH命令用来监视某个key是否在事务执行之前发生了变化:

在当前客户端上我们先使用watch k1监视k1键,此时第二个客户端执行了set k1 111此时当第一个客户端执行事务后,发现k1已经被改了于是返回nil,不执行set k1 222命令。
watch实现原理 :类似于乐观锁,即预估当前发生锁冲突的概率不是很高,加锁操作的成本也比较低

watch中的乐观锁是基于 "版本号 " 这样的机制实现的:

  1. 一开始客户端1执行watch k1时就会给k1分配一个版本号(递增的数字),例如为1,之后使用MULTI开始事务
  2. 这时客户端2执行SET k1 111修改了k1,此时版本号增长为2(由于客户端1此时还没有执行EXEC命令,因此由客户端2先执行)
  3. 此时客户端1执行EXEC命令,首先判断当时记录的k1的版本号是否与当前版本号一致,如果不一致说明有其余客户端更改,则返回nil,不执行事务中的内容;如果一致则继续执行

这个思想方法也与CAS的ABA问题解决方式相类似

相关推荐
岁月变迁呀3 小时前
Redis梳理
数据库·redis·缓存
Code apprenticeship5 小时前
怎么利用Redis实现延时队列?
数据库·redis·缓存
百度智能云技术站5 小时前
广告投放系统成本降低 70%+,基于 Redis 容量型数据库 PegaDB 的方案设计和业务实践
数据库·redis·oracle
装不满的克莱因瓶5 小时前
【Redis经典面试题六】Redis的持久化机制是怎样的?
java·数据库·redis·持久化·aof·rdb
黄名富8 小时前
Redis 附加功能(二)— 自动过期、流水线与事务及Lua脚本
java·数据库·redis·lua
G_whang9 小时前
centos7下docker 容器实现redis主从同步
redis·docker·容器
.生产的驴9 小时前
SpringBoot 对接第三方登录 手机号登录 手机号验证 微信小程序登录 结合Redis SaToken
java·spring boot·redis·后端·缓存·微信小程序·maven
我叫啥都行13 小时前
计算机基础复习12.22
java·jvm·redis·后端·mysql
阿乾之铭13 小时前
Redis四种模式在Spring Boot框架下的配置
redis
on the way 12315 小时前
Redisson锁简单使用
redis