Redis中的事务

Redis中的事务

参考

参考博客:https://blog.csdn.net/jiayi_yao/article/details/124689937

菜鸟教程:https://www.runoob.com/redis/redis-transactions.html

参考博客:https://blog.csdn.net/weixin_46156200/article/details/121043694?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1-121043694-blog-124689937.pc_relevant_aa&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1-121043694-blog-124689937.pc_relevant_aa&utm_relevant_index=1

参考书籍:《Redis设计与实现》

Redis事务基本概念

Redis的事务是什么?

Redis事务是指将多条命令加入队列,一次批量执行多条命令,每条命令会按顺序执行,事务执行过程中不会受客户端传入的命令请求影响。

一个事务从开始到执行会经历以下三个阶段:

  1. 第一阶段:开始事务(其实就是修改Redis客户端中关于事务状态的flag)
  2. 第二阶段:命令入队(事务状态下,非WATCH、EXEC、DISCARD、MULTI都不会执行,而是加入事务队列)
  3. 第三阶段、执行事务。(给出EXEC指令)

EXEC:创建回复队列,依次遍历事务队列,取得结果放入回复队列,执行完毕后,清空事务队列,修改Redis客户端状态为非事务状态(非事务状态,接到命令就会立即执行),然后将回复队列返回给前端

  1. 第三阶段、执行事务。

Redis事务的相关命令如下:

MULTI:标识一个事务的开启,即开启事务;
EXEC:执行事务中的所有命令,即提交;
DISCARD:放弃事务;和回滚不一样,Redis事务不支持回滚。
WATCH:监视Key改变,用于实现乐观锁。如果监视的Key的值改变,事务最终会执行失败。
UNWATCH:放弃监视。

使用WATCH实现乐观锁

说到乐观锁,就和悲观锁一起简单说说对其的理解:

乐观锁:就是非常乐观,做什么事都往好处想; 对于数据库操作,就认为每次操作数据的时候都认为别的操作不会修改,所以不会加锁,而是通过一个类似于版本的字段来标识该数据是否修改过,在执行本次操作前先判断是否修改过,如果修改过就放弃本次操作重新再来。乐观锁适用于多读的应用类型,这样可以提高吞吐量;乐观锁策略:提交版本必须大于记录当前版本才能执行更新。

悲观锁:就是非常悲观,做什么事都觉得不好;对于数据库操作,每次操作数据数据都会认为别的操作会修改当前数据,所以都要对其进行加锁,类似于表锁和行锁。

WATCH通过监视指定Redis Key,如果没有改变,就执行成功,如果发现对应值发生改变,事务就会执行失败。

(WATCH命令一般在开启事务之前使用)

那会一直监视指定的Key吗?,答案是不会的,以下三种方式可以取消监视:

  • 事务执行之后,不管是否执行成功还好是失败,都会取消对应的监视;
  • 当监视的客户端断开连接时,也会取消监视;
  • 可以手动UNWATCH取
  • 消所有Key的监视;

WATCH如何实现?

每个Redis数据库都保存着一个watched_keys字典,这个字典的键是某个被WATCH命令监视的数据库键,字典的值是一个链表,链表中记录了所有监视相应数据库键的客户端。

通过watched_keys字典,服务器可以清楚地知道,那些数据库键正在被监视,以及那些客户端在监视。

监视机制的触发

所有对数据库修改的命令,例如SET、LPUSH、SADD等,在执行之后都会调用multi.c/touchWatchKey函数对watched_keys字典进行检查,查看是否有客户端正在监视刚刚修改的数据库键,如果有,那么touchWatchKey函数会将监视被修改的客户端的REDIS_DIRTY_CAS表示打开,表示该客户端的数据安全性已经被破坏。

判断事务是否安全

当客户端接收到一个客户端发来的EXEC命令时,服务器会根据这个客户端是否打开了REDIS_DIRTY_CAS来决定是否会执行该事务:

  • 如果客户端的REDIS_DIRTY_CAS表示已经被打开,那么说明这个客户端监视的键中,至少有一个已经被修改了,这种情况下,客户端提交的事务已经不安全了,所以服务器会拒绝执行客户端提交的事务
  • 如果客户端的REDIS_DIRTY_CAS表示没有被打开,说明客户端监视的所有的键都没有被修改(或者这个事务没有监视任何键),事务仍然是安全的,服务器将执行客户端提交的这个事务

事务的ACID性质

在Redis中,事务总是具有原子性,一致性,隔离性,在某些特定的持久化模式下,事务也具有持久性。

原子性

事务具有原子性是指得是,数据库事务中的一个操作会当作一个整体来执行,服务器要么所有操作都执行,要么都不执行。

对于Redis事务来说,事务中的命令要么全部执行,要么全不执行,因此Redis事务是具有一定的原子性的。Redis事务和传统事务最大的区别是,Redis不支持事务回滚机制,即使事务队列中有一个操作执行中发生错误,整个事务还会继续执行,直到所有事务执行完毕,并且之前执行的命令也不会受到影响。

Redis事务中没有提供回滚的支持,官方提供了理由:

不支持事务回滚是因为这种复杂的功能和Redis追求的简单高效不相符,他认为Redis事务执行错误,通常都是编程错误产生的,这种错误通常只会出现在开发环境,而很少会出现在实际的生产环境,没有必要增加回滚功能。

一致性

事务具有一致性指的是,如果数据库在执行事务前是一致的,那么执行事务后,无论成功与否,也应该是一致的。“一致”指的是数据符合数据库本身的定义和要求,没有包含非法或者无效的错误数据。

Redis通过谨慎的错误检测和简单的设计来保证事务的一致性。之后将介绍几个事务可能出错的地方,来说明Redis如何保证事务的一致性。

入队错误

如果一个事务在入队命令的时候,出现了命令不存在或者命令格式不存在等请况,那么Redis将拒绝执行这个事务。因为服务器拒绝执行入队过程中出现错误的事务,所以Redis事务一致性不会被有入队错误的事务影响。

执行错误

执行时错误,就是那种无法在入队时被服务器发现的错误,这些问题只会实际使用的时候触发。即使事务在执行的过程中发生了错误,服务器也不会中断事务地执行,它会继续执行剩下的命令,并且已执行的命令不会被出错的命令影响。

因为在事务的执行过程中,出错的命令会被服务器识别出来,并进行相应的错误处理,所以这些出错的命令不会对数据库做任何修改,也不会对事务的一致性产生影响。

服务器宕机

如果Redis执行过程中宕机,根据服务器使用的持久化模式,可能出现以下情况:

  • 如果服务器运行在无持久化的内存模式下,那么重启之后的数据库是空白的,因此数据总是一致
  • 如果服务运行在RDB模式下,那么事务中途停机不会导致不一致性,因为服务器可以根据现有的RDB文件来恢复数据,并且事务执行过程中不会进行RDB,所以恢复后的数据库能到达一个一致的状态。如果找不到可用的RDB文件,那么数据库就是空白的,空白的数据库总是一致的。
  • 如果服务器运行在AOF模式下,那么在事务中途不会导致不一致性,因为服务器可以根据AOF文件来恢复数据,从而将数据库库恢复到一个一致的状态,如果有部分数据写入到了AOF,我们可以通过redis-check-aof清除已完成的一些操作,来保证一致性。

综上,服务器无论在哪种持久化方式下,事务中途停止都不会影响一致性。

隔离性

事务的隔离性指的是,即使数据库中有多个事务并发地执行,各个事务之间也不会相互影响,并且在并发状态下执行的事务和串行下的结果完全相同。

因为Redis是单线程来执行,并且服务器抱枕,执行事务期间,不会对事务进行中断,因此Redis事务总是以串行的方式运行,并且事务也总是具有隔离性。

持久性

事物的持久性是指,当事务执行完毕,执行这个事务所得的结果都保存到非易失存储器上,即使服务器停机,执行事务得到的结果也不会丢失。

因为Redis事务至少用队列包裹了一组Redis命令,Redis并没有为事务提供持久化功能,所以Redis事务的持久性就由Redis采用的持久化方式决定:

  • 服务器无任何持久化方式,事务不具有持久性,服务器停机,数据消失
  • 当服务器采用RDB持久方式时,服务器只会在特定的保存条件被满足时,执行BGSAVE命令,对数据库进行保存操作,异步执行的BGSAVE不能保证事务的结果第一时间写入到硬盘,所以不具备持久性
  • 采用AOF方式,且appendsync=always,每次执行命令,就调用同步函数存入硬盘,所以具有持久性
  • 采用AOF方式,且appendsync=everysync,程序一秒同步一次,所以可能会导致修改丢失,所以不具有持久性
  • 采用AOF方式,且appendsync=no,交给服务器来判断同步时机,所以可能会导致修改丢失,所以不具有持久性

Redis事务优缺点

优点:

一次性按顺序执行多个Redis命令,不受其他客户端命令请求影响;
事务中的命令要么都执行(命令间执行失败互相不影响),要么都不执行(比如中间有命令语法错误);

缺点:

事务执行时,不能保证原子性;
命令入队每次都需要和服务器进行交互,增加带宽(可以使用LUA脚本,一次发送所有的命令,减少带宽);

注意:

当事务中命令语法使用错误时,最终会导致事务执行不成功,即事务内所有命令都不执行;
当事务中命令知识逻辑错误,就比如给字符串做加减乘除操作时,只能在执行过程中发现错误,这种事务执行中失败的命令不影响其他命令的执行。

Redis事务应用场景

Redis事务因为特性的原因,没有什么应用场景,如果大家有了解的应用场景,欢迎补充。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值