Redis基本的事务操作以及使用Redis实现乐观锁


以下总结了关于Redis比较全面的知识笔记以及面试题,方便自己复习的同时希望对大家有所帮助。

序号 内容 链接地址
1 Redis的基础知识、单机版安装、数据类型介绍(老版本) https://blog.csdn.net/weixin_43246215/article/details/107474283
2 Redis常用命令、配置文件介绍、数据持久化方式、集群搭建 (老版本) https://blog.csdn.net/weixin_43246215/article/details/107947562
3 Redis的五种常用数据类型、三种特殊数据类型详解 https://blog.csdn.net/weixin_43246215/article/details/108041739
4 Redis基本的事务操作以及使用Redis实现乐观锁 https://blog.csdn.net/weixin_43246215/article/details/108045931
5 使用JedisAPI操作Redis以及Jedis实现Redis事务 https://blog.csdn.net/weixin_43246215/article/details/108067542
6 Redis的配置文件详解(中文) https://blog.csdn.net/weixin_43246215/article/details/108068245
7 Redis数据持久化的两种方式以及Redis实现订阅发布 https://blog.csdn.net/weixin_43246215/article/details/108068797
8 Redis的伪集群搭建以及主从复制原理 https://blog.csdn.net/weixin_43246215/article/details/108069472
9 Redis中哨兵(Sentinel)模式的使用以及相关配置介绍 https://blog.csdn.net/weixin_43246215/article/details/108088179
10 Redis中的缓存穿透、缓存击穿以及缓存雪崩(理论知识) https://blog.csdn.net/weixin_43246215/article/details/108089026
11 Spring整合Redis实现查询缓存以及同步缓存 待更新
12 SpringBoot整合Redis以及自定义Redis Template https://blog.csdn.net/weixin_43246215/article/details/108476328
13 Redis常见面试题 https://blog.csdn.net/weixin_43246215/article/details/108090095
14 待更新

简介

本次测试的环境为:
Linux:centos 6.5
Redis:redis-5.0.7.tar.gz

Redis 的事务管理

1、什么是事务?

  • 事务的本质
    一组命令操作,要么全部执行成功,要么撤销不执行。

redis的事务中,一次执行多条命令,本质是一组命令的集合,一个事务中所有的命令将被序列化,即按顺序执行而不会被其他命令插入

在redis中,事务的作用就是在一个队列中一次性、顺序性、排他性的执行一系列的命令。

一个事务中的所有命令都会被序列化,在事务执行的过程中,会按照顺序执行。
由于命令会在队列中一次性执行(一次性),并且会按照顺序执行(顺序性),事务在执行的过程中是不允许被别人干扰的(排他性),以此来执行一系列的命令。
在这里插入图片描述

2、Redis中的事务注意事项

  • Redis单条命令是保持原子性的,但是Redis事务是不保持原子性的。
  • Redis事务没有隔离级别的概念。
  • 所有命令在事务中,并没有直接被执行,只有发起执行命令的时候才会执行。Exec

3、Redis的事务操作

Redis的事务操作步骤如下所示:

  • (1)开启事务:multi
  • (2)命令入队:…
  • (3)执行事务:exec

4、正常执行事务

在这里插入图片描述
首先连接Redis,当使用multi命令时,Redis就开启了事务操作,并且之后的set、get等命令操作都会依次进入队列当中,此时并不会执行,当执行exec命令的时候,队列中的命令才会依次执行。

注意:事务执行完毕后,队列中的命令全部消失(一次性),如果还需要执行其它事务,就需要重新再开启事务写入命令去执行。

5、放弃事务(discard)

如果在命令入列期间,因为一系列原因想放弃本次事务操作,可以使用discard命令进行事务的取消,当事务取消时,事务队列中的命令都不会被执行。如下图所示:
在这里插入图片描述
使用multi开启事务,并且将两个set命令写入队列,但是执行了discard命令后,本次事务取消,队列中的命令会被清空不执行。因此后面的get操作获取不到值。

6、编译型异常(代码有问题,命令有错),事务中的所有命令都不会被执行。

在这里插入图片描述
如图所示:当开启事务之后,在进行写入命令入队列时,写入的命令出现格式错误或者无此命令等等问题,会出现编译时异常,此时事务队列中的所有命令都不会被执行

7、运行时异常(1/0)

在这里插入图片描述
如上图所示:开启事务后,将命令依次写入队列,期间出现如上图所示时,语法上没有什么错误,但是运行时会报异常,这种异常不会影响其它命令的执行。运行时异常,表示命令是正确的,但是不能正确运算,所以此条命令会报错,但是不会影响其它命令的执行,其它命令还是可以执行的。

Redis中的事务和MySQL中的事务不一样,
MySQL中的事务要么全部执行成功,要么全部执行失败。
MySQL的事务是保证原子性操作的,但是Redis的事务是不保证原子性操作的

8、总结redis事务的三条性质:

  • 单独的隔离操作:事务中的所有命令会被序列化、按顺序执行,在执行的过程中不会被其他客户端发送来的命令打断
  • 没有隔离级别的概念:队列中的命令在事务没有被提交之前不会被实际执行
  • 不保证原子性:redis中的一个事务中如果存在命令执行失败,那么其他命令依然会被执行,没有回滚机制

Redis实现乐观锁(Watch监控)

1、乐观锁和悲观锁的概念

  • 悲观锁:顾名思义,就是比较悲观的锁,总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。Java中synchronized和ReentrantLock等独占锁就是悲观锁思想的实现。

  • 乐观锁:总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制version和CAS算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。

Redis的监控机制,一旦事务执行成功,监控就会自动取消掉。

2、Redis的监控测试

正常执行成功

在这里插入图片描述

测试多线程修改值,监视失败

  • 第一步:启动两个客户端:
    在这里插入图片描述
  • 第二步:客户端A开启事务进程部分操作,还没进行提交时,客户端B就对变量money进行操作了。
    客户端A操作:
    在这里插入图片描述
    客户端B在A事务未提交之前操作如下:
    在这里插入图片描述
  • 第三步:客户端A去提交事务,但是由客户端B修改了money,所以事务执行失败
    在这里插入图片描述
    以上总结:客户端A在未提交事务之前对money和out进行操作的同时,客户端B改变了Money的值,这个时候客户端A去exec提交事务的时候就提交失败。

解决以上问题:如果修改失败,重新获取最新的值就好了

在这里插入图片描述
如果事务执行失败,那么首先执行unwatch命令进行解锁,然后重新获取最新的money,并且再次监控,最后提交时对结果进行对比,如果监视的值发生了变化,那就说明有其它线程正在对money进行修改,那么本次事务执行失败,如果该变量未发生变化,那么事务执行成功。

这里的值变化,指的是,比如说现在money为1000,然后我开启事务操作,写入相应命令入队列,并且执行,而在执行事务之前,也就是执行exec之前money的值发生了变化,那么就说明有其它线程正在修改money,那么事务就执行失败了。

经典面试题

Redis可以实现乐观锁吗?

可以。

Redis的命令是原子性操作,那么Redis的事务也一定是原子性的对不?

不对,Redis的事务不具有原子性。Redis的命令之所以是原子性的,是因为因为Redis是单线程的,线程是操作系统最小的执行单元,在单线程程序中,任务一个一个地做,必须做完一个任务后,才会去做另一个任务。

怎么理解Redis的事务?

  • 事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其它客户端发送来的命令请求所打断。
  • 没有隔离级别的概念:队列中的命令在事务没有被提交之前不会被实际执行。
  • 不保证原子性:redis中的一个事务中如果存在命令执行失败,那么其他命令依然会被执行,没有回滚机制

说一下Redis事务的生命周期

  • 事务的创建:使用MULTI开启一个事务

  • 命令入队列:在开启事务的时候,每次操作的命令将会被插入到一个队列中,同时这个命令并不会被真的执行

  • EXEC命令进行提交事务

Redis中关于事务的命令有哪些?

  • MULTI:使用该命令,标记一个事务块的开始,通常在执行之后会回复OK,(但不一定真的OK),这个时候用户可以输入多个操作来代替逐条操作,redis会将这些操作放入队列中。

  • EXEC:执行这个事务内的所有命令

  • DISCARD:放弃事务,即该事务内的所有命令都将取消

  • WATCH:监控一个或者多个key,如果这些key在提交事务(EXEC)之前被其他用户修改过,那么事务将执行失败,需要重新获取最新数据重头操作(类似于乐观锁)。

  • UNWATCH:取消WATCH命令对多有key的监控,所有监控锁将会被取消。

Redis中的事务为什么没有原子性与watch锁

Redis事务可以一次执行多个命令,它先以 MULTI 开始一个事务, 然后将多个命令入队到事务中, 最后由 EXEC 命令触发事务, 一并执行事务中的所有命令,在Redis中,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做,也因此得出 Redis 事务的执行并不是原子性的。

因为在事务中不能改变被锁的值,所以没有watch锁。

创作不易,如果本文对你有帮助,可以点个赞支持一下哈。

猜你喜欢

转载自blog.csdn.net/weixin_43246215/article/details/108045931