MySQL事务篇-事务概念、并发事务问题、隔离级别

事务

事务是一组不可分割的操作集合,这些操作要么同时成功提交,要么同时失败回滚。

acid事物的四大特性

原子性

最小工作单元,要么同时成功,要么同时失败。

例如A转账300给B,A账户-300与B账户+300必须满足操作原子性,避免出现A已转账但B未收到的一致性问题。

一致性

事务操作的起点和终点必须是从一个一致性状态到另一个一致性状态,也就是数据库的数据变化必须符合预定义期望变化。(不会出现一个数据库修改成功、一个失败的情况)

例如在转账案例中事务开始时的账户总额等于事务结束时的账户金额。(并不是一定相等,数据变化符合业务预定义期望即可)

隔离性

并发的事务是相互隔离的。

例如多个并发转账事务,每个转账操作的数据是相互独立的,不会出现数据混乱的情况。

持久性

一旦事务提交,其结果就是永久的,不会因系统崩溃丢失。

事务提交后会将数据持久化到硬盘,例如在装张案例中,变更后账户数据持久化在硬盘,数据库崩溃依然被保留。

并发事务问题

脏读

事务A读取到事务B未提交的修改。

不可重复读

同一事务内多次读取同一数据时因为其他事物在此期间提交了数据修改导致结果不同。

幻读

同一事务内对一张表的查询结果集不同,因为其他事务在此期间插入删除了数据。

select * 结果集行数不同。

select count()/sum() 等聚合函数,查询内容可能不同。

例如,事务A查询name=张三不存在,事务B插入张三,事务A按照张三不存在的业务逻辑插入张三但无法插入。

隔离级别

读未提交(RU)

允许事务读取其他事物未提交的修改(脏读)。

并发性能最高。

读已提交(RC)

不允许事务读取其他事物未提交的修改(脏读)。

无法避免不可重复读现象。

可重复读(RR)

不会出现脏读和不可重复读问题。

无法避免幻读问题。

MySQL默认隔离级别。

串行化(S)

完全避免所有并发问题。

并发性能最低。

如何选择隔离级别

隔离级别越高,并发性能越低。

  • 读未提交(RU):仅适用对数据准确性要求极低,并发性能要求极高的场景,如监控数据,日志采集,瞬时数据不影响整体的场景,但实际实际生产环境下中还是极少使用,规避脏读风险。

  • 读已提交(RC):适用大部分普通业务场景,也是大部分数据库的默认隔离级别。例如用户信息页,用户A修改提交后,用户B刷新就能看到用户A提交的修改内容,但不会看到用户A未提交的内容。

    RC下不可重复读问题:

    🛒 场景一:库存扣减(并发抢购)

    • 业务逻辑: 用户下单时,需要检查并扣减商品库存(例如商品A,初始库存10件)。

    • 事务A (用户1下单):

      1. BEGIN; (RC隔离级别)

      2. SELECT stock FROM products WHERE id = 'A'; // 返回 10 (库存充足)

      3. (基于查询结果10,决定继续下单逻辑... 生成订单、计算价格等,耗时几毫秒/秒)

    • 事务B (用户2下单): (几乎与事务A同时发生)

      1. BEGIN; (RC隔离级别)

      2. SELECT stock FROM products WHERE id = 'A'; // 也返回 10 (库存充足)

      3. UPDATE products SET stock = stock - 1 WHERE id = 'A'; // 扣减1件,库存变为9

      4. COMMIT; // 用户2下单成功,库存更新为9并生效

    • 事务A 继续执行:

      1. (执行完其他逻辑后)

      2. UPDATE products SET stock = stock - 1 WHERE id = 'A'; // 此时基于 *当前已提交数据* (stock=9) 扣减,库存变为8

      3. COMMIT; // 用户1下单成功

    • 问题:

      • 两个用户都成功下单购买了商品A。

      • 最终库存变为 8,这符合物理扣减。

    • 不可重复读在哪里?

      • 事务A 在步骤2读取 stock=10

      • 在它执行后续逻辑时,事务B 修改并提交了库存(变为9)。

      • 当事务A 执行更新操作(步骤2.2)时,它没有基于自己最初读到的10去减1,而是基于最新已提交值9去减1。虽然最终库存正确(8),但事务A在逻辑判断(库存是否充足)后,执行更新操作时依赖的数据(库存值)已经发生了变化(10 -> 9)。这就是一次“不可重复读”(在同一个事务A内,如果它再次执行SELECT stock...,结果会是9,而不是最初的10)。

    • 潜在风险:

      • 超卖风险: 如果初始库存只有1件,多个事务都读到1(认为充足),然后都去扣减1(事务B扣成0并提交,事务A再基于0扣减就会变成-1)。这就是经典的并发超卖问题!虽然RC下避免了脏读(不会读到事务B未提交的扣减),但因为不可重复读,两个事务都基于“过时”的充足判断进行了扣减,导致库存为负。解决超卖通常需要额外的并发控制(如乐观锁、悲观锁、Redis分布式锁等),而不仅仅是依赖隔离级别。

    🕒 场景二:预约系统(时间段占用检查)

    • 业务逻辑: 用户预约某个资源(如会议室A在10:00-11:00时段)。

    • 事务A (用户1预约):

      1. BEGIN; (RC隔离级别)

      2. SELECT COUNT(*) FROM bookings WHERE room = 'A' AND start_time < '11:00' AND end_time > '10:00'; // 返回 0 (表示10:00-11:00空闲)

      3. (用户1填写预约信息,点击确认... 耗时几秒)

    • 事务B (用户2预约): (几乎与事务A同时发生,且操作更快)

      1. BEGIN; (RC隔离级别)

      2. SELECT ... // 同样返回0 (空闲)

      3. INSERT INTO bookings (room, start_time, end_time, user) VALUES ('A', '10:00', '11:00', 'user2'); // 插入预约记录

      4. COMMIT; // 用户2预约成功

    • 事务A 继续执行:

      1. (用户1点击确认)

      2. INSERT INTO bookings (room, start_time, end_time, user) VALUES ('A', '10:00', '11:00', 'user1'); // 尝试插入

      3. (可能成功也可能失败,取决于唯一性约束)

      4. COMMIT;

    • 问题:

      • 事务A和事务B都检查了同一时间段,都认为它是空闲的(SELECT返回0)。

      • 事务B更快地插入记录并提交。

      • 事务A随后也尝试插入记录。

    • 不可重复读在哪里?

      • 事务A在步骤2执行SELECT查询,得知会议室A在10:00-11:00空闲。

      • 在它执行插入操作之前,事务B已经插入并提交了占用该时间段的记录。

      • 当事务A执行插入操作时,它所依赖的“空闲”状态(SELECT的结果)已经不再成立(因为事务B的插入已提交)。事务A在逻辑判断(是否空闲)后,执行插入操作时依赖的数据状态(时间段是否被占用)已经发生了变化。如果表上有(room, start_time, end_time)的唯一约束,事务A的插入会失败(主键/唯一键冲突)。如果没有唯一约束,则会产生双重预订

    • 潜在风险:

      • 双重预订: 最严重的后果!同一个时间段被预约给了两个用户,导致冲突和用户投诉。解决双重预订通常需要更严格的并发控制,如对目标时间段加行锁(SELECT FOR UPDATE)或使用乐观锁(版本号)。

  • 可重复读(RR):适用同一事务内涉及一个以上对同一数据的查询,业务要求不能使两次查询结果不一致。

    幻读问题典型案例

    假设存在一张 goods 表,存储商品库存信息,初始数据如下:

    idnamestock
    1手机10
    2电脑5

    现在有两个并发事务:事务 A 负责查询并修改库存小于 10 的商品,事务 B 负责插入一条新的库存小于 10 的商品记录。

    步骤 1:事务 A 启动并首次查询

    事务 A 开始,执行查询 “库存小于 10 的商品”:

    -- 事务 ABEGIN;-- 第一次查询:查询库存 < 10 的商品SELECT * FROM goods WHERE stock < 10;

    此时结果为:

    idnamestock
    2电脑5
    步骤 2:事务 B 插入新数据并提交

    事务 B 启动,插入一条新商品记录(库存 8,符合 stock < 10),并提交事务:

    -- 事务 BBEGIN;-- 插入一条新商品,库存 8(符合 stock < 10)INSERT INTO goods (name, stock) VALUES ('平板', 8);COMMIT;

    此时表中数据变为:

    idnamestock
    1手机10
    2电脑5
    3平板8
    步骤 3:事务 A 再次查询并尝试修改

    事务 A 再次执行相同的查询:

    -- 事务 A-- 第二次查询:再次查询库存 < 10 的商品SELECT * FROM goods WHERE stock < 10;

    RR 隔离级别下,由于 MVCC 的可重复读特性,事务 A 第二次查询的结果仍为:

    idnamestock
    2电脑5

    但此时如果事务 A 尝试修改 “所有库存 < 10 的商品”(例如批量增加库存):

    -- 事务 A-- 尝试修改所有库存 < 10 的商品UPDATE goods SET stock = stock + 2 WHERE stock < 10;COMMIT;

    执行后,事务 A 查看最终数据时会发现:新插入的 “平板”(id=3)的库存也被修改为 10(8+2)。 这就是幻读:事务 A 两次查询都没看到 “平板”,但修改操作却影响了它,仿佛数据 “凭空出现” 并被修改。

    • 在RR级别下,不可重复读场景能被解决,但依然会出现更新操作前判断失效的情况,update是当前读会直接读取最新数据修改,依然会出现同时判断成功的超卖问题。

    • 场景一:库存扣减

      RR下的行为:

      1.事务A开始并创建快照,执行SELECT stock...读取的始终是快照中的库存值(如10)

      2.事务B开始并执行扣减库存,此时数据库中stock值为9

      3.事务A开始执行扣减库存操作UPDATE stock = stock - 1但会读取到被修改后的最新数据修改。

      结果:事务AB库存判断成功虽然解决了不可重复读问题但还是会导致超卖

      解决方法:乐观锁、悲观锁、分布式锁、库存判断加For UPDATE

      <select id="selectStockForUpdate" resultType="com.example.Goods">SELECT id, stock FROM goods WHERE id = #{id} FOR UPDATE  <!-- 关键:对查询到的行加排他锁 --></select>
  • 串行化(S):事务串行化执行,适用RR下会出现幻读且业务不允许的场景及事务必须严格按照提交顺序执行的场景。

    • 1. 💰 金融核心系统 - 银行转账

      -- 事务A: 检查A余额≥100 → A-100 → B+100-- 事务B: 检查B余额≥50  → B-50  → C+50

    风险:事务A先开启但是在未提交的情况下,事务B开启并检测B的余额,业务逻辑上B用户账户余额一定满足>=50,但是在RC,RR情况下事务A未提交所以事务B可能产生误判。

    串行化解决方案

    1. 严格顺序执行

      • 事务A完全执行后,再执行事务B

      • 或事务B完全执行后,再执行事务A

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

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

相关文章

C++高频知识点(二十三)

文章目录111. 谈谈atomic1. 什么是原子操作&#xff1f;2. std::atomic 的基本使用示例&#xff1a;基本使用3. 原子操作方法4. 内存模型与顺序一致性112. 引用成员变量是否占空间?1. 引用成员变量的定义2. 内存占用情况1. 成员变量的实际占用2. 类的总大小代码分析113. C中深…

云存储的高效安全助手:阿里云国际站 OSS

在这个数据爆炸的时代&#xff0c;数据存储和管理成为了众多企业和个人面临的一大挑战。想象一下&#xff0c;你是一位视频博主&#xff0c;随着粉丝量的增长&#xff0c;视频素材越来越多&#xff0c;电脑硬盘根本装不下&#xff0c;每次找素材都要花费大量时间。又或者你是一…

【线性基】P4301 [CQOI2013] 新Nim游戏|省选-

本文涉及知识点 C贪心 位运算、状态压缩、枚举子集汇总 线性基 P4301 [CQOI2013] 新Nim游戏 题目描述 传统的 Nim 游戏是这样的&#xff1a;有一些火柴堆&#xff0c;每堆都有若干根火柴&#xff08;不同堆的火柴数量可以不同&#xff09;。两个游戏者轮流操作&#xff0c;…

[25-cv-09610]Anderson Design Group 版权维权再出击,12 张涉案图片及近 50 个注册版权需重点排查!

Anderson 版权图案件号&#xff1a;25-cv-09610立案时间&#xff1a;2025年8月13日原告&#xff1a;Anderson Design Group, Inc.代理律所&#xff1a;Keith原告介绍原告是美国的创意设计公司&#xff0c;成立于1993年&#xff0c;简称ADG&#xff0c;一家家族企业&#xff0c;…

Mac下载AOSP源代码

一、前期准备 硬件要求 至少 200GB 可用空间(源码约 100GB,编译产物需额外空间),推荐 SSD。 内存 16GB+,避免同步 / 编译时卡顿。 系统要求 macOS 10.14+(推荐最新版本,兼容性更好) 二、环境配置 AOSP 源码包含大小写不同的文件(如 File.java 和 file.java),而 …

Linux之网络

Linux之网络两个模型应用层协议HTTPS传输层协议UDPTCP可靠性与效率的兼顾面向字节流TCP异常情况底层实现网络层协议IP网段划分子网划分NAT数据链路层协议以太网ARP代理服务器内网穿透五种IO多路复用Reactor模式本文旨在讲解tcp-ip协议原理&#xff0c;并不涉及代码部分&#xf…

MCP(模型上下文协议):是否是 AI 基础设施中缺失的标准?

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

基于粒子群优化算法优化支持向量机的数据回归预测 PSO-SVM

一、作品详细简介 1.1附件文件夹程序代码截图 全部完整源代码&#xff0c;请在个人首页置顶文章查看&#xff1a; 学行库小秘_CSDN博客​编辑https://blog.csdn.net/weixin_47760707?spm1000.2115.3001.5343 1.2各文件夹说明 1.2.1 main.m主函数文件 该代码实现了使用PSO…

版本更新!FairGuard-Mac加固工具已上线!

FairGuard-Mac加固工具1.0.2版本更新日志&#xff1a;■ 支持 AssetBundle 资源加密;■ 支持 Unity global-metadata 文件加密;AssetBundle &#xff0c;是 Unity 提供的一种资源存储压缩包。其中储存了游戏的资源&#xff0c;如图片、模型、纹理、音视频、代码等文件。AssetBu…

【Linux篇章】穿越数据迷雾:HTTPS构筑网络安全的量子级护盾,重塑数字信任帝国!

本篇摘要 本篇文章将从https是什么&#xff0c;为什么需要https角度&#xff0c;基于之前学的http[速戳速通HTTP]认识https&#xff0c;介绍什么是加密等&#xff0c;认识加密的两种方式&#xff1a;对称加密和非对称加密&#xff1b;引出五种不同的通信方加密方式外加渗透证书…

数据库:表和索引结构

表和索引是如何组织和使用的&#xff0c;在很大程度上取决于具体的关系型DBMS&#xff0c;然而它们都依赖于大致相似的结构和原则。索引页和表页表行和索引行都被存储在页中。页的大小一般为4kb&#xff0c;这是一个可以满足大部分需求的大小&#xff0c;也可以是其他大小&…

Java 学习笔记(基础篇5)

1. 综合练习(1) 抽奖public class test10 {public static void main(String[] args) {int[] arr {2,588,888,1000,10000};Random r new Random();for (int i 0; i < arr.length; i) {int randomIndex r.nextInt(arr.length);int temp arr[randomIndex];arr[randomIndex…

P1162 填涂颜色(染色法)

P1162 填涂颜色 - 洛谷 #include <bits/stdc.h> using namespace std; #define ll long long const int N 1e7 10; int n; int a[100][100],b[110][110]; int dx[4]{-1,1,0,0}; int dy[4]{0,0,1,-1}; void dfs(int x,int y) {if(x<0 || x>n1 || y<0 || y>n…

Webrtc在项目中承担的角色

一、简单划分 解决方案层:负责对SDK的对接、操作业务逻辑、UI封装、采集、渲染等,属于基础业务逻辑层 会议SDK层:负责对会议业务逻辑的封装、服务端交互、创会/加会/离会等,属于会议业务逻辑层 mediasoupclient层: 负责对webrtc封装,提供会议层面相关接口,属于webrtc业务…

Servlet上传文件

这是一个Maven项目tomcat版本&#xff1a;9.0.107pom.xml<project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.0.0 http://maven.apache.…

cocos creator 3.8 - 精品源码 -《汉中汉:汉字中的字》

cocos creator 3.8 - 精品源码 - 超级文字大师游戏介绍功能介绍免费体验下载开发环境游戏截图免费体验游戏介绍 《汉中汉&#xff1a;汉字中的字》、找汉字&#xff0c;是一款从文字中的笔画找出可以组成新汉字的小游戏。比如&#xff1a;“王”字中的笔画就可以组成&#xff…

手机端的音视频界面或者图片文档界面共享给大屏

手机端的音视频界面或者图片文档界面共享给大屏&#xff0c;可通过无线投屏和有线连接等技术手段实现&#xff0c;以下是具体介绍&#xff1a;无线投屏&#xff1a;AirPlay&#xff1a;这是苹果公司开发的无线共享协议。苹果手机可通过上滑或下拉调出控制中心&#xff0c;点击 …

Linux内存管理系统性总结

Linux内存管理系统性总结 内存管理核心架构图 #mermaid-svg-hKRdgBBYXZTiost8 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-hKRdgBBYXZTiost8 .error-icon{fill:#552222;}#mermaid-svg-hKRdgBBYXZTiost8 .error-t…

MySQL 运算符实战:9 道经典练习题解析

MySQL 运算符实战&#xff1a;9 道经典练习题解析 运算符是 MySQL 查询的 “灵魂”&#xff0c;灵活运用各类运算符能让数据筛选更加精准高效。本文通过 9 道实战练习题&#xff0c;详解逻辑运算符、比较运算符及模糊匹配的用法&#xff0c;帮你快速掌握运算符的核心应用场景。…

【R语言】更换电脑后,如何在新设备上快速下载原来设备的 R 包?

【R语言】更换电脑后&#xff0c;如何在新设备上快速下载原来设备的 R 包&#xff1f; 在日常使用 R 进行数据分析时&#xff0c;我们往往会安装很多包&#xff08;packages&#xff09;&#xff0c;一旦更换电脑&#xff0c;手动一个一个重新安装会非常麻烦。本文介绍一种简单…