Redis中的事务机制

Redis中的事务机制

概述。

事务表示一组动作,要么全部执行,要么全部不执行。例子如下。
Redis提供了简单的事务功能,讲一组需要一起执行的命令放到multi和exec两个命令之间。multi命令代表事务开始,exec命令代表事务结束,如果要停止事务的执行,可以使用discard命令代替exec命令即可。它们之间的命令是原子执行的

例子

例如在社交网站上用户A关注了用户B,那么需要在用户A的关注表中加入用户B,并且在用户B的粉丝表中添加用户A,这两个行为要么全部执行,要么全部不执行,否则会出现数据不一致的情况

例如下面代码实现了用户关注问题

127.0.0.1:6379> SADD u:a:follow ub
QUEUED
127.0.0.1:6379> SADD u:b:fans ua
QUEUED

可以看到SADD命令此时的返回结果是QUEUED,代表命令并没有真正执行,而是暂时保存在Redis中的一个缓存队列(所以discard也只是丢弃这个缓存队列中的未执行命令,并不会回滚已经操作过的数据,这一点要和关系型数据库的Rollback操作区分开)。如果此时另一个客户端执行下方代码,返回结果应该是0

127.0.0.1:6379> SISMEMBER u:a:follow ub
(integer) 0

只有当exec执行后,用户A关注用户B的行为才算完成,如下所示exec返回的两个结果对应SADD命令

127.0.0.1:6379> multi
OK
127.0.0.1:6379> SADD u:a:follow ub
QUEUED
127.0.0.1:6379> SADD u:b:fans ua
QUEUED
127.0.0.1:6379> exec
1) (integer) 1
2) (integer) 1

另一个客户端:

127.0.0.1:6379> SISMEMBER u:a:follow ub
(integer) 0
127.0.0.1:6379> SISMEMBER u:a:follow ub
(integer) 1

错误处理机制

如果事务中的命令出现错误,Redis的处理机制也不尽相同

1.命令错误

例如下面操作将set写成了SETT,属于语法错误,会造成整个事务无法执行,

127.0.0.1:6379> set txkey hello
OK
127.0.0.1:6379> set txcount 100
OK
127.0.0.1:6379> mget txkey txcount
1) "hello"
2) "100"
127.0.0.1:6379> multi
OK
127.0.0.1:6379> sett txkey world
(error) ERR unknown command `sett`, with args beginning with: `txkey`, `world`,
127.0.0.1:6379> incr txcount
QUEUED
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> mget txkey txcount
1) "hello"
2) "100"

2.运行时错误

例如用户B在添加粉丝列表时,误把SADD命令(针对集合)写成了ZADD命令(针对有序集合),这种就是运行时命令,因为语法时正确的:

127.0.0.1:6379> SADD u:b:fans ua
(integer) 1
127.0.0.1:6379> multi
OK
127.0.0.1:6379> SADD u:c:follow ub
QUEUED
127.0.0.1:6379> ZADD u:b:fans 1 uc
QUEUED
127.0.0.1:6379> exec
1) (integer) 1
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
127.0.0.1:6379> sismember u:c:follow ub
(integer) 1

可以看到Redis并不支持回滚功能,SADD u:c:follow ub命令已经执行成功,开发人员需要自己修复这类问题。

3.WATCH机制

有些应用场景需要在事务之前,确保事务中的key没有key没有被其他客户端修改过,才执行事务,否则不执行(类似乐观锁)。Redis提供了watch命令来解决这类问题。
客户端1

127.0.0.1:6379> set testwatch java
OK
127.0.0.1:6379> watch testwatch
OK
127.0.0.1:6379> multi
OK

客户端2

127.0.0.1:6379> get testwatch
"java"
127.0.0.1:6379> append testwatch python
(integer) 10

客户端1继续:

127.0.0.1:6379> append testwatch jedis
QUEUED
127.0.0.1:6379> exec
(nil)
127.0.0.1:6379> get testwatch
"javapython"

可以看到客户端1在执行multi之前执行了watch命令,客户端2在客户端1执行exec之前修改了key值,造成可客户端1事务没有执行(exec结果为nil)

Pipeline和事务的区别

  • 1.pipeline是客户端的行为,对于服务器来说是透明的,可以认为服务器无法区分客户端发送来的查询命令是以普通命令的形式还是以pipeline的形式发送到服务器的;
  • 2.而事务则是实现在服务器端的行为,用户执行MULTI命令时,服务器会将对应这个用户的客户端对象设置为一个特殊的状态,在这个状态下后续用户执行管的查询命令不会被真的执行,而是被服务器缓存起来,直到用户执行EXEC命令为止,服务器会将这个用户对应的客户端对象中缓存的命令按照提交的顺序依次执行
  • 3.应用pipeline可以提高服务器的吞吐能力,并提高Redis处理查询请求的能力。但是这里存在一个问题,当通过pipeline提交的查询命令数据较少,可以被内核缓冲区所容纳时,Redis可以保证这些命令执行的原子性。然而一旦数据量过大,超过了内核缓冲区的接收大小,那么命令的执行将会被打断,原子性也就无法得到保证。因此pipeline只是一种提升服务器吞吐能力的机制,如果想要命令以事务的方式原子性地被执行,还是需要事务机制,或者使用更高级的脚本功能以及模块功能
  • 4.可以将事务和pipeline结合起来使用,减少事务地命令在网络上的传输时间,将多次网络IO缩减为一次网络IO.
  • Redis提供了简单的事务,之所以说它简单,主要时因为它不支持事务中的回滚特性,同时无法实现命令之间的逻辑关系计算,当然也体现了Redis的"keep it simple"的特性

Lua脚本

Redis在2.6推出了脚本功能,允许开发者使用Lua语言编写脚本传到Redis中执行,使用脚本的好处如下:

  • 1.减少网络开销:本来5次网络请求的操作,可以用一个请求完成,原先5次请求的逻辑放在Redis服务器上完成,使用脚本,减少了网络往返时延,跟管道类似
  • 2.源自操作:Redis会将整个脚本作为一个整体执行,中间不会被其他命令插入,管道不是原子的,redis的批量操作命令(类似mset)是原子的
  • 3.替代redis的事务功能:redis自带的事务功能很鸡肋,而redis的lua脚本几乎实现了常规的事务功能,官方推荐如果要使用redis的事务功能可以用redis lua替代
    A Redis script is transactional by definition, so everything you can do with a Redis transaction, you can also do with a script,and usually the script will be both simpler and faster.

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

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

相关文章

【项目】均衡代码评测

TOC 目录 项目介绍 开发环境 主要技术 项目实现 公共模块 日志 工具类 编译运行模块 介绍 编译 运行 编译和运行结合起来 业务逻辑模块 介绍 MVC模式框架 模型(Model) 视图(View) 控制器(Controller&#xff09…

使用html做一个2048小游戏

下载地址: https://pan.xunlei.com/s/VNtiF13HxmmE4gglflvS1BUhA1?pwdvjrt# 提取码:vjrt”

Linux文件系统 底层原理

linux文件、目录、Inode inode负责文件的元数据和数据存储,文件存储块负责实际数据的存储,而目录文件维护文件名和inode之间的联系。 1. 用户空间到内核空间 首先,当用户程序请求打开一个文件时(例如使用open系统调用&#xff09…

C/C++ 一些使用网站收集...

C/C 标准 各种语言协议标准文档 open-std.org 网站 C语言标准文档 open-std.org C基金会网站 C/C 标准库网站 c/c 标准库 cplusplus.com 网站 c/c标准库 cppreference.com 网站 C Core Guidelines【isocpp.org】 C/C 发展 C现有状态及未来规划【isocpp.org】

Excel 打开后提示:MicrosoftExcel无法计算某个公式。在打开的工作簿中有一个循环引用...

目录预览 一、问题描述二、原因分析三、解决方案四、参考链接 一、问题描述 MicrosoftExcel无法计算某个公式。在打开的工作簿中有一个循环引用,但无法列出导致循环的引I用。请尝试编辑上次输入的公式,或利用“撤消”命令删除该公式,如下图&…

Matplotlib如何显示多张图片(管理多个子图)

Matplotlib 可以使用 subplot 或 subplots 方法来创建子图,从而在同一窗口中显示多张图片。以下是一些示例代码,展示如何使用 Matplotlib 显示多张图片: 1.使用 subplot import matplotlib.pyplot as plt import matplotlib.image as mpi…

微服务(基础篇-003-Nacos)

目录 Nacos注册中心(1) 认识和安装Nacos(1.1) Nacos快速入门(1.2) 服务注册到Nacos(1.2.1) Nacos服务分级存储模型(1.3) 配置集群(1.3.1) 根据集群修改…

pta L1-004 计算摄氏温度

L1-004 计算摄氏温度 分数 5 全屏浏览 切换布局 作者 陈建海 单位 浙江大学 给定一个华氏温度F,本题要求编写程序,计算对应的摄氏温度C。计算公式:C5(F−32)/9。题目保证输入与输出均在整型范围内。 输入格式: 输入在一行中给出一个华氏…

如何用Airtest脚本连接无线Android设备?

之前我们已经详细介绍过如何用AirtestIDE无线连接Android设备,它的关键点在于,需要先 adb connect 一次,才能点击 connect 按钮无线连接上该设备: 但是有很多同学,在使用纯Airtest脚本的形式连接无线设备时&#xff0c…

odoo中,使用paramiko库ssh连接Linux

在 Python 中,可以使用 Paramiko 库来实现 SSHClient 连接。Paramiko 是一个用于远程登录、文件传输和自动化的 SSH 客户端库。 import paramiko# 创建 SSHClient 对象 ssh paramiko.SSHClient()# 设置自动添加主机密钥 ssh.set_missing_host_key_policy(paramiko.…

K8s-网络原理-下篇

引言 本文是《深入剖析 K8s》的学习笔记,相关图片和案例可从https://github.com/WeiXiao-Hyy/k8s_example中获取,欢迎Star! K8s 的网络隔离: NetWorkPolicy K8s 如何考虑容器之间网络的“隔离” -> NetWorkPolicy 以下是一个 NetWorkPolicy 的定义…

气候模型与大模型结合的高精度天气预报技术研究

1. 背景介绍 随着全球气候变化的加剧,天气预报的准确性变得越来越重要。传统的气候模型在预测长期气候变化方面取得了显著进展,但在短期天气预报方面仍存在一定的局限性。近年来,随着人工智能技术的发展,尤其是大模型在自然语言处…

Android开发 --- Android12外部存储权限问题

1.问题 Android12使用如下权限&#xff0c;将不会获得读写文件的权限 <uses-permission android:name"android.permission.WRITE_EXTERNAL_STORAGE" /> 2.解决 if (!Environment.isExternalStorageManager()) {Intent intent new Intent(Settings.ACTION_M…

将数据转换成xml格式的文档并下载

现在有一个实体类对象的集合&#xff0c;需要将它们转换为xml文档&#xff0c;xml文档就是标签集合的嵌套&#xff0c;例如一个学生类&#xff0c;有姓名、年龄等&#xff0c;需要转换成一下效果&#xff1a; <student><age>14</age><name>张三</na…

Java学习Day3

一道简单练习题&#xff0c;对编号进行随机排名 import java.util.Random; import java.util.Scanner;public class Main {public static void rand(int[] a,int n){Random rnew Random();for (int i0;i<n;i){int rrr.nextInt(n);int tempa[i];a[i]a[rr];a[rr]temp;}}publ…

如何更换Ubuntu系统的软件源

Ubuntu系统的软件源是决定你的系统从哪里下载软件包和更新的关键设置。有时&#xff0c;由于网络连接问题或软件源的更新&#xff0c;你可能需要更换软件源。以下是详细的步骤&#xff0c;教你如何在Ubuntu系统上更换软件源&#xff0c;并确保在更改之前进行备份。 步骤1&…

时间戳的转换-unix时间戳转换为utc时间(python实现)

import datetimetimestamp = 1711358882# 将时间戳转换为UTC时间 utc_time = datetime.datetime.utcfromtimestamp(timestamp)# 格式化并输出时间 formatted_time = utc_time.strftime(%Y-%m-%d %H:%M:%S) print(formatted_time)同样:UTC如何转换为unix时间戳 from datetime …

如何利用python 把一个表格某列数据和另外一个表格某列匹配 类似Excel VLOOKUP功能

环境: python3.8.10 Excel2016 Win10专业版 问题描述: 如何利用python 把一个表格某列数据和另外一个表格某列匹配 类似Excel VLOOKUP功能 先排除两表A列空白单元格,然后匹配x1表格和x2表格他们的A列,把x1表格中A列A1-A810范围对应的B列B1-B810数据,匹配填充到x2范围…

进程和线程的区别

进程 什么是进程呢 进程就是一个正在执行的程序 ,是计算机操作系统进行资源分配和任务调度的基本单位. 进程是计算机中的程序关于某个数据集合上的一次运行活动,是系统分配资源的基本单位,是操作系统结构的基础.每个进程都有它自己的地址空间,包括文本区域 、数据区域和堆栈区…

C语言程序与设计——预处理命令

宏 在C语言中宏有三种形式: 定义符号常量定义傻瓜表达式定义代码段 在使用宏的过程中需要注意的是&#xff0c;宏的作用仅仅是在预处理阶段对代码进行替换&#xff0c;而非进行运算&#xff0c;所以在使用时&#xff0c;如果出现了我们预期之外的结果&#xff0c;很有可能是宏…