【Redis13】Redis基础:事务与Lua脚本命令

Redis基础学习:事务与Lua脚本命令

对于一个传统关系型数据库系统来说,事务是非常重要的一个组成部分。但是,在 NoSQL 相关的数据库中,为了效率以及实现形式的不同,事务远达不到真正的关系型数据库中的那种 ACID 的控制级别。今天,我们就来学习一下 Redis 中的事务操作。另外,我们还会简单地看一下在 Redis 中如何去执行 Lua 脚本程序。

事务

在 Redis 中,事务其实就是先使用 MULTI 开启事务操作,然后把每次执行的命令放到一个队列中,当执行 EXEC 命令后才会真正的执行队列中的所有命令。

// 客户端1
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> set a 111
QUEUED
127.0.0.1:6379(TX)> incr a
QUEUED

// 客户端2
127.0.0.1:6379> get a
(nil)

// 客户端1
127.0.0.1:6379(TX)> exec
OK
112

// 客户端2
127.0.0.1:6379> get a
"112"

我们在第一个客户端开启了事务,然后设置一个 a 的值为 111 ,并且 INCR 一下,也就是加 1 。此时还没有执行 EXEC 命令,然后在客户端2查询 a ,会发现 a 还是空的。接着我们在客户端1执行 EXEC 后,a 数据正式写入并且加1,所有的客户端都可以访问到了。在执行事务的时候,命令行会很明显的显示一个 TX 标识。

既然是事务,如果不想执行了,是不是可以回滚?不不不,在 Redis 中没有回滚一说,因为命令只是在队列中,并没有真正被执行,所以自然也不能叫做回滚,而是叫做丢弃 DISCARD 。

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> set a 111
QUEUED
127.0.0.1:6379(TX)> DISCARD
OK

127.0.0.1:6379> get a
"112"

上面的例子中,我们在事务中尝试重新将 a 的值设置为 111 ,但最后选择了 DISCARD ,这样后面再访问的时候 a 的值就不会发生变化。

好了,接下来我们测试一下 Redis 事务的原子性和隔离性。先看看隔离性,也就是一个事务在执行的时候会不会受到外部的干扰。

// 客户端1
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> incr a
QUEUED

// 客户端2
127.0.0.1:6379> set a 456
OK

// 客户端1
127.0.0.1:6379(TX)> EXEC
457
127.0.0.1:6379> get a
457

从例子中可以看出,客户端2在客户端1的事务未提交时修改了 a 的数据,这时客户端1进行了事务的提交,结果是在客户端2修改后的数据上进行了 INCR 操作。

可以说,完全没有隔离性,同时Redis 中也没有事务隔离级别的概念。但是这样的操作似乎会带来问题呀?没错,隔离性对于数据的准确性会有很大的影响,银行转账的例子大家在学 MySQL 的时候相信也已经听烦了。那么在 Redis 中如何解决这种情况呢?

在 Redis 中,实现的是一种 乐观锁 机制。之前学习 MySQL 中的锁时,我们已经学习过,MySQL 中所有的锁相关操作都是 悲观锁 ,同时我们可以通过版本字段或者时间字段之类的来实现 乐观锁 。在 Redis 中,我们可以通过 WATCH 实现乐观锁。

// 客户端1
127.0.0.1:6379> WATCH a
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> INCR a
QUEUED

// 客户端2
127.0.0.1:6379> set a 789
OK

// 客户端1
127.0.0.1:6379(TX)> EXEC

127.0.0.1:6379> get a
789

看出来是什么意思了吗?乐观锁 就是信任其他人不会修改数据,如果发生了修改,自己就不更新了。WATCH 就是这个意思,在事务开始前,我们通过 WATCH 监控 a 这个 key ,如果在事务的执行过程中,有其它的客户端修改了 a 的数据,那么事务在 EXEC 时,就不会执行。

上面的例子中可以看到,EXEC 执行后未返回任何内容,同时客户端2设置的 789 也并没有 INCR 数据。这就是乐观锁的应用。在实际的业务开发中,可以查看 EXEC 返回的结果来确定事务是否正常执行,如果没有返回信息,说明有其它客户端修改了数据。那么我们就可以再次开启事务进行操作。

看完了隔离性,再来看看原子性,原子性说的是事务提交要么全部成功,要么全部失败回滚。在 Redis 中,如果在事务执行中间发生了异常,那么事务会出现两种情况,我们一个一个来看。

第一种情况是运行时异常,比如使用的命令参数出现了错误,但不影响其它命令的执行,这时只有出问题的命令不会执行,其它的命令还是会执行,这种情况其实是非原子性的。

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> INCR a
QUEUED
127.0.0.1:6379(TX)> set b 222 121
QUEUED
127.0.0.1:6379(TX)> EXEC
1) (integer) 790
2) (error) ERR syntax error

另一种情况是编译型异常,比如使用了错误的命令,这时整个事务的提交都会被丢弃,这个才是原子性的。

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> INCR a
QUEUED
127.0.0.1:6379(TX)> setget b 123 112
(error) ERR unknown command `setget`, with args beginning with: `b`, `123`, `112`,
127.0.0.1:6379(TX)> EXEC
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get a
"790"

综上所述,Redis 中的事务就像我们最开始讲的那样,其实就是一个命令执行队列,并不是完全意义上的关系型数据库中的事务的概念。在面试的时候要注意面试官在这里挖坑哦!

Lua脚本

Lua脚本是非常轻量级的脚本语言,同时也是受到 Nginx 和 Redis 所支持的一种脚本语言。怎么说呢,就是 Redis 可以直接运行或通过 Lua 脚本进行一些操作。

不过我对 Lua 并不熟悉,所以这里也就是演示一下在 Redis 中去执行或加载操作 Lua 脚本的一些命令。

首先看一下如何执行一段 Lua 脚本。

127.0.0.1:6379> EVAL "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 a b 111 222
1) "a"
2) "b"
3) "111"
4) "222"

EVAL 命令后面可以跟着一段 Lua 脚本,后面的参数中,2 表示有两个 KEYS ,a 和 b 就是在 Lua 中可以获取到的 KEYS 数组中的内容。后面的 111 和 222 其实就是对应到 Lua 脚本中 ARGV 数组中的数据。

来个更实际一点的例子,比如我们要为 a 这个 key 赋值。

127.0.0.1:6379> EVAL "redis.call('set', KEYS[1], ARGV[1]);return 'ok'" 1 a 111
"ok"
127.0.0.1:6379> get a
"111"
127.0.0.1:6379> EVAL "return redis.call('get', 'a')" 0
"111"

这个例子就明显很多了吧,我们使用的是 redis.call 这个函数,然后使用 set 命令,通过外部传值把数据传给 Lua 脚本中的 KEYS 和 ARGV ,这样就执行了一个 set a 111 的操作。同样的,我们也可以通过 redis.call 来执行其它的 Redis 命令。

除了 EVAL 命令之外,还有一套 SCRIPT 命令,也是用来操作 Lua 脚本的。SCRIPT 是一套复合命令,它的子命令包括下面这些。

127.0.0.1:6379> SCRIPT HELP
 1) SCRIPT <subcommand> [<arg> [value] [opt] ...]. Subcommands are:
 2) DEBUG (YES|SYNC|NO)
 3)     Set the debug mode for subsequent scripts executed.
 4) EXISTS <sha1> [<sha1> ...]
 5)     Return information about the existence of the scripts in the script cache.
 6) FLUSH [ASYNC|SYNC]
 7)     Flush the Lua scripts cache. Very dangerous on replicas.
 8)     When called without the optional mode argument, the behavior is determined by the
 9)     lazyfree-lazy-user-flush configuration directive. Valid modes are:
10)     * ASYNC: Asynchronously flush the scripts cache.
11)     * SYNC: Synchronously flush the scripts cache.
12) KILL
13)     Kill the currently executing Lua script.
14) LOAD <script>
15)     Load a script into the scripts cache without executing it.
16) HELP
17)     Prints this help.

那么 SCRTIP 命令和 EVAL 命令有什么区别呢?EVAL 是直接执行,而 SCRIPT 可以通过 SCRIPT LOAD 命令将脚本先加载到 Redis 中,但并不马上执行,之后我们可以通过 EVALSHA 命令来执行,就像下面这样。

127.0.0.1:6379> SCRIPT LOAD "return redis.call('get', 'a')"
"b2dc80c45e350e7bf2b3fc26fb0451ee65259785"
127.0.0.1:6379> EVALSHA b2dc80c45e350e7bf2b3fc26fb0451ee65259785 0
"111"

看出来什么意思了吧,SCRIPT LOAD 返回一个哈希签名,然后 EVALSHA 可以直接使用这个签名去运行之前加载进来的脚本。

SCRIPT EXISTS 用于判断给定的签名是否已经加载在当前的服务器环境中。

127.0.0.1:6379> SCRIPT EXISTS b2dc80c45e350e7bf2b3fc26fb0451ee65259785
1) (integer) 1
127.0.0.1:6379> SCRIPT EXISTS b2dc80c45e350e7bf2b3fc26fb0451ee65259784
1) (integer) 0

SCRIPT FLUSH 则是清除所有已加载的脚本。

127.0.0.1:6379> SCRIPT FLUSH
OK
127.0.0.1:6379> SCRIPT EXISTS b2dc80c45e350e7bf2b3fc26fb0451ee65259785
1) (integer) 0

最后,我们再来看一下如何去运行一个外部的 Lua 脚本文件。

➜  ~ vim get.lua
return redis.call('get', 'a')


➜  ~ redis-cli --eval get.lua
"111"

通过 redis-cli --eval 就可以在外部去加载运行一个指定 .lua 文件,这样其实我们就可以写一些自己的 Lua 脚本,比如说预热数据之类的,通过外部执行的方式就能够利用语言优势来批量、循环的操作数据。

Lua 脚本在 Redis 中有非常重要的作用,虽说我们可能平时用不到,但是,在很多框架中,比如 Laravel 或者 Java 的 Redisson 中,都大量频繁地使用了 Lua 脚本。这是为啥呢?那是因为一段 Lua 脚本的执行,在 Redis 中是可以保证完全的原子性的,也就是真正的要么全部成功,要么全部失败,而不是 Redis 事务中的监视事务这种乐观锁机制。

之前我们在学习 Laravel 框架的时候,其实就见过 Redis 配合 Lua 脚本在 Laravel 中的应用,不记得的小伙伴可以去看看视频 【Laravel系列7.7】队列系统https://mp.weixin.qq.com/s/55-wp3YIQpLSrktIlZKMow 。

总结

今天的重点很明显就是事务相关命令的学习,如果你会 Lua 的话,当然也可以在 Redis 中大展身手了。对于日常的工作来说,如果只是将 Redis 作为缓存或者实现一些简单的队列应用的话,事务也都是可有可无的,毕竟我们也不完全依赖于 Redis 来实现真正的需要强事务的功能操作。但是,这一块却又是很多面试官喜欢问的东西,所以了解一下总没坏处。

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

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

相关文章

好用的便签APP排行榜前十名?

我是一名时间管理与自律达人&#xff0c;而便签应用程序就是必备与理想的时间管理工具。经过自己长期的总结认为好用的电脑手机云便签APP应用程序应该具备以下功能。 1、多设备同步&#xff1a;可以方便地将电脑和手机之间的数据同步&#xff0c;随时随地管理便签内容。 2、分…

ijkplayer 编译增加支持更多的音视频格式

ijkplayer是B站开源的一款基于ffmpeg的移动端播放器。但为了减少播放器的体积&#xff0c;很多音视频的格式播放默认都是不支持的&#xff0c;需要自己下载ijkplayer源码进行编译。这里以mac环境下android为例&#xff0c;简述ijkplayer的编译过程&#xff0c;以及为了支持更多…

【C++ 二十】STL:遍历、查找、排序、拷贝和替换、算术生成、集合算法

STL&#xff1a;遍历、查找、排序、拷贝和替换、算术生成、集合算法 文章目录 STL&#xff1a;遍历、查找、排序、拷贝和替换、算术生成、集合算法前言1 常用遍历算法1.1 for_each1.2 transform 2 常用查找算法2.1 find2.2 find_if2.3 adjacent_find2.4 binary_search2.5 count…

零、网络基础概述(TCP/IP模型、端口、网关、DNS、ARP、IP编址与子网划分、UDP、VRP)

文章目录 前言一、网络基础1、TCP/IP模型2、端口的作用&#xff1a;3、MAC 地址4、网关&#xff08;gateway&#xff09;5、域名解析服务&#xff08;DNS&#xff09;6、TCP端口、UDP端口区别&#xff1a;7、交换机与路由器 二、ARP 理论1、定义2、查看ARP缓存3、ARP 报文种类&…

深度学习TensorFlow

博主简介 博主是一名大二学生&#xff0c;主攻人工智能研究。感谢让我们在CSDN相遇&#xff0c;博主致力于在这里分享关于人工智能&#xff0c;c&#xff0c;Python&#xff0c;爬虫等方面知识的分享。 如果有需要的小伙伴可以关注博主&#xff0c;博主会继续更新的&#xff0c…

初识Linux+Linux基本指令(一)

目录 一.&#x1f606;计算机与操作系统&#x1f606; 计算机与操作系统发展史简介: 计算机与操作系统的关系: 二.&#x1f604;Linux操作系统&#x1f604; 开源软件的代名词:Linux 非图形化界面的Liunx 三.&#x1f606;Linux基本指令之文件管理篇&#x1f606; 1.操…

SQL sever数据库----基础增删改查操作与where条件限制

where条件限制方法 在SQL sever中使用where语句&#xff0c;可以对各种操作添加限制条件 基础格式为 ———————— where 逻辑表达式 例如限制条件的查询 select 范围 from 表名 where 逻辑表达式 逻辑表达式就是一个判断 如 a > 5 、a6>9、a>5 and b>5 各种…

php+vue+mysql校园大学生兼职信息网站系统

商家功能模块 商家通过点击后台管理&#xff0c;进入页面可以进行首页、个人中心、热门兼职管理、兼职接单管理、学生咨询管理、兼职任务管理、完成评价管理等功能模块&#xff0c;进行相对应操作 兼职接单管理&#xff1a;通过兼职接单管理可以进行获取兼职名称、专业、分类、…

Jenkins 流水线

采用Jenkins的自由风格构建的项目&#xff0c;适合用于测试和学习&#xff0c;主要问题有&#xff1a; 构建过程中整体流程是不可见的&#xff0c;无法确认每个流程花费的时间出现问题不方便快速的定位无法进行版本化管理多个任务中有很多步骤需要重复搭建 Jenkins的Pipeline…

ServletConfig和ServletContext 的介绍和代码实现

目录 ServletConfig ServletConfig 基本介绍 ServletConfig 类能干什么 为什么需要 ServletContext 1. 方案 1-DB 2. 方案 2-ServletCntext 代码实战 ServletContext ServletContext 基本介绍 ServletContext 可以做什么 代码实战 代码实战2 ServletConfig Servle…

SpringBoot单元测试断言 assertions

断言 断言&#xff08;assertions&#xff09;是测试方法中的核心部分&#xff0c;用来对测试需要满足的条件进行验证。这些断言方法都是 org.junit.jupiter.api.Assertions 的静态方法。JUnit 5 内置的断言可以分成如下几个类别&#xff1a; 1、简单断言 2、数组断言 通过 …

原来情感可以这样影响用户体验设计

&#x1f525;情绪的基本情况 Emotion&#xff1a;即刻的生理反应&#xff0c; Feeling&#xff1a;物理的或者心理上的&#xff0c;是emotion经过思考后的 Mood&#xff1a;持续时间更长&#xff0c;是一种状态&#xff0c;受到很多因素影响&#xff08;天气、睡眠&#x…

OpenCV算法加速的一些学习总结

一、概述 算法加速在实际软件层面应用来说 大数据和复杂计算的过程中 算法优化&#xff0c;指降低算法计算复杂度&#xff0c;设计新算法快速求解&#xff0c;比如Hungarian匹配算法。或牺牲一些内存&#xff0c;预计算一些重复计算的过程&#xff0c;减少程序层面的复杂度。 …

微软文字转语音不能试用了,分享三个方法给大家!

最近很多小伙伴告诉我&#xff0c;微软文字转语音不能在线试用了&#xff0c;这是因为微软关闭了官方的使用页面&#xff0c;所以现在不能直接使用微软的网页版进行文字转语音了。 那么我们还有没有更好的方法去“白嫖”微软的文字转语音呢&#xff1f; 答案是肯定的&#xf…

MTU 网卡bond 简介

MTU 最大传输单元MTU&#xff08;Maximum Transmission Unit&#xff0c;MTU&#xff09;&#xff0c;是指网络能够传输的最大数据包大小&#xff0c;以字节为单位。MTU的大小决定了发送端一次能够发送报文的最大字节数。如果MTU超过了接收端所能够承受的最大值&#xff0c;或者…

被裁后找不到工作,本质上是因为原来的能力就配不上高薪,如果技术好,根本不怕被裁,相当于白送n+1!...

被裁员后&#xff0c;能要求公司补缴公积金吗&#xff1f; 一位网友问&#xff1a; 被裁员了&#xff0c;要求公司把历史公积金全部足额缴纳&#xff0c;现在月薪2.3万&#xff0c;但公司每个月只给自己缴纳300元公积金&#xff0c;结果一次补了二十多万&#xff0c;一次性取出…

Linux工具——yum和vim

目录 &#x1f34f;Linux软件包管理器-yum&#x1f34e;yum简介&#x1f34e;rzsz工具&#x1f34e;注意事项&#x1f34e;软件包查看&#x1f34e;如何安装和卸载软件 &#x1f34f;Linux编辑器-vim&#x1f34e;vim的基本概念&#x1f34e;vim的基本操作&#x1f34e;vim正常…

Linux基础——FTP原理与配置

Linux基础——FTP原理与配置 一、文件传输协议——FTP服务二、ftp配置文件解析三、FTP服务器搭建 一、文件传输协议——FTP服务 FTP是典型的C/S结构的应用层协议&#xff0c;需要由服务器软件、客户端软件两个部分共同实现文件传输功能 FTP 连接模式 FTP服务器默认使用TCP协议…

盖子的c++小课堂——第十七讲:递归

前言 通知一下&#xff0c;以后每周不定期更新&#xff0c;有可能是周六更新&#xff0c;也可能是周日吧&#xff0c;反正会更新的~~还有我新出的专栏《跟着盖子读论语》&#xff0c;记得订阅一下啊跟着盖子学《论语》_我叫盖子的盖鸭的博客-CSDN博客 三元表达式 三元表达式…

中国版ChatGPT来了!快跟我一起申请文心一言吧

随着ChatGPT的快速进化吸引了全球网友的眼球 国内厂商也纷纷推出了相似的产品 其中百度推出的“文心一言”已经正式开始的相关的测试 很多人都在问 文心一言入口在哪&#xff1f; 文心一言邀请码在哪可以领&#xff1f; 文心一言怎么申请内测&#xff1f; 自从文心一言发…
最新文章