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

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 一组命令操作,要么全部执行成功,要么全部不成功。在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
目录
相关文章
|
1月前
|
NoSQL Redis 数据库
10- 你们用过Redis的事务吗 ? 事务的命令有哪些 ?
```markdown Redis事务包括MULTI、EXEC、DISCARD、WATCH四个命令。虽具备事务功能,但在实际开发中使用较少。 ```
43 7
|
14天前
|
存储 缓存 NoSQL
【Go语言专栏】Go语言中的Redis操作与缓存应用
【4月更文挑战第30天】本文探讨了在Go语言中使用Redis进行操作和缓存应用的方法。文章介绍了Redis作为高性能键值存储系统,用于提升应用性能。推荐使用`go-redis/redis`库,示例代码展示了连接、设置、获取和删除键值对的基本操作。文章还详细阐述了缓存应用的步骤及常见缓存策略,包括缓存穿透、缓存击穿和缓存雪崩的解决方案。利用Redis和合适策略可有效优化应用性能。
|
4天前
|
NoSQL Java Redis
在Java中操作Redis
在Java中操作Redis
7 0
|
5天前
|
监控 NoSQL 关系型数据库
深入浅出Redis(五):Redis的事务机制与ACID原则
深入浅出Redis(五):Redis的事务机制与ACID原则
|
5天前
|
监控 NoSQL 关系型数据库
Redis 事务 与 管道
Redis 事务 与 管道
9 0
|
14天前
|
存储 NoSQL 安全
java 中通过 Lettuce 来操作 Redis
java 中通过 Lettuce 来操作 Redis
java 中通过 Lettuce 来操作 Redis
|
15天前
|
NoSQL 关系型数据库 Redis
数据管理DMS产品使用合集之要通过 DMS 登录到 RDS、DRDS 或 Redis,我该怎么操作
阿里云数据管理DMS提供了全面的数据管理、数据库运维、数据安全、数据迁移与同步等功能,助力企业高效、安全地进行数据库管理和运维工作。以下是DMS产品使用合集的详细介绍。
|
17天前
|
缓存 NoSQL Java
【Redis系列笔记】Redis事务
Redis事务的本质是一组命令的集合。事务支持一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。
55 3
|
20天前
|
JSON NoSQL Java
SpringDataRedis 操作 Redis,并指定数据序列化器
SpringDataRedis 操作 Redis,并指定数据序列化器
18 1
|
1月前
|
监控 NoSQL Java