MySQL基础:事务篇

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL DuckDB 分析主实例,集群系列 8核16GB
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;
相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。   相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情: https://www.aliyun.com/product/rds/mysql 
相关文章
|
3月前
|
SQL 关系型数据库 MySQL
MySQL锁机制:并发控制与事务隔离
本文深入解析了MySQL的锁机制与事务隔离级别,涵盖锁类型、兼容性、死锁处理及性能优化策略,助你掌握高并发场景下的数据库并发控制核心技巧。
|
4月前
|
存储 监控 Oracle
MySQL事务
MySQL事务具有ACID特性,包括原子性、一致性、隔离性和持久性。其默认隔离级别为可重复读,通过MVCC和间隙锁解决幻读问题,确保事务间数据的一致性和并发性。
MySQL事务
|
5月前
|
存储 SQL 关系型数据库
mysql底层原理:索引、慢查询、 sql优化、事务、隔离级别、MVCC、redolog、undolog(图解+秒懂+史上最全)
mysql底层原理:索引、慢查询、 sql优化、事务、隔离级别、MVCC、redolog、undolog(图解+秒懂+史上最全)
mysql底层原理:索引、慢查询、 sql优化、事务、隔离级别、MVCC、redolog、undolog(图解+秒懂+史上最全)
|
2月前
|
关系型数据库 MySQL 数据库
【赵渝强老师】MySQL的事务隔离级别
数据库并发访问时易引发数据不一致问题。如客户端读取到未提交的事务数据,可能导致“脏读”。MySQL通过四种事务隔离级别(读未提交、读已提交、可重复读、可序列化)控制并发行为,默认为“可重复读”,以平衡性能与数据一致性。
261 0
|
3月前
|
关系型数据库 MySQL 数据库
MySql事务以及事务的四大特性
事务是数据库操作的基本单元,具有ACID四大特性:原子性、一致性、隔离性、持久性。它确保数据的正确性与完整性。并发事务可能引发脏读、不可重复读、幻读等问题,数据库通过不同隔离级别(如读未提交、读已提交、可重复读、串行化)加以解决。MySQL默认使用可重复读级别。高隔离级别虽能更好处理并发问题,但会降低性能。
159 0
|
5月前
|
安全 关系型数据库 MySQL
mysql事务隔离级别
事务隔离级别用于解决脏读、不可重复读和幻读问题。不同级别在安全与性能间权衡,如SERIALIZABLE最安全但性能差,READ_UNCOMMITTED性能高但易导致数据不一致。了解各级别特性有助于合理选择以平衡并发性与数据一致性需求。
185 1
|
12月前
|
SQL 安全 关系型数据库
【MySQL基础篇】事务(事务操作、事务四大特性、并发事务问题、事务隔离级别)
事务是MySQL中一组不可分割的操作集合,确保所有操作要么全部成功,要么全部失败。本文利用SQL演示并总结了事务操作、事务四大特性、并发事务问题、事务隔离级别。
4919 56
【MySQL基础篇】事务(事务操作、事务四大特性、并发事务问题、事务隔离级别)
|
11月前
|
SQL 关系型数据库 MySQL
MySQL事务日志-Undo Log工作原理分析
事务的持久性是交由Redo Log来保证,原子性则是交由Undo Log来保证。如果事务中的SQL执行到一半出现错误,需要把前面已经执行过的SQL撤销以达到原子性的目的,这个过程也叫做"回滚",所以Undo Log也叫回滚日志。
566 7
MySQL事务日志-Undo Log工作原理分析
|
12月前
|
SQL 关系型数据库 MySQL
MySQL进阶突击系列(04)事务隔离级别、AICD、CAP、BASE原则一直搞不懂? | 看这篇就够了
本文详细介绍了数据库事务的四大特性(AICD原则),包括原子性、隔离性、一致性和持久性,并深入探讨了事务并发问题与隔离级别。同时,文章还讲解了分布式系统中的CAP理论及其不可能三角关系,以及BASE原则在分布式系统设计中的应用。通过具体案例和图解,帮助读者理解事务处理的核心概念和最佳实践,为应对相关技术面试提供了全面的知识准备。
|
关系型数据库 MySQL 数据库
MySQL事务隔离级别及默认隔离级别的设置
在数据库系统中,事务隔离级别是一个关键的概念,它决定了事务在并发执行时如何相互隔离。MySQL提供了四种事务隔离级别,每种级别都解决了不同的并发问题。本文将详细介绍这些隔离级别以及MySQL的默认隔离级别。

推荐镜像

更多