MySQL基础:事务篇

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: MySQL基础:事务篇

MySQL基础:事务篇

笔记来源:

  1. 黑马程序员 MySQL数据库入门到精通
  2. 小林coding

1.事务简介

事务:是一组操作的集合,它时一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败

什么是事务

  • 在MySQL中的事务(Transaction)是由存储引擎实现的,在MySQL中,只有InnoDB存储引擎才支持事务。
  • 事务处理可以用来维护数据库的完整性,保证成批的 SQL 语句要么全部执行,要么全部不执行。
  • 事务用来管理 DDL、DML、DCL 操作,比如 insert,update,delete 语句,默认是自动提交的。

理解事务

事务相当于一些操作的集合,这些操作要么全都完成,要么全都不完成。

在银行转账时,必须保证转账绝对安全,这时需要事务参与:

update account set money = money - 200 where id = 1; 
update account set money = money + 200 where id = 2;

一个人给另一个人转账,那么转账人的金额必须减少,被转账人的金额必须增加,两件事是捆绑在一起的。

假如在第一次update之后,出现了意外、异常,没有执行第二次update,这时转账就出现了错误。

所以事务就是用来保证数据安全的一个重要举措,当发生异常时可以进行回滚,恢复已经执行的操作。

image-20220920103947159

2.事务操作

模拟转账操作

  1. 首先我们先创建一个account表。
CREATE TABLE account (
    id int PRIMARY KEY,
    `name` VARCHAR(10),
    money int 
);
INSERT into account(id,name,money) VALUES(1,'张三',2000)
INSERT into account(id,name,money) VALUES(2,'李四',2000)
  1. 实现转账操作
-- 1.查询张三账户余额
select money FROM account where name='张三';

-- 2.将张三余额-1000
UPDATE account 
SET money = money - 1000 
WHERE
    NAME = '张三'

-- 3.将李四余额+1000
UPDATE account 
SET money = money + 1000 
WHERE
    NAME = '李四'

我们先来执行一遍转账操作,执行结果:没有问题

image-20220920110340125

现在我们模拟转账过程中出现了异常的状况

  1. 先将两个人的余额恢复为2000

    UPDATE account
    set money = 2000
    WHERE name='张三' or name ='李四'
  2. 模拟异常

    -- 1.查询张三账户余额
    select money FROM account where name='张三';
    
    -- 2.将张三余额-1000
    UPDATE account 
    SET money = money - 1000 
    WHERE
        NAME = '张三';
    模拟异常
    -- 3.将李四余额+1000
    UPDATE account 
    SET money = money + 1000 
    WHERE
        NAME = '李四';
  3. 执行结果

    image-20220920110953607

此时我们就发现了异常,张三余额减了1000,而李四余额没有加上。

为什么会出现这样的结果:

因为sql语句是自动提交的,没执行一次语句就自动提交上去了,当执行发现了异常就不在执行,后面的语句也不执行,但异常前的语句执行完不会在变了。所以就导致了信息不对等的结果。这也就是为什么有事务的原因。

事务的一些操作

-- 查看事务提交方式
SELECT @@autocommit;
-- 将自动提交改为手动提交
SET @@autocommit = 0; 
-- 提交事务
COMMIT;
-- 回滚事务
ROLLBACK;

设置事务提交方式为手动提交之后,我们再次执行转账操作

-- 1.查询张三账户余额
select money FROM account where name='张三';

-- 2.将张三余额-1000
UPDATE account 
SET money = money - 1000 
WHERE
    NAME = '张三';
-- 3.将李四余额+1000
UPDATE account 
SET money = money + 1000 
WHERE
    NAME = '李四'

执行完后,我们发现account表中的数据并没有发生变化,这是因为设置手动提交之后,执行的语句只是在当前会话中,如果我们自己不执行commit操作,那执行的语句就不会提交给数据库。也就不会修改里面的内容。

我们执行commit语句。

执行完过后,我们就会发现数据已经更改了。

image-20220920112456174

现在我们再来模拟一遍转账出现异常的操作。

1.先将表中的余额都修改回2000.

UPDATE account
set money = 2000
WHERE name='张三' or name ='李四';
COMMIT;

2.模拟出错:

-- 1.查询张三账户余额
select money FROM account where name='张三';

-- 2.将张三余额-1000
UPDATE account 
SET money = money - 1000 
WHERE
    NAME = '张三';
模拟异常
-- 3.将李四余额+1000
UPDATE account 
SET money = money + 1000 
WHERE
    NAME = '李四'

此时已经发生了错误,我们就不能在进行手动提交事务,而是需要回滚事务

3.回滚事务

-- 回滚事务
ROLLBACK;

执行完之后我们就发现数据没有发生变化。

通过另一种方式开启事务:

1、开启事务:Start Transaction

  • 任何一条DML语句(insert、update、delete)执行,标志事务的开启
  • 命令:BEGIN 或 START TRANSACTION

2、提交事务:Commit Transaction

  • 成功的结束,将所有的DML语句操作历史记录和底层硬盘数据来一次同步,如果没有提交,会发现内存的数据发生改变,但硬盘内的数据不变。
  • 命令:COMMIT

3、回滚事务:Rollback Transaction

  • 失败的结束,将所有的DML语句操作历史记录全部清空
  • 命令:ROLLBACK
-- 开启事务
START TRANSACTION;
BEGIN;
-- 提交事务
COMMIT;
-- 回滚事务
ROLLBACK;

模拟转账异常

-- 改为自动提交
SET @@autocommit = 1; 

-- 开启事务
START TRANSACTION;

-- 执行转账操作

-- 1.查询张三账户余额
select money FROM account where name='张三';

-- 2.将张三余额-1000
UPDATE account 
SET money = money - 1000 
WHERE
    NAME = '张三';
模拟异常
-- 3.将李四余额+1000
UPDATE account 
SET money = money + 1000 
WHERE
    NAME = '李四';
    
-- 提交事务
COMMIT;

-- 如果转账中的任何一条出现问题,则回滚事务 ,这个要和jdbc结合才能使用
-- 回滚事务
ROLLBACK;

3. 事务四大特性

  • 原子性:事务是不可分割的最小操作单元,要么全部成功,要么全部失败。
  • 一致性:事务完成时,必须时所有的数据保持一致状态。
  • 隔离性:数据库提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行。
  • 持久性:事务一旦提交或回滚,它对数据库中的数据的改变是永久。

4.并发事务问题

  • 脏读:一个事务读到另一个事务还没有提交的数据
  • 不可重复读:一个事务先后读取同一条数据,但两次读取的数据不同。
  • 幻读:一个事务按照条件查询数据时,没有对象的数据行,但是在插入数据时,又发现这行数据已经存在,好像出现了“幻觉”。

    脏读

如果一个事务「读到」了另一个「未提交事务修改过的数据」,就意味着发生了「脏读」现象。

假设有A和B这两个事务同时在处理,事务A先开始读取金额,然后执行更新操作,此时事务A还没有提交事务,而此时正好事务B也从数据库中读取小林的余额数据,你们事务B读取到的余额是刚才事务A更新后的数据,即使没有提交事务。但事务A可能会因为出错而回滚事务。所以事务B读到的数据是过期的数据,这就是脏读

image-20220922170306334

不可重复读

在一个事务内多次读取同一个数据,如果出现前后两次读到的数据不一样的情况,就意味着发生了「不可重复读」现象。

假设又A和B两个事务在同时处理,事务A现开始从数据库中读取余额,然后继续执行代码逻辑,在这时如果事务B更新了这条数据,并提交了事务,那么当事务A再次读取该数据时,就会发现前后两次读到的数据不一致,这就叫不可重复读

image-20220922200520052

幻读

在一个事务内多次查询某个符合查询条件的「记录数量」,如果出现前后两次查询到的记录数量不一样的情况,就意味着发生了「幻读」现象。

假设有A和B两个事务同时在处理,事务A先开始从数据库查询账户余额大于100万的记录,发现共有五条,然后B也按相同的搜索条件也是查询除了5条数据记录。但是,接下来事务A插入了一条余额超过100的记录,并且提交了事务,此时事务B再次查询余额大于100万的记录,发现记录有6条。发现和前一次读到的记录数量不一样了,就感觉发生了幻觉一样,这就叫幻读

image-20220922200948159

5. 事务隔离级别

当多个事务并发执行时可能遇到「脏读、不可重复读、幻读」的现象,这些现象会对事务的一致性产生不同程序的影响。

  • 脏读:读取到其他事务未提交的数据
  • 不可重复读:前后读取的数据不一致
  • 幻读:前后读取到的记录数量不一致

这三个现象的严重性排序:

image-20220922203836406

SQL标准提出了四种隔离级别来规避这些现象,隔离级别越高,性能效率就越低,这四个隔离级别如下:

  • 读未提交:指一个事务还未提交时,它做的变更就能被其他事务看到。
  • 读提交:指一个事务提交之后,它做的改变才能被其他事务看到。
  • 可重复读:指一个事务执行过程中看到的数据,一致跟这个事务启动时看到的数据是一致的,MySQL InnoDB 引擎的默认隔离级别
  • 串行化:会对记录加上读写锁,在多个事务对这条记录进行读写操作时,如果发生了读写冲突的时候,后访问的事务必须等前一个事务执行完成之后,才能继续执行。

按隔离级别水平高低排序如下:

image-20220922204542157

对于不同的隔离级别,并发事务时可能发生的现象也会不同。

image-20220922204617333

  • 读未提交的隔离级别下,可能发生脏读、不可重复读和幻读现象。
  • 读提交的隔离级别下,可能会发生不可重复读和幻读的现象。
  • 可重复读的隔离级别下,可能会发生幻读的现象
  • 串行化的隔离级别下,这些现象都不会发生。

所以,要解决脏读现象,起码要升级到读提交以上的级别,要解决不可重复读的现象,起码要升级到可重复读的隔离级别。

但是,要解决幻读现象不建议将隔离级别升级到串行化,因为这样会导致很多事务无法并行操作,在并发事务时性能很差。

MySQL InnoDB 引擎的默认隔离级别虽然是「可重复读」,但是它很大程度上避免幻读现象,解决的方案有两种:

  • 针对快照读(普通select语句),通过MVCC(多版本并发控制)方式解决了幻读,因为可重复读隔离级别下,事务执行过程中看到的数据,一致跟这个事务启动时看到的数据是一致的,即使中途有其他事务插入了一条数据,是查询不出来这条数据的。所以很好的避免了幻读问题。
  • 针对当前读(select .. for update 等语句),是通过next-key lock (记录锁+间隙锁)方式解决了幻读,因为当执行select ... for update语句的时候,会加上next-key lock,如果有其他事务在next-key lock范围内插入了一条记录,那么这个插入语句就会被阻塞,无法成功插入,所以就很好了避免幻读问题。

接下来,举个具体的例子来说明这四种隔离级别,有一张账户余额表,里面有一条账户余额为 100 万的记录。然后有两个并发的事务,事务 A 只负责查询余额,事务 B 则会将我的余额改成 200 万,下面是按照时间顺序执行两个事务的行为:

image-20220922212312268

在不同隔离级别下,事务A执行过程中查询到的余额可能会不同:

  • 读未提交隔离级别下,事务B修改余额后,虽然没有提交事务,但是此时的余额已经可以被事务A看见了,于是事务A中余额V1的值是200万,余额V2、V3自然也是200万了。
  • 读提交隔离级别下,事务B修改余额后,但没有提交事务,所以余额V1还是100万,等事务B提交之后,最新的余额次啊能被事务A看见,因此余额V2、V3都是200万。
  • 可重复读隔离级别下,事务A只能看见启动事务时的数据,所以V1,V2的值都是100万,当事务A提交完食物后,就能看见最新的余额数据了,所以余额V3的值时200万。
  • 串行化隔离级别下,事务B在执行将余额100万修改未200万时,由于此前事务A执行了读操作,这样就发生了读写冲突,于是就会被锁住,直到事务A提交后,事务B才能执行。所以从A的角度来看,余额V1、V2都是100万,而V3的值是200万。

接下来我们用sql语句具体实现这些隔离级别

-- 查看事务隔离级别
-- 版本低的用:
select @@tx_isolation;
-- 版本高的用:
SELECT @@TRANSACTION_isolation;

-- 设置隔离级别
set SESSION TRANSACTION ISOLATION LEVEL read UNCOMMITTED;

我们用两个命令行窗口来演示两个并发事务。

读未提交

image-20220923000937958

读提交

image-20220923001304820

可重复读

image-20220923001953459

串行化

image-20220923094354457

image-20220923094458676

事务的隔离级别-操作

-- 查看隔离级别 
show variables like '%isolation%';

-- 设置隔离级别
set session transaction isolation level 级别字符串
级别字符串:read uncommitted、read committed、repeatable read、serializable

-- 设置read uncommitted
set session transaction isolation level read uncommitted;
 
-- 设置read committed
set session transaction isolation level read committed;
 
-- 设置repeatable read
set session transaction isolation level repeatable read;
 
-- 设置serializable
set session transaction isolation level serializable;
相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
2月前
|
存储 SQL 关系型数据库
MySQL的事务隔离级别
【10月更文挑战第17天】MySQL的事务隔离级别
100 43
|
2月前
|
存储 缓存 关系型数据库
MySQL事务日志-Redo Log工作原理分析
事务的隔离性和原子性分别通过锁和事务日志实现,而持久性则依赖于事务日志中的`Redo Log`。在MySQL中,`Redo Log`确保已提交事务的数据能持久保存,即使系统崩溃也能通过重做日志恢复数据。其工作原理是记录数据在内存中的更改,待事务提交时写入磁盘。此外,`Redo Log`采用简单的物理日志格式和高效的顺序IO,确保快速提交。通过不同的落盘策略,可在性能和安全性之间做出权衡。
1646 14
|
3月前
|
存储 Oracle 关系型数据库
Oracle和MySQL有哪些区别?从基本特性、技术选型、字段类型、事务、语句等角度详细对比Oracle和MySQL
从基本特性、技术选型、字段类型、事务提交方式、SQL语句、分页方法等方面对比Oracle和MySQL的区别。
548 18
Oracle和MySQL有哪些区别?从基本特性、技术选型、字段类型、事务、语句等角度详细对比Oracle和MySQL
|
2月前
|
SQL 关系型数据库 MySQL
阿里面试:MYSQL 事务ACID,底层原理是什么? 具体是如何实现的?
尼恩,一位40岁的资深架构师,通过其丰富的经验和深厚的技術功底,为众多读者提供了宝贵的面试指导和技术分享。在他的读者交流群中,许多小伙伴获得了来自一线互联网企业的面试机会,并成功应对了诸如事务ACID特性实现、MVCC等相关面试题。尼恩特别整理了这些常见面试题的系统化解答,形成了《MVCC 学习圣经:一次穿透MYSQL MVCC》PDF文档,旨在帮助大家在面试中展示出扎实的技术功底,提高面试成功率。此外,他还编写了《尼恩Java面试宝典》等资料,涵盖了大量面试题和答案,帮助读者全面提升技术面试的表现。这些资料不仅内容详实,而且持续更新,是求职者备战技术面试的宝贵资源。
阿里面试:MYSQL 事务ACID,底层原理是什么? 具体是如何实现的?
|
3月前
|
SQL 关系型数据库 MySQL
MySQL基础:事务
本文详细介绍了数据库事务的概念及操作,包括事务的定义、开启、提交与回滚。事务作为一组不可分割的操作集合,确保了数据的一致性和完整性。文章还探讨了事务的四大特性(原子性、一致性、隔离性、持久性),并分析了并发事务可能引发的问题及其解决方案,如脏读、不可重复读和幻读。最后,详细讲解了不同事务隔离级别的特点和应用场景。
149 4
MySQL基础:事务
|
2月前
|
SQL 关系型数据库 MySQL
【MySQL】索引和事务
【MySQL】索引和事务
55 0
|
3月前
|
SQL Oracle 关系型数据库
详解 MySQL 的事务以及隔离级别
详解 MySQL 的事务以及隔离级别
43 0
|
4月前
|
API C# 开发框架
WPF与Web服务集成大揭秘:手把手教你调用RESTful API,客户端与服务器端优劣对比全解析!
【8月更文挑战第31天】在现代软件开发中,WPF 和 Web 服务各具特色。WPF 以其出色的界面展示能力受到欢迎,而 Web 服务则凭借跨平台和易维护性在互联网应用中占有一席之地。本文探讨了 WPF 如何通过 HttpClient 类调用 RESTful API,并展示了基于 ASP.NET Core 的 Web 服务如何实现同样的功能。通过对比分析,揭示了两者各自的优缺点:WPF 客户端直接处理数据,减轻服务器负担,但需处理网络异常;Web 服务则能利用服务器端功能如缓存和权限验证,但可能增加服务器负载。希望本文能帮助开发者根据具体需求选择合适的技术方案。
168 0
|
4月前
|
C# Windows 监控
WPF应用跨界成长秘籍:深度揭秘如何与Windows服务完美交互,扩展功能无界限!
【8月更文挑战第31天】WPF(Windows Presentation Foundation)是 .NET 框架下的图形界面技术,具有丰富的界面设计和灵活的客户端功能。在某些场景下,WPF 应用需与 Windows 服务交互以实现后台任务处理、系统监控等功能。本文探讨了两者交互的方法,并通过示例代码展示了如何扩展 WPF 应用的功能。首先介绍了 Windows 服务的基础知识,然后阐述了创建 Windows 服务、设计通信接口及 WPF 客户端调用服务的具体步骤。通过合理的交互设计,WPF 应用可获得更强的后台处理能力和系统级操作权限,提升应用的整体性能。
109 0
|
4月前
|
存储 关系型数据库 MySQL
MySQL 中的事务存储引擎深入解析
【8月更文挑战第31天】
57 0

热门文章

最新文章

下一篇
无影云桌面