Redis中基本事务操作及乐观锁的实现

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 Tair(兼容Redis),内存型 2GB
简介: 一组命令操作,要么全部执行成功,要么全部不成功。在redis中,事务的作用就是在一个队列中一次性、顺序性、排他性的执行一系列的命令。

一、Mysql事务回顾

mysql:ACID四个原则,要么同时成功,要么同时失败,原子性(关系型数据库)
mysql事务是必须满足4个条件(ACID):

原子性(Atomicity,或称不可分割性)、一致性(Consistency)、隔离性(Isolation,又称独立性)、持久性(Durability)。

  • 原子性:一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
  • 一致性:在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。
  • 隔离性:数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。
  • 持久性:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

二、Redis事务管理

1. 什么是事务?

一组命令操作,要么全部执行成功,要么全部不成功。

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

Redis事务本质:一组命令的集合!一个事务中的所有命令都会序列化,在事务执行过程中,会按照顺序执行而不会被其他命令插入

redis中事务具有:一次性,顺序性,排他性!
在这里插入图片描述

2. 事务注意事项

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

3. 事务执行

事务操作步骤:

    1. 开启事务(multi)
    2. 命令入队(其他命令...)
    3. 执行事务(exec)

3.1 正常执行事务

127.0.0.1:6379> multi            #开启事务
OK
#命令入队  所有命令在事务中,并没有直接被执行!只有发起执行命令的时候才会执行
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> exec        #执行事务
1) OK
2) OK
3) "v2"
4) OK

当使用multi命令时,Redis就开启了事务操作,set、get等命令操作都会依次进入队列当中,注意此时并不会执行,当执行exec命令的时候,队列中的命令才会依次执行。当事务执行完毕后,队列中的命令全部消失(一次性),如果还需要执行其它事务,就需要重新再开启事务写入命令去执行。


3.2 放弃事务

如果在命令入列期间,因为某些原因想放弃本次事务操作,可以使用discard命令进行事务的取消,当事务取消时,事务队列中的命令都不会被执行。

127.0.0.1:6379> multi        #开启事务
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> DISCARD        #取消事务 discard
OK
127.0.0.1:6379> get k4        #事务队列中命令都不会执行!
(nil)

当执行了discard命令后,本次事务取消,队列中的命令会被清空不执行。因此get操作获取不到值。


3.3 编译型异常

如代码有问题,命令有错,事务中所有命令都不会被执行

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> getset k3        #命令错误
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> exec    #执行事务报错
(error) EXECABORT Transaction discarded because of previous errors.
#所有命令都不会执行
127.0.0.1:6379> get k4
(nil)
127.0.0.1:6379> get k1
(nil)

上述代码中,由于getset命令错误,导致编译时异常,此次事务中所有命令都不会被执行,因此get命令时,值为空。


3.4 运行时异常(比如1/0)

如果事务队列中存在语法性,那么执行命令的时候,其他命令是可以正常执行的,错误命令抛出异常!

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> INCR k1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> get k3
QUEUED
127.0.0.1:6379> exec
1) (error) ERR value is not an integer or out of range    #虽然第一条命令报错,但依然正常执行成功
2) OK
3) OK
4) "v3"
127.0.0.1:6379> get k2
"v2"

4. Redis事务 VS MySql事务

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


5. 小结

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

三、Redis实现乐观锁

1. 概念

  • 悲观锁:其是基于一种悲观的态度类来防止一切数据冲突,它是以一种预防的姿态在修改数据之前把数据锁住,然后再对数据进行读写,在它释放锁之前任何人都不能对其数据进行操作,直到前面一个人把锁释放后下一个人数据加锁才可对数据进行加锁,然后才可以对数据进行操作
  • 乐观锁:其是对于数据冲突保持一种乐观态度,操作数据时不会对操作的数据进行加锁(这使得多个任务可以并行的对数据进行操作),只有到数据提交的时候才通过一种机制来验证数据是否存在冲突。可以使用版本号机制version和CAS算法实现。

2.Redis监控测试

2.1 正常执行

127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> watch money        监视 money 对象
OK
#事务正常结束,数据期间没有发生变动,这个时候就可以正常执行成功
127.0.0.1:6379> multi
OK
127.0.0.1:6379> DECRBY money 20
QUEUED
127.0.0.1:6379> INCRby out 20
QUEUED
127.0.0.1:6379> exec
1) (integer) 80
2) (integer) 20

2.2多线程修改值

多线程修改值,使用watch可以当做redis的乐观锁操作

127.0.0.1:6379> watch money        #监视 money
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> DECRBY money 10
QUEUED
127.0.0.1:6379> incrby out 10
QUEUED
127.0.0.1:6379> exec        #执行之前,另一个线程修改了money的值,这样会导致事务执行失败
(nil)

在执行本次事务中执行之前,另一个线程将money值进行修改,从而导致本次事务执行失败,
如果修改失败,获取最新的即可。

127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> DECRBY money 10
QUEUED
127.0.0.1:6379> INCRBY out 10
QUEUED
127.0.0.1:6379> exec            #事务执行失败
(nil)
127.0.0.1:6379> unwatch            #解锁
OK
127.0.0.1:6379> watch money        #获取最新的值,再次监视
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 10
QUEUED
127.0.0.1:6379> incrby out 10
QUEUED
127.0.0.1:6379> exec            #对比监视的值是否发送了变化,如果没有变化,那么可以执行成功,如果变化就执行失败
1) (integer) 60
2) (integer) 40

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

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
2月前
|
缓存 NoSQL Redis
Redis 事务
10月更文挑战第18天
28 1
|
4月前
|
负载均衡 NoSQL 算法
一天五道Java面试题----第十天(简述Redis事务实现--------->负载均衡算法、类型)
这篇文章是关于Java面试中Redis相关问题的笔记,包括Redis事务实现、集群方案、主从复制原理、CAP和BASE理论以及负载均衡算法和类型。
一天五道Java面试题----第十天(简述Redis事务实现--------->负载均衡算法、类型)
|
2月前
|
缓存 NoSQL Java
大数据-50 Redis 分布式锁 乐观锁 Watch SETNX Lua Redisson分布式锁 Java实现分布式锁
大数据-50 Redis 分布式锁 乐观锁 Watch SETNX Lua Redisson分布式锁 Java实现分布式锁
69 3
大数据-50 Redis 分布式锁 乐观锁 Watch SETNX Lua Redisson分布式锁 Java实现分布式锁
|
2月前
|
SQL 分布式计算 NoSQL
大数据-42 Redis 功能扩展 发布/订阅模式 事务相关的内容 Redis弱事务
大数据-42 Redis 功能扩展 发布/订阅模式 事务相关的内容 Redis弱事务
28 2
|
2月前
|
NoSQL 关系型数据库 MySQL
Redis 事务特性、原理、具体命令操作全方位诠释 —— 零基础可学习
本文全面阐述了Redis事务的特性、原理、具体命令操作,指出Redis事务具有原子性但不保证一致性、持久性和隔离性,并解释了Redis事务的适用场景和WATCH命令的乐观锁机制。
368 0
Redis 事务特性、原理、具体命令操作全方位诠释 —— 零基础可学习
|
4月前
|
NoSQL 关系型数据库 Redis
Redis6入门到实战------ 九、10. Redis_事务_锁机制_秒杀
这篇文章深入探讨了Redis事务的概念、命令使用、错误处理机制以及乐观锁和悲观锁的应用,并通过WATCH/UNWATCH命令展示了事务中的锁机制。
Redis6入门到实战------ 九、10. Redis_事务_锁机制_秒杀
|
3月前
|
监控 NoSQL 关系型数据库
9)Redis 居然也有事务
9)Redis 居然也有事务
35 0
|
5月前
|
NoSQL Linux Redis
Redis性能优化问题之想确认Redis延迟变大是否因为fork耗时导致的,如何解决
Redis性能优化问题之想确认Redis延迟变大是否因为fork耗时导致的,如何解决
|
6月前
|
缓存 NoSQL Redis
redis管道操作(节省网络IO开销)
pipeline中发送的每个command都会被server立即执行,如果执行失败,将会在此后的响应中得到信息;也就是pipeline并不是表达“所有command都一起成功”的语义,管道中前面命令失败,后面命令不会有影响,继续执行。
58 1
|
5月前
|
监控 NoSQL Redis
Redis事务和Redis管道
Redis事务和Redis管道
66 0
下一篇
DataWorks