Redis 之四:Redis 事务和乐观锁

事务特点

Redis 事务可以一次执行多个命令, 并且带有以下三个重要的保证:

  • 批量操作在发送 EXEC 命令前被放入队列缓存。

  • 收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。不具备原子性。

  • 在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。

Redis 事务的缺点
  1. 不支持回滚: Redis事务在执行EXEC命令之前,如果通过WATCH检测到监视的键发生了变化,则会拒绝执行整个事务并返回空结果。但请注意,这并不是传统数据库中的“回滚”操作,因为Redis不会自动撤销已经执行过的命令,它仅仅是在事务中止时阻止后续未执行的命令。
  2. 命令排队一次性执行: 在Redis事务中,所有命令会被放入一个队列,在EXEC命令被执行时按照先进先出的顺序执行,期间不能中断或插入新的命令。这意味着事务开始后无法根据中间结果动态调整事务内的操作,降低了灵活性。
  3. 无隔离级别: Redis的事务没有提供如SQL数据库那样的多种事务隔离级别(如读已提交、可重复读等)。所有事务都是在单线程环境下的串行化执行,因此避免了脏读、不可重复读等问题,但这也意味着在高并发场景下可能会有性能瓶颈。
  4. Watch-Multi-Exec模式的问题: 使用WATCH进行乐观锁控制时,一旦网络延迟或者客户端异常导致事务未能及时执行,监视的数据可能已经被其他客户端修改,此时即便事务最终执行,也无法保证数据一致性。
  5. 批量操作不具备完全的原子性: 虽然Redis的所有命令在服务器内部是原子执行的,但在一个事务中多个命令的组合并不能视为一个原子操作。例如,事务中包含对多个key的操作,即使其中一个操作失败,事务内其它命令也会被执行完毕,而不是整体取消。
三个阶段

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

  • 开始事务。
  • 命令入队。
  • 执行事务。
案例步骤

从multi 开始,一系列操作,最后执行 exec ,批量执行处理

127.0.0.1:6379> multi
OK
127.0.0.1:6379> keys *
QUEUED
127.0.0.1:6379> hset person name zhang age 34
QUEUED
127.0.0.1:6379> hdel person age
QUEUED
127.0.0.1:6379> hset person email zhang@sina.com
QUEUED
127.0.0.1:6379> exec
1)  1) "listz"2) "book"3) "word"4) "myset"5) "mydest"6) "student"
2) (integer) 2
3) (integer) 1
4) (integer) 1
127.0.0.1:6379> hgetall person
1) "name"
2) "zhang"

下表列出了 redis 事务的相关命令:

序号命令及描述
1[DISCARD] 取消事务,放弃执行事务块内的所有命令。
2[EXEC] 执行所有事务块内的命令。
3[MULTI] 标记一个事务块的开始。
4[UNWATCH] 取消 WATCH 命令对所有 key 的监视。
5[WATCH key [key ...]] 监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。
乐观锁

乐观锁(Optimistic Locking)是一种在数据库并发控制中的策略,它假设多用户同时访问同一数据时发生冲突的概率较低,并且在更新数据之前并不立即进行加锁操作。与悲观锁不同的是,悲观锁在读取数据时就直接获取并持有锁,直到事务结束才释放;而乐观锁则是:

  1. 读取阶段:当一个事务想要修改数据时,它不会立即锁定该数据行。每个事务在读取数据时都会记录下当时的数据版本号或时间戳等信息。
  2. 验证阶段:在事务提交更新操作前,会再次检查当前要更新的数据是否自上次读取以来没有被其他事务修改过。这通常通过比较数据的版本号来实现,如果版本号未变,则认为可以安全地执行更新。
  3. 更新阶段:如果数据版本验证通过(即版本号仍为事务开始读取时的版本),则执行更新操作,并将数据版本号递增,确保后续的并发事务能够识别出这次更新。如果发现版本号已被改变,说明存在并发修改,此时乐观锁机制会让当前事务回滚,并提示并发错误,通常需要重新读取数据并尝试更新。
Redis 乐观锁

Redis 乐观锁是一种在分布式系统中实现并发控制的机制,它借鉴了数据库领域的乐观并发控制思想,并通过Redis提供的命令来实现。在乐观锁策略下,假定多个客户端同时访问同一数据时,通常不会发生冲突或至少冲突的概率较低。因此,在读取数据时不立即加锁,而是在更新数据前才去检查在此期间是否有其他客户端修改过该数据。

在Redis中,乐观锁主要通过WATCH命令和事务(multi/exec)来实现:

  1. WATCH命令:客户端使用WATCH命令监视一个或多个键,这些键的数据状态将被记录下来。当执行WATCH后,如果任何被监视的键在事务提交前发生了变化,则整个事务将会被打断,即不会执行EXEC命令内的操作。
  2. 事务处理:客户端可以将一系列命令放入事务中,使用MULTI开始一个事务块,然后执行一系列的操作指令。最后,用EXEC命令尝试提交事务。只有在所有被WATCH的键自WATCH以来未被其他客户端改变的情况下,事务中的命令才会被执行。

举例来说:

  • 客户端A对一个键进行WATCH
  • 然后客户端A开始一个事务,并准备修改这个键的值。
  • 在事务提交(EXEC)之前,如果其他客户端改变了该键的值,那么客户端A的事务在执行EXEC时会发现数据已经被修改,从而导致事务回滚,不执行任何操作。

下面具体来举例:

主要是 watch 和 multi 两个命令配合使用,实现乐观锁

watch 监视某一个可能变化的 key。然后开启事务,一系列操作放入队列等待执行,如果在exec 执行事务之前,其他的客户端对监视的key 做了修改,则exec 执行结果为nil 。什么都不执行。

127.0.0.1:6379> watch mm         #### 开始监视 mm 变量
OK
127.0.0.1:6379> multi            #### 开启事务
OK
127.0.0.1:6379> incrby mm 500    #### 第一次 给 mm 加500 操作放入队列
QUEUED
127.0.0.1:6379> incrby mm 500    #### 第二次 给 mm 加500 操作放入队列 
QUEUED
### 此时去另一个客户端执行修改操作

另一个客户端的修改操作

127.0.0.1:6379> decrby mm 500    #### 给 mm 减去500
(integer) -500                   #### 立即起效,结果为 -500

然后再回原来执行事务的客户端执行下面操作:

127.0.0.1:6379> exec             #### 开始执行事务
(nil)                            #### 没有任何执行结果  因为乐观锁起作用了
127.0.0.1:6379> get mm           #### 再次查看结果
"-500"                           #### 是另一个客户端的执行结果。刚才加的两次500 无效。
127.0.0.1:6379> watch mm         #### 再次监控
OK
127.0.0.1:6379> multi            #### 开启事务
OK
127.0.0.1:6379> incrby mm 500    #### 加500
QUEUED
127.0.0.1:6379> incrby mm 500    #### 加500
QUEUED
127.0.0.1:6379> exec             #### 执行事务
1) (integer) 0
2) (integer) 500                 #### 执行成功,因为其他客户端没有修改被监视的变量 mm .
127.0.0.1:6379> 

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

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

相关文章

通讯录——C语言实现

头文件Contact.h #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<string.h> #include<stdlib.h> #pragma once #define MAX 100 #define MAX_NAME 20 #define MAX_SEX 5 #define MAX_TELE 12 #define MAX_ADDR 30//表示一个人的信息 //struct…

npm使用国内淘宝镜像的方法整理

命令配置安装&#xff1a; 淘宝镜像&#xff1a; npm config set registry https://registry.npm.taobao.org/ 官方镜像&#xff1a; npm config set registry https://registry.npmjs.org 通过cnpm安装&#xff1a; npm install -g cnpm --registryhttps://registry.npm.…

PTA L2-003 月饼 (附坑点说明)

月饼是中国人在中秋佳节时吃的一种传统食品&#xff0c;不同地区有许多不同风味的月饼。现给定所有种类月饼的库存量、总售价、以及市场的最大需求量&#xff0c;请你计算可以获得的最大收益是多少。 注意&#xff1a;销售时允许取出一部分库存。样例给出的情形是这样的&#…

如何在Java中反转字符串?

目录 1. 使用StringBuilder的reverse方法&#xff1a; 2. 使用for循环和字符数组&#xff1a; 3. 使用递归&#xff1a; 4. 使用Java 8的Stream API&#xff1a; More Java中&#xff0c;反转字符串可以通过多种方式实现。以下是一些常见的方法&#xff1a; 1. Java中使用…

【Python】PyGameUI控件

哈里前段时间写了一个windows平板上自娱自乐&#xff08;春节和家人一起玩&#xff09;基于pygame的大富翁游戏。 pygame没有按钮之类的UI控件&#xff0c;写起来不怎么顺手。就自己写一个简单的框架。 仓库地址 哈里PygameUi: pygame ui封装自用 (gitee.com) 使用示例 示…

上海亚商投顾:沪指终结月线6连阴 北向资金净买入超160亿

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 一.市场情绪 三大指数昨日低开高走&#xff0c;沪指重新站上3000点&#xff0c;深成指、创业板指大涨超3%。半导体产业链全…

实时聊天系统PHP

实时聊天系统可以让用户在网站上实时交流&#xff0c;这对社交平台、在线客服等网站非常有帮助。以下是一个简单的基于 PHP 和 WebSocket 的实时聊天系统示例&#xff1a; 1. 首先创建一个 HTML 文件 index.html 来显示聊天界面和发送消息的表单&#xff1a; html <!DOCTYP…

【C#】 List.Sort 方法

【C#】 List.Sort 方法 在C#中&#xff0c;List.Sort()不仅为系统自带的变量(int, float, double …)类型的集合提供默认排序&#xff0c;还提供了自定义的排序方法。 List自带排序 List<int> list new List<int>(); list.Add(5); list.Add(3); list.Add(4); l…

探索那些能唤起情感共鸣的壁纸

1、方小童在线工具集 网址&#xff1a; 方小童 该网站是一款在线工具集合的网站&#xff0c;目前包含PDF文件在线转换、随机生成美女图片、精美壁纸、电子书搜索等功能&#xff0c;喜欢的可以赶紧去试试&#xff01;

Python:关于数据服务中的Web API的设计

搭建类似joinquant、tushare类似的私有数据服务应用&#xff0c;有以下一些点需要注意&#xff1a; 需要说明的是&#xff0c;这里讨论的是web api前后端&#xff0c;当然还有其它方案&#xff0c;thrift&#xff0c;grpc等。因为要考虑到一鱼两吃&#xff0c;本文只探讨web ap…

高项软考电子版论文答题纸(附下载)

24年软考又要来了&#xff0c;作为高项软考的拦路虎&#xff0c;论文你准备好了吗&#xff1f;&#xff01;记住在开始考试之前一定要用论文答题纸上把准备好的论文&#xff0c;在规定的时间内写上几遍&#xff0c;一是现在很少动笔写字了。二是、熟悉一下论文考试的感觉。 准备…

UniApp Vue 3 中的网络请求封装详解及用法

在UniApp中&#xff0c;结合Vue 3的强大特性&#xff0c;进行网络请求的封装是项目中常见的需求。这样的封装不仅提高了代码的可维护性&#xff0c;还使得在组件中使用网络请求更加简洁。本文将详细介绍UniApp Vue 3中的网络请求封装&#xff0c;并提供一个简单的用法示例。 1…

索引使用规则4——覆盖索引回表查询

覆盖索引&#xff1a;查询使用了索引&#xff0c;并且需要返回的列&#xff0c;在索引里面都可以找到&#xff0c;减少select*的使用 1、using index condition Extra 为using index condition 表明查找使用了索引&#xff0c;但是需要回表查询&#xff08;也就是先二级索引&…

第十八届全国大学生智能汽车竞赛——摄像头算法(附带个人经验)

文章目录 前言一、摄像头图像处理1、摄像头图像采集2、图像二值化与大津算法 二、左右边界&#xff0c;中线扫描 前言 参加了第十六&#xff0c;十七和第十八届全国大学生智能车竞赛&#xff0c;对摄像头的学习有部分心得&#xff0c;分享给大家&#xff0c;三届车赛&#xff…

【C语言基础】:深入理解指针(一)

文章目录 一、内存和地址1. 内存2. 如何理解编址 二、指针变量和地址2.1 取地址操作符(&)2.2 指针变量和解引用操作符(*)2.2.1 指针变量2.2.2 如何拆解指针变量2.2.3 解引用操作符 2.3 指针变量的大小 三、指针变量类型的意义3.1 指针的解引用3.2 指针 - 整数3.3 void*指针…

HCIA-HarmonyOS设备开发认证V2.0-习题

目录 习题一习题二&#xff08;待续...&#xff09;坚持就有收获 习题一 # HarmonyOS简介 1. 以下哪几项属于OpenHarmony的技术特性&#xff1f;&#xff08;&#xff09;A. 统一OS&#xff0c;弹性部署B. 一次开发&#xff0c;多端部署C. 硬件互助&#xff0c;资源共享2. Ope…

从零开始的Java知识(下)

从零开始的Java知识 双列数据集合&#xff08;Day1&#xff09;Map 双列数据集合&#xff08;Day1&#xff09; Map 注意点&#xff1a; Map一次加入一个key-value一个key对应一个valuekey与key之间是不重复的key-value被称为键值对&#xff0c;键值对对象或者是entry对象 …

离散数学

(理解大于识记, 这么多公式我是记不住) 命题逻辑 P P P Q Q Q P \neg P P 否定/非 P ∧ Q P \wedge Q P∧Q 合取/与 P ∨ Q P \vee Q P∨Q 析取/或 P → Q P \to Q P→Q 蕴含 P ↔ Q P \leftrightarrow Q P↔Q 等价0010011011011010001001101111 P → Q P\to Q P→Q 的自然语…

openssl 加密文件(支持大文件,对称、非对称)

一、非对称加密&#xff08;小文件&#xff09; 生成 2048 位密钥 openssl genrsa -out rsa2048.key 2048从 rsa2048.key 密钥文件中提取出公钥 pub2048.key openssl rsa -in rsa2048.key -pubout -out pub2048.key使用 pub2048.key 公钥加密一个文件 (data.zip 为原始文件&…

C# WPF编程-创建项目

1.创建新项目 选择“WPF应用程序”》“下一步” 2. 设置项目 设置项目名称&#xff0c;保存位置等参数>下一步 3.选择框架 4.项目创建成功 5.运行项目