MYSQL-初级-事务篇

news2024/11/15 19:50:58

目录

    • 概述
      • 为什么有事务?
    • 事务操作
    • 事务的四大特性(AICD)
      • 原子性(Atomicity)
      • 一致性(Consistency)
      • 隔离性(Isolation)
      • 持久性(Durability)
    • 并发事务问题
      • 脏读(Dirty Read)
      • 不可重复读
      • 幻读
    • 事务隔离级别
      • 查看事务隔离级别
      • 查看当前 Session 的事务隔离级别
      • 设置全局事务隔离级别
    • 证明
      • 解决脏读问题
        • 模拟场景
        • 解决
      • 解决不可重复读
        • 模拟场景
        • 解决
      • 幻读
        • 模拟场景
        • 解决
    • Reference

概述

事务是一组操作集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起想系统提交或撤销请求,这么操作要么全部执行成功,要么全部失效

为什么有事务?

假设有一个银行数据库,张三向李四转账 1000 元。
image.png
这个转账操作涉及到两个步骤:

  1. 从张三的账户中扣除1000元。
  2. 在李四的账户中增加1000元。

如果没有事务,这两个步骤可能会分开执行,可能会出现以下问题:

  1. 第一步执行成功,张三 的账户减少了1000 元,但是第二步执行失败,李四 的账户没有增加1000 元。这样会导致数据不一致,张三 的钱消失了。
  2. 如果第一步执行失败,张三 的账户没有减少1000元,但是第二步执行成功,李四 的账户增加了1000元。这样也会导致数据不一致,李四 无缘无故多了1000 元。

解决办法:
使用事务可以解决这个问题,将上述两个步骤放在一个事务中。

  1. 如果第一步执行成功而第二步执行失败,那么整个事务会回滚,即第一步的操作也会被撤销,张三 的账户不会减少1000元。
  2. 同样,如果第一步执行失败,整个事务也会回滚,不会执行第二步。这样就可以确保数据的一致性。

事务操作

  1. 查看/设置事务提交方式
SHOW VARIABLES LIKE 'autocommit';

Variable_name|Value|
-------------+-----+
autocommit   |ON   |

-- 设置自动提交事务
SET autocommit = ON;
-- 设置手动提交事务
SET autocommit = OFF;
  1. 开启事务
-- 开始事务
BEGIN;

-- 执行一系列操作
UPDATE account SET balance = balance - 1000 WHERE account_id = 1;
UPDATE account SET balance = balance + 1000 WHERE account_id = 2;

-- 提交事务
COMMIT;

-- 如果发生错误,可以回滚事务
-- ROLLBACK;

事务的四大特性(AICD)

事务的四大特性,通常被称为ACID特性,是数据库管理系统保证数据一致性和可靠性的基础。这些特性分别是:

原子性(Atomicity)

事务中的所有操作要么全部完成,要么全部不完成,不会处于中间状态。如果事务在执行过程中发生错误,系统会回滚到事务开始前的状态,就像事务从未执行过一样。

  1. 从张三的账户中扣除1000元。
  2. 在李四的账户中增加1000元。

两个步骤要么全部完成,要么全部失效。

一致性(Consistency)

事务必须使数据库从一个一致性状态转移到另一个一致性状态。这意味着事务执行结束后,数据库的完整性约束没有被破坏,数据保持一致。

转账后,张三和李四的账户总额 == 转账前,张三和李四的账户总额。

隔离性(Isolation)

事务的隔离性意味着一个事务的执行不应该受到其他事务的干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。

持久性(Durability)

一旦事务被提交,它对数据库的更改就是永久性的。即使系统发生崩溃,事务的结果也不会丢失,因为它们已经被持久化到数据库中。
这些特性共同保证了数据库事务的可靠性,即使在并发访问和系统故障的情况下,也能保持数据的一致性和完整性。

完成转账后,张三和李四的账户数据被持久存储到磁盘。


并发事务问题

脏读(Dirty Read)

一个事务读取了另一个未提交事务修改过的数据。如果这个未提交的事务最终回滚,那么第一个事务读取的数据就是无效的。
image.png

  1. bilibli 开启事务 A,查看了张三的账户余额还有 2000 元,自动扣款年度大会员 200 元。
  2. 张三开启事务 B,查看账户余额还有 1800 元。
  3. 此时,bilibili 发生异常,自动口矿失败,事务回滚。
  4. 张三之前查看的账户余额数据就是无效的。

假设我们已经解决了脏读的问题。

不可重复读

事务 A 两次读取同一行数据,但在两次读取之间,事务 B 修改了这行数据。因此,事务 A 在两次读取中得到了不同的结果。
主要针对 updatedelete 操作。
image.png

  1. 张三开启事务 A,第一次查看账户余额 2000 元。
  2. bilibili 开启事务 B 自动扣款年费大会员 200 元,并执行了 commit成功提交了事务。
  3. 张三再次读取余额,发现账户余额只有 1800 元,两次查看发现余额不同。

幻读

事务 A 在数据库中执行查询两次,但在两次查询之间,事务 B 插入了一些满足查询条件的新行。因此,事务 A 在两次查询中得到了不同的结果集。
主要针对 insert 操作。
image.png

  1. bilibli 管理员开启事务 A,查询用户账号信息,发现没有 Boss 的账户。
  2. 此时,Boss 开启事务 B,并成功注册了账户。
  3. 此时 bilibli 管理员想新增 Boss 账户时,发现新增失败。
  4. bilibili 管理员再次查询用户的账号信息,任然没有 Boss 的账户。

bilibli 管理员两次查询的用户数据不同,即幻读


事务隔离级别

隔离级别脏读不可重复读幻读
Read uncommitted(未提交读)
Read committed(已提交读)
Repeatable Read**(默认)**
Serializable

查看事务隔离级别

SELECT @@tx_isolation;
-- 或者(在MySQL 5.7.20及更高版本中)
SELECT @@transaction_isolation;

|@@transaction_isolation|
|-----------------------+
|REPEATABLE-READ        |

查看当前 Session 的事务隔离级别

  1. 将事务隔离级别设置为READ UNCOMMITTED
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
  1. 将事务隔离级别设置为READ COMMITTED
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
  1. 将事务隔离级别设置为REPEATABLE READ
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
  1. 将事务隔离级别设置为SERIALIZABLE
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;

设置全局事务隔离级别

  1. 讲事务隔离级别设置为READ UNCOMMITED
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
  1. 将事务隔离级别设置为READ COMMITTED
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
  1. 将事务隔离级别设置为REPEATABLE READ
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;
  1. 将事务隔离级别设置为SERIALIZABLE
SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE;

证明

现在有账户余额表:

id|name|salary|
--+----+------+
 1|张三  |  2000|

解决脏读问题

模拟场景
-- bilibli开启事务                           
START TRANSACTION;                       

-- 1. 查看张三用户余额                         
SELECT * FROM test_table tt;
|id|name|salary|
|--+----+------+
| 1|张三  |  2000|

-- 2. bilibli自动扣款
UPDATE test_table tt
SET tt.salary = tt.salary - 200
WHERE name = '张三'; 
                                                -- 设置Session隔离级别为READ UNCOMMITTED
                                                SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
                                                -- 张三开启事务 
                                                START TRANSACTION;        
                                                -- 1. 张三查看账户余额
                                                SELECT * FROM test_table tt;
                                                |id|name|salary|
                                                |--+----+------+
                                                | 1|张三|1800  |    

-- 3. 自动扣款发生异常,回滚事务
ROLLBACK;    

|id|name|salary|
|--+----+------+
| 1|张三  |  2000|
  1. 张三查询的账户余额并不是真实余额,发生了脏读
解决
  1. 将 MYSQL 默认的隔离级别设置为 READ COMMITED
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
  1. 执行同样的操作
START TRANSACTION;
SELECT * FROM test_table tt;
COMMIT;

// output
|id|name|salary|
|--+----+------+
| 1|张三  |  2000|

解决不可重复读

模拟场景
-- 设置Session隔离级别为 READ COMMITTED
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

-- 1. 张三开启事务
START TRANSACTION;   
-- 2. 张三查看余额   
SELECT * FROM test_table tt;
|id|name|salary|
|--+----+------+
| 1|张三  |  2000|

                                                            -- 3. bilibli开启事务                           
                                                            START TRANSACTION;                       
                                                            -- 4. bilibli自动扣款
                                                            UPDATE test_table tt
                                                            SET tt.salary = tt.salary - 200
                                                            WHERE name = '张三'; 
                                                            -- 5. 提交事务
                                                            COMMIT;
-- 6. 张三再次查看余额                                        
SELECT * FROM test_table tt;
|id|name|salary|
|--+----+------+
| 1|张三  |  1800|

-- 7. 提交事务
COMMIT;
  1. 张三两次查询的账户余额不同,发生了不可重复读
解决
  1. 将 MYSQL 默认的隔离级别设置为 REPEATABLE READ
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;
  1. 执行同样的操作
-- 1. 张三开启事务
START TRANSACTION;   
-- 2. 张三查看余额   
SELECT * FROM test_table tt;
|id|name|salary|
|--+----+------+
| 1|张三  |  2000|

                                                            -- 3. bilibli开启事务                           
                                                            START TRANSACTION;                       
                                                            -- 4. bilibli自动扣款
                                                            UPDATE test_table tt
                                                            SET tt.salary = tt.salary - 200
                                                            WHERE name = '张三'; 
                                                            -- 5. 提交事务
                                                            COMMIT;
-- 6. 张三再次查看余额                                        
SELECT * FROM test_table tt;
|id|name|salary|
|--+----+------+
| 1|张三  |  2000|

-- 7. 提交事务
COMMIT;

幻读

模拟场景
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
-- 1.  bilibli 管理员开启事务
START TRANSACTION;   
-- 2. bilibli 管理员查看账号信息
SELECT * FROM test_table tt;
|id|name|salary|
|--+----+------+
| 1|张三  |  2000|
                                                            SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
                                                            -- 3.Boss 开启事务                           
                                                            START TRANSACTION;                       
                                                            -- 4. Boss 注册bilibli账号
                                                            INSERT INTO test_table (id,name,salary)
                                                            VALUES(2,'Boss',100000);
                                                            -- 5. 提交事务
                                                            COMMIT;
-- 6. bilibli 管理员新增 Boss 账号                                    
INSERT INTO test_table (id,name,salary)
VALUES(2,'Boss',100000);

// 发生报错,主键冲突,新增失败
// SQL 错误 [1062] [23000]: Duplicate entry '2' for key 'test_table.PRIMARY'

-- 7. bilibli 管理员再次查看账号信息,还是没有
SELECT * FROM test_table tt;
|id|name|salary|
|--+----+------+
| 1|张三  |  2000|
  1. bilibli 管理员新增失败后,再次查看,还是没有 Boss 的账号。
解决
  1. 将 MYSQL 默认的隔离级别设置为 SERIALIZABLE
SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE;
  1. 再次执行
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
-- 1.  bilibli 管理员开启事务
START TRANSACTION;   
-- 2. bilibli 管理员查看账号信息
SELECT * FROM test_table tt;
|id|name|salary|
|--+----+------+
| 1|张三  |  2000|
                                                            SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
                                                            -- 3.Boss 开启事务                           
                                                            START TRANSACTION;                       
                                                            -- 4. Boss 注册bilibli账号
                                                            INSERT INTO test_table (id,name,salary)
                                                            VALUES(2,'Boss',100000);

                                                            // 发生阻塞,无法新增数据。。。。。。。。。。
                                                      
-- 5. bilibli 管理员新增 Boss 账号                                    
INSERT INTO test_table (id,name,salary)
VALUES(2,'Boss',100000);

-- 6. bilibli 管理员再次查看账号
SELECT * FROM test_table tt;
|id|name|salary|
|--+----+------+
| 1|张三  |  1800|
| 2|Boss|100000|

                                                            // 结束阻塞,新增报错 

Reference

  1. 黑马程序员 MySQL数据库入门到精通,从mysql安装到mysql高级、mysql优化全囊括

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

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

相关文章

为虚幻引擎C++项目设置VS开发环境

为虚幻引擎C项目设置VS开发环境 虚幻引擎(简称UE) 能与 Visual Studio(简称VS) 完美结合,使你能够快速、简单地改写项目代码,并能即刻查看编译结果。设置Visual Studio以使用虚幻引擎能提高开发者对虚幻引…

rust 桌面 sip 软电话(基于tauri 、pjsip库)

本文尝试下rust 的tauri 桌面运用 原因在于体积小 1、pjsip 提供了rust 接口官方的 rust demo 没编译出来 在git找了个sip-phone-rs-master https://github.com/Charles-Schleich/sip-phone-rs 可以自己编译下pjsip lib库替换该项目的lib 2、创建一个tauri demo 引用 [depe…

操作系统(4)——文件系统

目录 小程一言文件系统管理基础概念&功能基本概念文件的结构和属性文件的操作文件的安全性和权限控制文件系统的实现和分配方式 问题&解答1、文件系统在操作系统中起到什么作用?2、文件的逻辑结构和物理结构有何区别?3、如何理解文件权限控制在操…

时间序列异常值检验替换——基于Hampel滤波器

Hampel滤波器作为一种强大的时间序列异常值处理工具,在数据清洗和分析中发挥着重要作用。本文将深入研究Hampel滤波器的原理和数学推导,并通过实际代码演示其在异常值处理中的应用 1. Hampel滤波器简介 1.1 什么是Hampel滤波器? Hampel滤波…

【达梦数据库】通过线程pid定位会话SQL

【达梦数据库】通过线程pid定位会话SQL 1、查找数据库进程 ps -ef|grep dmserver2、通过进程pid去找对应的线程 top -H -p $pid -------------------- top命令经常用来监控linux的系统状况,是常用的性能分析工具,能够实时显示系统中各个进程的资源占用…

Signac包-1.Analyzing PBMC scATAC-seq

–https://stuartlab.org/signac/articles/pbmc_vignette 好的,开始学习scATAC-seq的数据是怎么玩的了,先跑完Signac的教程,边跑边思考怎么跟自己的课题相结合。 留意更多内容,欢迎关注微信公众号:组学之心 数据和R…

Qt安卓开发的一些概念

目录 1、Android 版本和 API 的对应关系? 2、ABI是什么 2.1、x86_64 2.2、x86 2.3、arm64-v8a 2.4、armeabi-v7a 3、不同架构的特点 3.1、32位 ARM 架构 (ARMv7) 3.2、64位 ARM 架构 (ARMv8-A) 3.3、32位 Intel 架构 (x86) 3.4、64位 Intel 架构 (x86-64…

vue+element-ui的列表查询条件/筛选条件太多以下拉选择方式动态添加条件(支持全选、反选、清空)

1、此功能已集成到TQueryCondition组件中 2、最终效果 3、具体源码(新增moreChoose.vue) <template><el-popoverpopper-class"t_query_condition_more":bind"popoverAttrsBind"ref"popover"v-if"allcheckList.length>0"…

本地VSCode连接远程linux环境服务器的docker

目录 1、安装远程SSH 2、连接远程主机 3、远程中安装docker 4、查看容器 &#xff08;1&#xff09;直接查看容器和镜像 &#xff08;2&#xff09;使用命令查看 最近在新服务器中执行程序&#xff0c;要用到远程的docker。但是命令行环境下查看代码非常不方便&#xff0…

upload-labs 1-19关 攻略 附带项目下载地址 小白也能看会

本文章提供的工具、教程、学习路线等均为原创或互联网收集&#xff0c;旨在提高网络安全技术水平为目的&#xff0c;只做技术研究&#xff0c;谨遵守国家相关法律法规&#xff0c;请勿用于违法用途&#xff0c;如有侵权请联系小编处理。 环境准备&#xff1a; 1.靶场搭建 下…

Nsight System and Nsight Compute 性能分析工具

Nsight System 系统级别去分析性能&#xff0c;也就是宏观方向。 Achieved Accupacy低&#xff1a;在Nsight System pipline可以直接看出来 kernel launch 延迟&#xff1a;cpu发起gpu执行kernel过程中&#xff0c;有个kernel launch环节&#xff0c;grid block。优化方法&a…

ScrollView(滚动条)

1.滚动到底部&#xff1a; 我们可以直接利用ScrollView给我们提供的:fullScroll()方法&#xff1a; scrollView.fullScroll(ScrollView.FOCUS_DOWN);滚动到底部 scrollView.fullScroll(ScrollView.FOCUS_UP);滚动到顶部 另外用这玩意的时候要小心异步的玩意&#xff0c;就是a…

iOS 18:照片应用添加“恢复”相册,可恢复数据库损坏所丢失照片!

小伙伴们可能在使用 iPhone 拍摄时遇到“明明拍下了&#xff0c;但是相册中却根本没有相关照片”的问题&#xff0c;而在 iOS 18 / macOS 15 中&#xff0c;苹果公司悄悄为“照片”应用引入了一项“恢复”功能以解决相关 Bug。 按照苹果的描述&#xff0c;照片、视频丢失可能是…

XXE漏洞复现

XML外部实体注入(XML Extenrnal Entity Injection)&#xff0c;简称XXE漏洞。引发XXE漏洞的主要原因是XML解析依赖库libxml默认开启了对外部实体的引用&#xff0c;导致服务端在解析用户提交的XML信息时未作处理直接进行解析&#xff0c;导致加载恶意的外部文件和代码&#xff…

一文剖析高可用向量数据库的本质

面对因电力故障、网络问题或人为操作失误等导致的服务中断&#xff0c;数据库系统高可用能够保证系统在这些情况下仍然不间断地提供服务。如果数据库系统不具备高可用性&#xff0c;那么系统就需要承担停机和数据丢失等重大风险&#xff0c;而这些风险极有可能造成用户流失&…

SpringBoot通过3种方式实现AOP切面

❃博主首页 &#xff1a; 「码到三十五」 &#xff0c;同名公众号 :「码到三十五」&#xff0c;wx号 : 「liwu0213」 ☠博主专栏 &#xff1a; <mysql高手> <elasticsearch高手> <源码解读> <java核心> <面试攻关> ♝博主的话 &#xff1a…

实战内测-某内测项目站点FUZZ到Sql注入

0x1 前言 下面给师傅们分享的案例呢是前段时间实战的一个站点&#xff0c;也是我朋友前段时间让我测的一个站点。整体的测试流程也还算ok&#xff0c;然后里面有些细节要是对师傅们有帮助可以收藏下&#xff0c;后面主要是利用FUZZ打了一个sql注入漏洞上去。 0x2 fuzz和sql结…

MAC安装acl

在编译前&#xff0c;需要先从 github https://github.com/acl-dev/acl 下载源码&#xff0c;国内用户可以选择从 gitee https://gitee.com/acl-dev/acl 下载源码。 3.1、Linux/Unix 平台上编译安装 在 Linux/Unix 平台上的编译非常简单&#xff0c;可以选择使用 make 方式或 …

【高校科研前沿】马里兰大学地理科学系在环境科学Top期刊发文:美国本土湿地因不透水面而损失的热点区域

1.文章简介 论文名称&#xff1a;Hotspots of wetland loss to impervious surfaces in the conterminous United States&#xff08;美国本土湿地因不透水面而损失的热点区域&#xff09; 第一作者及单位&#xff1a;Zhenhua Zou&#xff08;马里兰大学(美国)|助理研究教授&a…

Widget自定义动画按钮实战(鼠标进入则放大,离开恢复)

目录 引言 准备工作 步骤一&#xff1a;创建项目和基础控件 步骤二&#xff1a;设计UI 步骤三&#xff1a;自定义按钮类&#xff08;AniBtn&#xff09; 步骤四&#xff1a;在主窗口中使用自定义按钮 步骤五&#xff1a;编译和运行 总结 引言 在Qt开发中&#xff0c;自…