详解事务模式和 Lua 脚本,带你吃透 Redis 事务

news/2024/5/17 12:04:23/文章来源:https://blog.csdn.net/crg18438610577/article/details/130520161

先说结论:

Redis 的事务模式具备如下特点:

  • 保证隔离性;
  • 无法保证持久性;
  • 具备了一定的原子性,但不支持回滚;
  • 一致性的概念有分歧,假设在一致性的核心是约束的语意下,Redis 的事务可以保证一致性。

但 Lua 脚本更具备实用场景,它是另一种形式的事务,他具备一定的原子性,但脚本报错的情况下,事务并不会回滚。Lua 脚本可以保证隔离性,而且可以完美的支持后面的步骤依赖前面步骤的结果

Lua 脚本模式的身影几乎无处不在,比如分布式锁、延迟队列、抢红包等场景。

1 事务原理

Redis 的事务包含如下命令:

事务包含三个阶段:

  1. 事务开启,使用 MULTI , 该命令标志着执行该命令的客户端从非事务状态切换至事务状态 ;
  2. 命令入队,MULTI 开启事务之后,客户端的命令并不会被立即执行,而是放入一个事务队列 ;
  3. 执行事务或者丢弃。如果收到 EXEC 的命令,事务队列里的命令将会被执行 ,如果是 DISCARD 则事务被丢弃。

下面展示一个事务的例子。

 redis> MULTI OKredis> SET msg "hello world"QUEUEDredis> GET msgQUEUEDredis> EXEC1) OK1) hello world

这里有一个疑问?在开启事务的时候,Redis key 可以被修改吗?

在事务执行 EXEC 命令之前 ,Redis key 依然可以被修改

在事务开启之前,我们可以 watch 命令监听 Redis key 。在事务执行之前,我们修改 key 值 ,事务执行失败,返回 nil 。

通过上面的例子,watch 命令可以实现类似乐观锁的效果 。

2 事务的 ACID

2.1 原子性

原子性是指:一个事务中的所有操作,或者全部完成,或者全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行过一样。

第一个例子:

在执行 EXEC 命令前,客户端发送的操作命令错误,比如:语法错误或者使用了不存在的命令。

 redis> MULTIOKredis> SET msg "other msg"QUEUEDredis> wrongcommand  ### 故意写错误的命令(error) ERR unknown command 'wrongcommand' redis> EXEC(error) EXECABORT Transaction discarded because of previous errors.redis> GET msg"hello world"

在这个例子中,我们使用了不存在的命令,导致入队失败,整个事务都将无法执行 。

第二个例子:

事务操作入队时,命令和操作的数据类型不匹配 ,入队列正常,但执行 EXEC 命令异常 。

 redis> MULTI OKredis> SET msg "other msg"QUEUEDredis> SET mystring "I am a string"QUEUEDredis> HMSET mystring name  "test"QUEUEDredis> SET msg "after"QUEUEDredis> EXEC1) OK2) OK3) (error) WRONGTYPE Operation against a key holding the wrong kind of value4) OKredis> GET msg"after"

这个例子里,Redis 在执行 EXEC 命令时,如果出现了错误,Redis 不会终止其它命令的执行,事务也不会因为某个命令执行失败而回滚 。

综上,我对 Redis 事务原子性的理解如下:

  1. 命令入队时报错, 会放弃事务执行,保证原子性;
  2. 命令入队时正常,执行 EXEC 命令后报错,不保证原子性;

也就是:Redis 事务在特定条件下,才具备一定的原子性 。

2.2 隔离性

数据库的隔离性是指:数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。

事务隔离分为不同级别 ,分别是:

  • 未提交读(read uncommitted)
  • 提交读(read committed)
  • 可重复读(repeatable read)
  • 串行化(serializable)

首先,需要明确一点:Redis 并没有事务隔离级别的概念。这里我们讨论 Redis 的隔离性是指:并发场景下,事务之间是否可以做到互不干扰

我们可以将事务执行可以分为 EXEC 命令执行前和 EXEC 命令执行后两个阶段,分开讨论。

  • EXEC 命令执行前

在事务原理这一小节,我们发现在事务执行之前 ,Redis key 依然可以被修改。此时,可以使用 WATCH 机制来实现乐观锁的效果。

  • EXEC 命令执行后

因为 Redis 是单线程执行操作命令, EXEC 命令执行后,Redis 会保证命令队列中的所有命令执行完 。 这样就可以保证事务的隔离性。

2.3 持久性

数据库的持久性是指 :事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

Redis 的数据是否持久化取决于 Redis 的持久化配置模式 。

  1. 没有配置 RDB 或者 AOF ,事务的持久性无法保证;
  2. 使用了 RDB 模式,在一个事务执行后,下一次的 RDB 快照还未执行前,如果发生了实例宕机,事务的持久性同样无法保证;
  3. 使用了 AOF 模式;AOF 模式的三种配置选项 no 、everysec 都会存在数据丢失的情况 。always 可以保证事务的持久性,但因为性能太差,在生产环境一般不推荐使用。

综上,redis 事务的持久性是无法保证的 。

2.4 一致性

一致性的概念一直很让人困惑,在我搜寻的资料里,有两类不同的定义。

  • 维基百科

我们先看下维基百科上一致性的定义:

Consistency ensures that a transaction can only bring the database from one valid state to another, maintaining database invariants: any data written to the database must be valid according to all defined rules, including constraints, cascades, triggers, and any combination thereof. This prevents database corruption by an illegal transaction, but does not guarantee that a transaction is correct. Referential integrity guarantees the primary key – foreign key relationship.

在这段文字里,一致性的核心是 “约束”,“any data written to the database must be valid according to all defined rules ”。

如何理解约束?这里引用知乎问题 如何理解数据库的内部一致性和外部一致性,蚂蚁金服 OceanBase 研发专家韩富晟回答的一段话:

“约束” 由数据库的使用者告诉数据库,使用者要求数据一定符合这样或者那样的约束。当数据发生修改时,数据库会检查数据是否还符合约束条件,如果约束条件不再被满足,那么修改操作不会发生。

关系数据库最常见的两类约束是 “唯一性约束” 和 “完整性约束”,表格中定义的主键和唯一键都保证了指定的数据项绝不会出现重复,表格之间定义的参照完整性也保证了同一个属性在不同表格中的一致性。

“Consistency in ACID” 是如此的好用,以至于已经融化在大部分使用者的血液里了,使用者会在表格设计的时候自觉的加上需要的约束条件,数据库也会严格的执行这个约束条件。

所以事务的一致性和预先定义的约束有关,保证了约束即保证了一致性

我们细细品一品这句话: This prevents database corruption by an illegal transaction, but does not guarantee that a transaction is correct

写到这里可能大家还是有点模糊,我们举经典转账的案例。

我们开启一个事务,张三和李四账号上的初始余额都是 1000 元,并且余额字段没有任何约束。张三给李四转账 1200 元。张三的余额更新为 -200 , 李四的余额更新为 2200。

从应用层面来看,这个事务明显不合法,因为现实场景中,用户余额不可能小于 0 , 但是它完全遵循数据库的约束,所以从数据库层面来看,这个事务依然保证了一致性。

Redis 的事务一致性是指:Redis 事务在执行过程中符合数据库的约束,没有包含非法或者无效的错误数据。

我们分三种异常场景分别讨论:

  1. 执行 EXEC 命令前,客户端发送的操作命令错误,事务终止,数据保持一致性;
  2. 执行 EXEC 命令后,命令和操作的数据类型不匹配,错误的命令会报错,但事务不会因为错误的命令而终止,而是会继续执行。正确的命令正常执行,错误的命令报错,从这个角度来看,数据也可以保持一致性;
  3. 执行事务的过程中,Redis 服务宕机。这里需要考虑服务配置的持久化模式。
  • 无持久化的内存模式:服务重启之后,数据库没有保持数据,因此数据都是保持一致性的;
  • RDB / AOF 模式: 服务重启后,Redis 通过 RDB / AOF 文件恢复数据,数据库会还原到一致的状态。

综上所述,在一致性的核心是约束的语意下,Redis 的事务可以保证一致性

  • 《设计数据密集型应用》

这本书是分布式系统入门的神书。在事务这一章节有一段关于 ACID 的解释:

Atomicity, isolation, and durability are properties of the database,whereas consistency (in the ACID sense) is a property of the application. The application may rely on the database’s atomicity and isolation properties in order to achieve consistency, but it’s not up to the database alone. Thus, the letter C doesn’t really belong in ACID.

原子性,隔离性和持久性是数据库的属性,而一致性(在 ACID 意义上)是应用程序的属性。应用可能依赖数据库的原子性和隔离属性来实现一致性,但这并不仅取决于数据库。因此,字母 C 不属于 ACID 。

很多时候,我们一直在纠结的一致性,其实就是指符合现实世界的一致性,现实世界的一致性才是事务追求的最终目标。

为了实现现实世界的一致性,需要满足如下几点:

  1. 保证原子性,持久性和隔离性,如果这些特征都无法保证,那么事务的一致性也无法保证;
  2. 数据库本身的约束,比如字符串长度不能超过列的限制或者唯一性约束;
  3. 业务层面同样需要进行保障 。

2.5 事务特点

我们通常称 Redis 为内存数据库,不同于传统的关系数据库,为了提供了更高的性能,更快的写入速度,在设计和实现层面做了一些平衡,并不能完全支持事务的 ACID。

Redis 的事务具备如下特点:

  • 保证隔离性;
  • 无法保证持久性;
  • 具备了一定的原子性,但不支持回滚;
  • 一致性的概念有分歧,假设在一致性的核心是约束的语意下,Redis 的事务可以保证一致性。

从工程角度来看,假设事务操作中每个步骤需要依赖上一个步骤返回的结果,则需要通过 watch 来实现乐观锁 。

3 Lua 脚本

3.1 简介

Lua 由标准 C 编写而成,代码简洁优美,几乎在所有操作系统和平台上都可以编译,运行。Lua 脚本可以很容易的被 C/C ++ 代码调用,也可以反过来调用 C/C++ 的函数,这使得 Lua 在应用程序中可以被广泛应用。

Lua 脚本在游戏领域大放异彩,大家耳熟能详的《大话西游 II》,《魔兽世界》都大量使用 Lua 脚本。Java 后端工程师接触过的 api 网关,比如 Openresty ,Kong 都可以看到 Lua 脚本的身影。

从 Redis 2.6.0 版本开始, Redis 内置的 Lua 解释器,可以实现在 Redis 中运行 Lua 脚本。

使用 Lua 脚本的好处 :

  • 减少网络开销。将多个请求通过脚本的形式一次发送,减少网络时延。
  • 原子操作。Redis 会将整个脚本作为一个整体执行,中间不会被其他命令插入。
  • 复用。客户端发送的脚本会永久存在 Redis 中,其他客户端可以复用这一脚本而不需要使用代码完成相同的逻辑。

Redis Lua 脚本常用命令:

3.2 EVAL 命令

命令格式:

 EVAL script numkeys key [key ...] arg [arg ...]

说明:

  • script 是第一个参数,为 Lua 5.1 脚本;
  • 第二个参数 numkeys 指定后续参数有几个 key;
  • key [key ...],是要操作的键,可以指定多个,在 Lua 脚本中通过 KEYS [1], KEYS [2] 获取;
  • arg [arg ...],参数,在 Lua 脚本中通过 ARGV [1], ARGV [2] 获取。

简单实例:

 redis> eval "return ARGV[1]" 0 100 "100"redis> eval "return {ARGV[1],ARGV[2]}" 0 100 1011) "100"2) "101"redis> eval "return {KEYS[1],KEYS[2],ARGV[1]}" 2 key1 key2 first second1) "key1"2) "key2"3) "first"4) "second"

下面演示下 Lua 如何调用 Redis 命令 ,通过 redis.call () 来执行了 Redis 命令 。

 redis> set mystring 'hello world'OKredis> get mystring"hello world"redis> EVAL "return redis.call('GET',KEYS[1])" 1 mystring"hello world"redis> EVAL "return redis.call('GET','mystring')" 0

"hello world"

3.3 EVALSHA 命令

使用 EVAL 命令每次请求都需要传输 Lua 脚本 ,若 Lua 脚本过长,不仅会消耗网络带宽,而且也会对 Redis 的性能造成一定的影响。

思路是先将 Lua 脚本先缓存起来,返回给客户端 Lua 脚本的 sha1 摘要。 客户端存储脚本的 sha1 摘要 ,每次请求执行 EVALSHA 命令即可。

EVALSHA 命令基本语法如下:

 redis> EVALSHA sha1 numkeys key [key ...] arg [arg ...] 

实例如下:

 redis> SCRIPT LOAD "return 'hello world'""5332031c6b470dc5a0dd9b4bf2030dea6d65de91"redis> EVALSHA 5332031c6b470dc5a0dd9b4bf2030dea6d65de91 0"hello world"

4 事务 VS Lua 脚本

从定义上来说, Redis 中的脚本本身就是一种事务, 所以任何在事务里可以完成的事, 在脚本里面也能完成。 并且一般来说, 使用脚本要来得更简单,并且速度更快

因为脚本功能是 Redis 2.6 才引入的, 而事务功能则更早之前就存在了, 所以 Redis 才会同时存在两种处理事务的方法。

不过我们并不打算在短时间内就移除事务功能, 因为事务提供了一种即使不使用脚本, 也可以避免竞争条件的方法, 而且事务本身的实现并不复杂。

-- https://redis.io/

Lua 脚本是另一种形式的事务,他具备一定的原子性,但脚本报错的情况下,事务并不会回滚。Lua 脚本可以保证隔离性,而且可以完美的支持后面的步骤依赖前面步骤的结果

Lua 脚本模式的身影几乎无处不在,比如分布式锁、延迟队列、抢红包等场景。

不过在编写 Lua 脚本时,要注意如下两点:

  1. 为了避免 Redis 阻塞,Lua 脚本业务逻辑不能过于复杂和耗时;
  2. 仔细检查和测试 Lua 脚本 ,因为执行 Lua 脚本具备一定的原子性,不支持回滚。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_296498.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

GUI编程(一)

1、简介 GUI的核心技术:Swing、 AWT 1、外观不太美观,组件数量偏少 2、运行需要JRE环境 为什么我们要学习? 组件(JTable,JList等)很多都是MVC的经典示范,学习也可以了解mvc架构。工作时,也有可能遇见需要维护N年前awt/swing写的…

港联证券|4连板的AI+传媒概念股火了,近5亿资金抢筹

今天,沪深两市共51股涨停,除掉10只ST股,合计41股涨停。别的,11股封板未遂,全体封板率为81%。 涨停战场:长江传媒封单量最高 从收盘涨停板封单量来看,长江传媒封单量最高,有39.96万手…

Linux 内存管理 pt.2

哈喽大家好我是咸鱼,在《Linux 内存管理 pt.1》中我们学习了什么是物理内存、虚拟内存,了解了内存映射、缺页异常等内容 那么今天我们来接着学习 Linux 内存管理中的多级页表和大页 多级页表&大页 在《Linux 内存管理 pt.1》中我们知道了内核为每…

【linux的学习与软件安装】

文章目录 linux的学习一、工具安装与联网?二、Linux软件安装1.安装jdk2.安装MySQL安装redis linux的学习 一、工具安装与联网? 1.1安装好VM后 进入vi /etc/sysconfig/network-scripts/ifcfg-ens33 然后ip addr 查看ip 1.2打开IDEA的tools 二、Linux软…

uniapp - 实现微信小程序电子签名板,横屏手写姓名签名专用写字画板(详细运行示例,一键复制开箱即用)

效果图 实现了在uniapp项目中,微信小程序平台流畅的写字签名板(也可以绘图)功能源码,复制粘贴,改改样式几分钟即可搞定! 支持自动横屏、持预览,真机运行测试非常流畅不卡顿。 基础模板 如下代码所示。 <template><view class=

Shell脚本2

自定义局部变量 :定义在一个脚本文件中的变量 只能在这个脚本文件中使用的变量&#xff0c;局部变量 语法&#xff1a; var_namevalue 变量定义规则 变量名称可以有字母,数字和下划线组成, 但是不能以数字开头 等号两侧不能有空格 在bash环境中, 变量的默认类型都是字符串…

Softing线上研讨会 | 轻松访问XML文件中的过程数据

| 线上研讨会时间&#xff1a;2023年5月8日下午4点或晚上10点 对于传统车间的系统应用和创新的物联网解决方案而言&#xff0c;高效访问机器和流程数据至关重要。而在现有工厂中&#xff0c;过程数据通常以XML文件的形式出现。对此&#xff0c;Softing Industrial提供了一个用…

【华为OD机试 2023最新 】箱子之字形摆放(C语言题解 100%)

文章目录 题目描述输入描述输出描述备注用例题目解析C语言题目描述 有一批箱子(形式为字符串,设为str), 要求将这批箱子按从上到下以之字形的顺序摆放在宽度为 n 的空地,请输出箱子的摆放位置。 例如:箱子ABCDEFG,空地宽度为3,摆放结果如图: 则输出结果为: AFG BE C…

2023年房地产抵押贷款研究报告

第一章 概述 房地产抵押贷款是一种以房地产为抵押品的贷款形式&#xff0c;包括个人和企业两种情况。个人房地产抵押贷款是指个人将名下房产作为抵押品向银行或其他金融机构申请贷款&#xff0c;而企业房地产抵押贷款则是指企业将自己名下的商业房产作为抵押品向金融机构申请贷…

2-Lampiao百个靶机渗透(精写-思路为主)框架漏洞利用2

特别注明&#xff1a;本文章只用于学习交流&#xff0c;不可用来从事违法犯罪活动&#xff0c;如使用者用来从事违法犯罪行为&#xff0c;一切与作者无关。 文章目录 前言一、环境重新部署二、AWVSxray联动和xraybs联动1.安装AWVSxray2.让xray和bs先联动3.AWVS和xray联动 三、p…

多城市门店店铺展示地图导航pc/h5系统开发

多城市门店店铺展示地图导航pc/h5系统开发 系统设置&#xff1a; 网站标题、网站副标题、Logo图、网站背景图、网站底部图、网站底部版权、网站ICP备案、腾讯地图Key。 店铺列表&#xff1a; 店铺名称、店铺图标、设备、电话、省市区、详细地址。 添加店铺&#xff1a; 店铺…

Kubernetes服务搭建[配置-部署](Kubeadm)

文章目录 **[1 — 7] ** [ 配置K8S主从集群前置准备操作 ]一&#xff1a;主节点操作 查看主机域名->编辑域名1.1 编辑HOST 从节点也做相应操作1.2 从节点操作 查看从节点102域名->编辑域名1.3 从节点操作 查看从节点103域名->编辑域名 二&#xff1a;安装自动填充&…

【Linux】usb游戏手柄测试、编程

1、简述 在ubuntu18.04下使用usb游戏手柄,之前联系客服,客服回答不清楚是否支持linux,因此采购一款北通蝙蝠2的手柄来测试 2、测试 2.1 测试环境 系统:Ubuntu18.04 正常电脑系统ubuntu中都是自带手柄驱动的joystick,即内核配置已添加选项:Joysticks interface和Joys…

看我如何通过帮助服务台轻松黑掉数百家公司

导语&#xff1a;几个月前&#xff0c;我发现黑客可以利用一个漏洞访问目标公司的内部通信。 这个漏洞只需要点击几下&#xff0c;就可以访问企业内部网络、 Twitter等社交媒体账户&#xff0c;以及最常见的Yammer和Slack团队。 更新: The Next Web 写了一篇我发现的这个漏洞的…

读书笔记——《2001太空漫游》

阿瑟克拉克神作&#xff0c;任何一个科幻迷都绕不开的一部作品。很早就听说过其大名&#xff0c;因为之前看过电影版的&#xff0c;总感觉少了点新鲜感&#xff0c;这本书就一直在书架上没有拿出来看。但是看过这本书后&#xff0c;我可以很负责任的说&#xff0c;全书都充满新…

【Android入门到项目实战-- 9.1】—— 传感器的使用教程

目录 传感器的定义 三大类型传感器 1、运动传感器 2、环境传感器 3、位置传感器 传感器开发框架 1、SensorManager 2、Sensor 3、SensorEvent 4、SensorEventListener 一、使用传感器开发步骤 1、获取传感器信息 1)、获取传感器管理器 2)、获取设备的传感器对象列…

Oracle数据库、实例、用户、表空间、表之间的关系

数据库&#xff1a; Oracle数据库是数据的物理存储。这就包括&#xff08;数据文件ORA或者DBF、控制文件、联机日志、参数文件&#xff09;。其实Oracle数据库的概念和其它数据库不一样&#xff0c;这里的数据库是一个操作系统只有一个库。可以看作是Oracle就只有一个大数据库。…

如何利用问卷工具助力活动开展,实现高效数据收集?

问卷调查是一种常用的活动开展方式&#xff0c;它可以帮助我们更好地了解参与者的需求和意见&#xff0c;为活动的开展提供有力的参考和依据。 1、了解期望和需求&#xff1a;在活动中&#xff0c;我们可以事先通过问卷调查了解参与者的需求、意见、对活动的需求和期望&#x…

2023年6月DAMA-CDGA/CDGP数据治理认证报名请尽早啦!

6月18日DAMA-CDGA/CDGP数据治理认证考试开放报名中&#xff01; 考试开放地区&#xff1a;北京、上海、广州、深圳、长沙、呼和浩特、杭州、南京、济南、成都、西安。其他地区凑人数中… DAMA-CDGA/CDGP数据治理认证开班时间&#xff1a;5月7日 DAMA认证为数据管理专业人士提供…

嵌入式设备逆向所需的工具链

导语&#xff1a;本文介绍了嵌入式设备逆向所需的工具链。 相关的应用程序或工具有&#xff1a; UART(Universal Asynchronous Receiver Transmitter&#xff0c;通用异步收发器)&#xff1a; UBoot&#xff1b; Depthcharge&#xff1b; SPI (Serial Peripheral Interface…