【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问题解决方式相类似

相关推荐
Kagol7 小时前
macOS 和 Windows 操作系统下如何安装和启动 MySQL / Redis 数据库
redis·后端·mysql
hzulwy7 小时前
Redis常用的数据结构及其使用场景
数据库·redis
ashane13149 小时前
Redis 哨兵集群(Sentinel)与 Cluster 集群对比
redis
Y第五个季节9 小时前
Redis - HyperLogLog
数据库·redis·缓存
Justice link10 小时前
企业级NoSql数据库Redis集群
数据库·redis·缓存
爱的叹息13 小时前
Spring Boot 集成Redis 的Lua脚本详解
spring boot·redis·lua
morris13121 小时前
【redis】redis实现分布式锁
数据库·redis·缓存·分布式锁
爱的叹息1 天前
spring boot集成reids的 RedisTemplate 序列化器详细对比(官方及非官方)
redis
weitinting1 天前
Ali linux 通过yum安装redis
linux·redis
纪元A梦1 天前
Redis最佳实践——首页推荐与商品列表缓存详解
数据库·redis·缓存