Redis学习篇:ACID特性和事务处理过程

目录

一、简介Redis事务

二、ACID特性

1、原子性

2、一致性

3、隔离性

4、持久性

三、乐观锁和悲观锁

四、WATCH命令


小白学习Redis第二篇记录,上一篇主要入门认识一下Redis,了解它的数据类型和底层数据结构。详见《入门Redis,从底层数据结构及应用原理开始》。然后这一篇我想记录一下Redis事务这部分,感觉也是比较重要的内容,事务的ACID特性并非Redis特有,Mysql数据库也有,所以这种通用特性我觉得是数据库的核心点,需要去理解原理。

数据库是一个面向多用户的共享管理系统,具备有并发控制和封锁机制,来保证数据库的完整和正常运行。事务就是保证完整性、并发控制和封锁机制的单位,它由一系列数据库命令组成为集合单元。在关系型数据库和非关系型数据库中都存在事务。下面就来看看Redis事务的相关内容。

一、简介Redis事务

Redis事务实现的基本命令:MULTI、EXEC、DISCARD、WATCH

  • MULTI:启动Redis事务,将客户端设置为事务状态。
  • EXEC:提交事务,执行MULTI命令到EXEC之间的命令队列,客户端此时为非事务状态。
  • DISCARD:取消事务,清空事务队列中的所有命令,客户端退出事务状态。
  • WATCH:监视键值对,当所有被监视的键值对没有被修改时,事务才正常执行,否则事务不会被执行。

Redis事务目前保证一个客户端请求的事务中的命令可以被连续的执行,在此过程中不会其他客户端发送过来的命令请求。在接收MULTI命令时候,就开启一个事务上下文,后续的命令都会放到一个队列中,当服务器接收EXEC命令后,这些命令才开始被执行。

二、ACID特性

1、原子性

事务的原子性是指启动事务状态后,队列中的命令被看做一个整体,Redis对这个命令队列要么全部执行成功,要么全部执行失败。

127.0.0.1:6379> set name "xiaochen"
OK
127.0.0.1:6379> set age 20
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set name "charzous"
QUEUED
127.0.0.1:6379> set age 21
QUEUED
127.0.0.1:6379> incrby age 3
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
3) (integer) 24
127.0.0.1:6379> get name
"charzous"
127.0.0.1:6379> get age
"24"
127.0.0.1:6379>

 可看到,不开启事务状态,输入命令执行完毕以“OK”返回;开启multi之后,输入的命令以“QUEUE”返回,表明命令入队。最后,exec命令执行队列中的命令后,以数组方式返回所有执行结果。

接下来验证事务的原子性,特意输入错误的命令格式set (不带key-value)

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set name "test"
QUEUED
127.0.0.1:6379> set age 18
QUEUED
127.0.0.1:6379> set
(error) ERR wrong number of arguments for 'set' command
127.0.0.1:6379> set sex "male"
QUEUED
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379>

(error) EXECABORT Transaction discarded because of previous errors.

返回error信息,Redis在执行队列过程发现前面存在错误的命令输入,所以执行事务失败,查询刚刚的信息确实没有更新和增加。

127.0.0.1:6379> get age
"24"
127.0.0.1:6379> get name
"charzous"
127.0.0.1:6379> get sex
(nil)
127.0.0.1:6379>

这里还存在另一种情况,输入的命令类型错误 ,比如这时对name字段执行加5操作:incrby name 5,则会出现下面情况:

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set name "test"
QUEUED
127.0.0.1:6379> set age 18
QUEUED
127.0.0.1:6379> incrby name 5
QUEUED
127.0.0.1:6379> set sex "male"
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
3) (error) ERR value is not an integer or out of range
4) OK
127.0.0.1:6379>

此时,只对 类型运用错误的命令 执行失败,其他命令不影响。

127.0.0.1:6379> get name
"test"
127.0.0.1:6379> get age
"18"
127.0.0.1:6379> get sex
"male"

2、一致性

事务的一致性是指:数据库在执行事务前后,数据库中的数据是保持一致性的。开始我觉得不太好理解,这里的一致性说是,数据库从当前状态变为一种新的状态,数据在前后依然符合数据本身的定义和要求,不包含非法或无效的脏数据。 字面上的意思应该就是:数据本身的定义就是数据它的类型所具有的特性和对该数据类型的约束不变。

正如上面验证事务的原子性,输入错误命令,事务执行失败,保证了原子性和一致性。

3、隔离性

事务的隔离性是指,多个用户并发访问数据库,数据库会为每个用户单独开启一个事务,它们之间互不干扰,相互隔离,实现了在并发状态下执行事务于串行执行产生的效果完全相同。

因为Redis有自己的特性,采用单进程、单线程模型实现键值对存储,在执行数据库命令时候,都是采用单线程方式,在上一个事务没执行完之前,其他命令不会被执行。

4、持久性

事务的持久性是指,一个事务正确执行后,对数据库的改变是永久的,即使数据库遇到故障,事务执行完成后操作不会消失。

Redis的持久性来源于特有的持久化方式。在Redis中,包括了AOF和RDB持久化方式,异步执行。

两种持久化方式各有优缺点,可以结合优势和场景使用。

三、乐观锁和悲观锁

此部分内容乐观锁和悲观锁正是Redis事务中使用到的读写数据机制,WATCH命令则用到该原理。

  • 乐观锁

顾名思义,这种锁的性格乐观,每次去数据库读数据都认为别人不会修改这些数据,不会对这些数据加锁。但它也粗中有细,更新数据时候,会判断期间有没有被别人修改过数据。若被修改过,则放弃更新;否则执行更新操作。使用场景:读多写少,提高吞吐量。

实现策略:版本号机制。

数据表中有一个“version”字段,记录数据的版本号。读数据时会被读取,写数据时版本号+1。将手中的版本号和数据表中的进行比较,若高于表中的版本号,则更新数据;否则,说明是过期数据,不进行更新。

  • 悲观锁

同样,顾名思义,这种锁性格悲观,每次去取数据时候会认为被别人修改过,所以每次都会加锁,限制别人的使用。在传统的关系型数据库中,存在大量的悲观锁:行锁、表锁、读锁、写锁。可见效率明显被限制。

四、WATCH命令

事务中的WATCH命令就是一种乐观锁。用于监视事务中的命令,这样就使 EXEC 命令需要有条件地执行。

只有在所有被WATCH命令监视 的键没有被修改过的前提下,事务才执行成功。否则,服务器拒绝执行,向客户端返回执行失败的空回复。

实现策略:

被监视的键在执行数据库相关命令时候,会调用touchWatchKey函数,打开监视被修改数据库键的客户端REDIS_DIRTY_CAS标识,当服务器接收EXEC命令后,会判断客户端是否打开这个标识,来决定是否执行这个事务。

验证WATCH命令的作用。

加油表情图

如果觉得不错欢迎“一键三连”哦,点赞收藏关注,有问题直接评论,交流学习! 


我的CSDN博客:https://blog.csdn.net/Charzous/article/details/114546348

猜你喜欢

转载自blog.csdn.net/Charzous/article/details/114922587