MySQL事务

发布于:2025-02-26 ⋅ 阅读:(18) ⋅ 点赞:(0)

MySQL系列:

初识MySQLMySQL常用数据类型和表的操作增删改查(CRUD)操作(总),数据库约束数据库设计SQL联合查询MySQL视图MySQL索引

1.了解事务

在MySQL中,事务是指一组操作的集合,这些操作要么全部成功执行,要么全部不执行。事务的主要目的是确保数据库操作的完整性和一致性。事务通常用于处理多个相关的数据库操作,以确保在发生错误或故障时,数据库能够保持一致的状态。

create table t_account(
id bigint primary key auto_increment,
name varchar(50) not null,
balance decimal(12,2)not null 
);
insert into t_account values(1,'张三',1000),(2,'李四',1000);

select * from t_account;
#张三余额减少100
update t_account set balance=balance-100 where name='张三';
#李四余额增加100
update t_account set balance=balance+100 where name='李四';

在这里插入图片描述
• 如果转账成功,应该有以下结果:

  1. 张三的账⼾余额减少 100 ,变成 900 ,李四的账⼾余额增加了 100 ,变成 1100 ,不能出现张
    三的余额减少⽽李四的余额没有增加的情况;
  2. 张三和李四在发⽣转账前后的总额不变,也就是说转账前张三和李四的余额总数为
    1000+1000=2000 ,转账后他们的余额总数为 900+1100=2000 ;
  3. 转账后的余额结果应当保存到存储介质中,以便以后读取;
  4. 还有⼀点需要要注意,在转账的处理过程中张三和李四的余额不能因其他的转账事件⽽受到⼲扰;
    以上这四点在事务的整个执⾏过程中必须要得到保证,这也就是事务的 ACID 特性

2.自动/手动创建事务

默认情况下,MySQL是⾃动提交事务的,也就是说我们执⾏的每个修改操作,⽐如插⼊、更新和删
除,都会⾃动开启⼀个事务并在语句执⾏完成之后⾃动提交,发⽣异常时⾃动回滚。
• 查看当前事务是否⾃动提交可以使⽤以下语句

show variables like 'autocommit';

在这里插入图片描述
通过以下语句可以设置事务为手动/自动

#自动:
set autocommit=1;
set autocommit=on;
#手动
set autocommit=0;
set autocommit=off;

在这里插入图片描述

3.使用事务

3.1查看存储引擎

要使⽤事务那么数据库就要⽀持事务,在MySQL中⽀持事务的存储引擎是InnoDB,可以通过
show engines; 语句查看:
在这里插入图片描述

3.2 语法

#开始一个新的事务:
start transaction; 
begin;
#提交当前事务,并对更改持久化保存
commit;
#回滚当前事务,取消更改
rollback;

START TRANSACTION 或 BEGIN 开始⼀个新的事务;
• COMMIT 提交当前事务,并对更改持久化保存;
• ROLLBACK 回滚当前事务,取消其更改;
• ⽆论提交还是回滚,事务都会关闭

3.3开始一个事务后进行回滚

beginl;
update t_account set balance=balance+100 where name='张三';
select * from t_account;
rollback;
select * from t_account;

在这里插入图片描述

3.4设置保存点

start transaction;
update t_account set balance=balance-100 where name='张三';
update t_account set balance=balance+100 where name='李四';
savepoint savepoint1;
select * from t_account;

在这里插入图片描述

update t_account set balance=balance-100 where name='张三';
update t_account set balance=balance+100 where name='李四';
savepoint savepoint2;
select * from t_account;

在这里插入图片描述

update t_account set balance=0 where name='张三';
update t_account set balance=0 where name='李四';
savepoint savepoint3;
select * from t_account;

在这里插入图片描述

rollback to savepoint3;
select * from t_account;
rollback to savepoint1;
select * from t_account;
rollback to savepoint2;
select * from t_account;

在这里插入图片描述

重新创建后再观察:

rollback to savepoint3;
select * from t_account;
rollback to savepoint2;
select * from t_account;
rollback to savepoint1;
select * from t_account;

在这里插入图片描述

rollback;
select * from t_account;
commit;

在这里插入图片描述
注意:
只要使⽤ START TRANSACTION 或 BEGIN 开启事务,必须要通过 COMMIT 提交才会持久
化,与是否设置 SET autocommit ⽆关。
• ⼿动提交模式下,不⽤显⽰开启事务,执⾏修改操作后,提交或回滚事务时直接使⽤ commit
或 rollback
• 已提交的事务不能回滚

4.事务的隔离性和隔离级别

4.1什么是隔离性

MySQL服务可以同时被多个客⼾端访问,每个客⼾端执⾏的DML语句以事务为基本单位,那么不同的客⼾端在对同⼀张表中的同⼀条数据进⾏修改的时候就可能出现相互影响的情况,为了保证不同的事务之间在执⾏的过程中不受影响,那么事务之间就需要要相互隔离,这种特性就是隔离性。

4.2 隔离级别

事务具有隔离性,那么如何实现事务之间的隔离?隔离到什么程度?如何保证数据安全的同时也要兼顾性能?这都是要思考的问题。事务间不同程度的隔离,称为事务的隔离级别;不同的隔离级别在性能和安全⽅⾯做了取舍,有的隔离级别注重并发性,有的注重安全性,有的则是并发和安全适中;在MySQL的InnoDB引擎中事务
的隔离级别有四种,分别是:
• READ UNCOMMITTED ,读未提交
• READ COMMITTED ,读已提交
• REPEATABLE READ ,可重复读(默认)
• SERIALIZABLE ,串⾏化

4.3查看和设置隔离级别

事务的隔离级别分为全局作用域和会话作用域
,查看不同作用域事务隔离级别
在这里插入图片描述

# 设置全局事务隔离级别为串⾏化,后续所有事务⽣效,不影响当前事务
SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE;
# 设置会话事务隔离级别为串⾏化,当前会话后续的所有事务⽣效,不影响当前事务,可以在任何时候执行
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
# 如果不指定任何作⽤域,设置只针对下⼀个事务,随后的事务恢复之前的隔离级别
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

在这里插入图片描述

set globl transaction_isolation='read-committed';
select @@GLOBAL.transaction_isolation;
set session transaction_isolation='read-committed';
select @@session.transaction_isolation;

在这里插入图片描述

5.不同隔离级别存在的问题

5.1 READ UNCOMMITTED - 读未提交与脏读

存在问题:
出现在事务的 READ UNCOMMITTED 隔离级别下,由于在读取数据时不做任何限制,所以并发性能很⾼,但是会出现⼤量的数据安全问题,⽐如在事务A中执⾏了⼀条 INSERT 语句,在没有执⾏COMMIT 的情况下,会在事务B中被读取到,此时如果事务A执⾏回滚操作,那么事务B中读取到事务A写⼊的数据将没有意义,我们把这个理象叫做 “脏读” 。

问题重现:

set global transaction isolation level read uncommitted;
select @@global.treansaction_isolation;

在这里插入图片描述
在不同的客服端:

start transaction;
insert into t_account values(3,'王五',3000);
select * from t_account;

在这里插入图片描述

rollback;
select * from t_account;

在这里插入图片描述
• 由于 READ UNCOMMITTED 读未提交会出现"脏读"现象,在正常的业务中出现这种问题会产⽣⾮
常危重后果,所以正常情况下应该避免使⽤ READ UNCOMMITTED 读未提交这种的隔离级别

5.2 READ COMMITTED - 读已提交与不可重复读

存在问题:
为了解决脏读问题,可以把事务的隔离级别设置为 READ COMMITTED ,这时事务只能读到了其他事务提交之后的数据,但会出现不可重复读的问题,⽐如事务A先对某条数据进⾏了查询,之后事务B对这条数据进⾏了修改,并且提交( COMMIT )事务,事务A再对这条数据进⾏查询时,得到了事务B修改之后的结果,这导致了事务A在同⼀个事务中以相同的条件查询得到了不同的值,这个现象要"不可重复读"。
问题重现:
• 在⼀个客⼾端A中先设置全局事务隔离级别为 READ COMMITTED 读已提交:

use java;
insert into t_account values(null,'王五',3000);
select * from t_account;
start transaction;
select @@global.transaction_isolation;
update t_account set balance=0 where name='王五';
rollback;
select * from t_account;

在这里插入图片描述

5.3REPEATABLE READ - 可重复读与幻读

存在问题
为了解决不可重复读问题,可以把事务的隔离级别设置为 REPEATABLE READ ,这时同⼀个事务中读取的数据在任何时候都是相同的结果,但还会出现⼀个问题,事务A查询了⼀个区间的记录得到结果集A,事务B向这个区间的间隙中写⼊了⼀条记录并提交,事务A再查询这个区间的结果集时会查到事务B新写⼊的记录得到结果集B,两次查询的结果集不⼀致,这个现象就是"幻读"。MySQL的InnoDB存储引擎使⽤了Next-Key锁解决了⼤部分幻读问题。
问题重现:

set global transaction isolation level read committed;
select @global.transaction_isolation;

在这里插入图片描述

use java;
start transaction;
update t_account set balance=888 where name='王五';
select * from t_account;
insert into t_account values(3,'赵六',5000);
commit;

在这里插入图片描述

5.4SERIALIZABLE - 串⾏化

进⼀步提升事务的隔离级别到 SERIALIZABLE ,此时所有事务串⾏执⾏,可以解决所有并发中的安
全问题。

6.不同隔离级别的性能与安全

在这里插入图片描述
在这里插入图片描述

InnoDB存储引擎事务隔离性以及相关的隔离级别是由锁和MVCC机制配合实现的。