【MySQL】事务及隔离性

目录

一、什么是事务

(一)概念

(二)事务的四大属性

(三)事务的作用

(四)事务的提交方式

二、事务的启动、回滚与提交

(一)事务的启动、回滚与提交

(二)特殊情况

[1、未 commit 事务,客户端崩溃,MySQL将自动回滚](#1、未 commit 事务,客户端崩溃,MySQL将自动回滚)

[2、commit 后,客户端崩溃,插入数据不受影响](#2、commit 后,客户端崩溃,插入数据不受影响)

3、手动开启事务不受自动提交事务影响

[4、自动提交事务对单条 SQL 语句的影响](#4、自动提交事务对单条 SQL 语句的影响)

5、结论

三、事务的隔离级别

(二)事务的隔离级别

(三)四种隔离级别详解

1、读未提交

2、读已提交

3、可重复读

4、串行化


一、什么是事务

(一)概念

MySQL 事务是指数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成,这些操作要么全部执行成功,要么全部不执行,是一个不可分割的工作单位。事务主要用于保证数据的一致性和完整性,特别是在需要多个操作同时成功或同时失败的场景中,比如银行转账、订单处理等。

(二)事务的四大属性

事务的特性:

  • 原子性:事务中的所有操作要么全部完成,要么全部不完成,不会结束在中间某个状态;
  • 一致性:事务必须使数据库从一个一致性状态变换到另一个一致性状态;
  • 隔离性:多个事务并发执行时,一个事务的执行不会影响其他事务;
  • 持久性:一旦事务提交,其结果就是永久性的,即使系统崩溃也不会丢失。

(三)事务的作用

事务的出现是为了简化程序开发时可能需要考虑的多种细节问题。例如:当银行账户 A 向银行账户 B 发起转账,首先需要现在 A 中扣除目标金额后再向B中添加目标金额,假如在扣除A账户的金额后出现了网络异常导致转账失败,这时的正常情况应该是账户A上返回了目标金额,事务的出现就使得该操作可以由 MySQL 自动完成而不需要程序员特殊处理。

(四)事务的提交方式

事务的提交方式分为自动提交和手动提交。在先前学习MySQL语句时并没有对事务进行过特殊操作,这是因为 MySQL 默认设置自动提交。也就是每当执行一条语句后 MySQL自动将该语句进行事务的提交。

查看事务自动提交方式:

sql 复制代码
//MySQl 默认打开自动提交
mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | ON    |
+---------------+-------+
1 row in set (0.02 sec)
//设置自动提交事务关闭
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)

mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | OFF   |
+---------------+-------+
1 row in set (0.00 sec)

二、事务的启动、回滚与提交

为方便演示,需关闭事务自动提交、将事务的隔离级别设为最低并准备一个测试表:

sql 复制代码
//打开事务自动提交
mysql> set autocommit=1;
Query OK, 0 rows affected (0.00 sec)

mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | ON    |
+---------------+-------+
1 row in set (0.00 sec)
//设置隔离级别(需重启终端)
mysql> set global transaction isolation level read uncommitted;
Query OK, 0 rows affected (0.00 sec)
//创建测试表
mysql> create table test(
    -> id int primary key,
    -> name varchar(20) not null,
    -> salary decimal(10,2) default 0.0
    -> );
Query OK, 0 rows affected (0.02 sec)

(一)事务的启动、回滚与提交

sql 复制代码
//手动开启事务 (手动开始事务后该事务不受自动提交影响)
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
//保存点1
mysql> savepoint save1;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into test values(1,'张三',5000);
Query OK, 1 row affected (0.00 sec)
//保存点2
mysql> savepoint save2;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into test values(2,'李四',4500);
Query OK, 1 row affected (0.00 sec)
//此时有两条记录
mysql> select * from test;
+----+--------+---------+
| id | name   | salary  |
+----+--------+---------+
|  1 | 张三   | 5000.00 |
|  2 | 李四   | 4500.00 |
+----+--------+---------+
2 rows in set (0.00 sec)
//回滚至保存点2
mysql> rollback to save2;
Query OK, 0 rows affected (0.00 sec)
//此时变为一条记录
mysql> select * from test;
+----+--------+---------+
| id | name   | salary  |
+----+--------+---------+
|  1 | 张三   | 5000.00 |
+----+--------+---------+
1 row in set (0.00 sec)
//回滚至最开始
mysql> rollback;
Query OK, 0 rows affected (0.00 sec)
//无记录
mysql> select * from test;
Empty set (0.00 sec)

由上述结果可以看出,手动开启事务后将不受自动提交的影响。以上便是手动开启事务以及回滚操作。

(二)特殊情况

以下情况都是最低隔离级别下的操作(读未提交),为方便说明开启两个终端进行演示:

1、未 commit 事务,客户端崩溃,MySQL将自动回滚

sql 复制代码
//客户端A
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into test values(1, '张三', 5000);
Query OK, 1 row affected (0.00 sec)

mysql> insert into test values(2, '李四', 4500);
Query OK, 1 row affected (0.00 sec)

mysql> Aborted
[X@centos-414 ~]$ 

//客户端B
mysql> select * from test;
Empty set (0.00 sec)

mysql> select * from test;
+----+--------+---------+
| id | name   | salary  |
+----+--------+---------+
|  1 | 张三   | 5000.00 |
+----+--------+---------+
1 row in set (0.00 sec)

mysql> select * from test;
+----+--------+---------+
| id | name   | salary  |
+----+--------+---------+
|  1 | 张三   | 5000.00 |
|  2 | 李四   | 4500.00 |
+----+--------+---------+
2 rows in set (0.00 sec)

mysql> select * from test;
Empty set (0.01 sec)

2、commit 后,客户端崩溃,插入数据不受影响

sql 复制代码
//客户端A
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into test values(1, '张三', 5000);
Query OK, 1 row affected (0.00 sec)

mysql> insert into test values(2, '李四', 4500);
Query OK, 1 row affected (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.00 sec)

mysql> Aborted

//客户端B
mysql> select * from test;
Empty set (0.00 sec)

mysql> select * from test;
+----+--------+---------+
| id | name   | salary  |
+----+--------+---------+
|  1 | 张三   | 5000.00 |
+----+--------+---------+
1 row in set (0.00 sec)

mysql> select * from test;
+----+--------+---------+
| id | name   | salary  |
+----+--------+---------+
|  1 | 张三   | 5000.00 |
|  2 | 李四   | 4500.00 |
+----+--------+---------+
2 rows in set (0.00 sec)

mysql> select * from test;
+----+--------+---------+
| id | name   | salary  |
+----+--------+---------+
|  1 | 张三   | 5000.00 |
|  2 | 李四   | 4500.00 |
+----+--------+---------+
2 rows in set (0.00 sec)

3、手动开启事务不受自动提交事务影响

sql 复制代码
//客户端A
mysql> set autocommit=1;
Query OK, 0 rows affected (0.00 sec)

mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | ON    |
+---------------+-------+
1 row in set (0.00 sec)

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into test values(3, '王五', 5500);
Query OK, 1 row affected (0.00 sec)

mysql> Aborted

//客户端B
mysql> select * from test;
+----+--------+---------+
| id | name   | salary  |
+----+--------+---------+
|  1 | 张三   | 5000.00 |
|  2 | 李四   | 4500.00 |
+----+--------+---------+
2 rows in set (0.00 sec)

mysql> select * from test;
+----+--------+---------+
| id | name   | salary  |
+----+--------+---------+
|  1 | 张三   | 5000.00 |
|  2 | 李四   | 4500.00 |
|  3 | 王五   | 5500.00 |
+----+--------+---------+
3 rows in set (0.00 sec)

mysql> select * from test;
+----+--------+---------+
| id | name   | salary  |
+----+--------+---------+
|  1 | 张三   | 5000.00 |
|  2 | 李四   | 4500.00 |
+----+--------+---------+
2 rows in set (0.01 sec)

4、自动提交事务对单条 SQL 语句的影响

sql 复制代码
//客户端A
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)

mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | OFF   |
+---------------+-------+
1 row in set (0.00 sec)

mysql> insert into test values(3, '王五', 5500);
Query OK, 1 row affected (0.00 sec)

mysql> Aborted

//客户端B
mysql> select * from test;
+----+--------+---------+
| id | name   | salary  |
+----+--------+---------+
|  1 | 张三   | 5000.00 |
|  2 | 李四   | 4500.00 |
+----+--------+---------+
2 rows in set (0.00 sec)

mysql> select * from test;
+----+--------+---------+
| id | name   | salary  |
+----+--------+---------+
|  1 | 张三   | 5000.00 |
|  2 | 李四   | 4500.00 |
|  3 | 王五   | 5500.00 |
+----+--------+---------+
3 rows in set (0.00 sec)

mysql> select * from test;
+----+--------+---------+
| id | name   | salary  |
+----+--------+---------+
|  1 | 张三   | 5000.00 |
|  2 | 李四   | 4500.00 |
+----+--------+---------+
2 rows in set (0.00 sec)

5、结论

  • 只要输入 begin 或者 start transaction,事务就必须通过 commit 提交才会持久化,与是否设置自动提交无关;
  • 对于 InnoDB 每一条 SQL 语言都默认封装成事务,自动提交。(select有特殊情况,因为 MySQL 有 MVCC );
  • 如果没有设置保存点,也可以回滚,只能回滚到事务的开始。直接使用 rollback(前提是事务还没有提交)
  • 如果一个事务被提交了则无法回退;
  • InnoDB 支持事务, MyISAM 不支持事务

三、事务的隔离级别

(一)什么是隔离性

事务的隔离性是数据库事务的四大特性之一,它确保并发执行的多个事务相互独立,一个事务的操作不会被其他事务干扰,从而避免数据不一致问题。隔离性通过不同的隔离级别来控制事务之间的可见性和影响程度。

查看隔离级别:

sql 复制代码
//查看全局隔离级别(一般默认为REPEATABLE-READ)
mysql> select @@global.tx_isolation;
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| READ-UNCOMMITTED      |
+-----------------------+
1 row in set, 1 warning (0.01 sec)
//查看此次会话隔离级别(一般开启MySQL客户端后该值由全局隔离级别进行初始化)
mysql> select @@session.tx_isolation;
+------------------------+
| @@session.tx_isolation |
+------------------------+
| READ-UNCOMMITTED       |
+------------------------+
1 row in set, 1 warning (0.00 sec)
//查看此次会话隔离级别
mysql> select @@tx_isolation;
+------------------+
| @@tx_isolation   |
+------------------+
| READ-UNCOMMITTED |
+------------------+
1 row in set, 1 warning (0.00 sec)

在使用MySQL客户端时的隔离级别由会话隔离级别等级决定。

设置隔离级别:

sql 复制代码
//设置会话隔离级别
mysql> set session transaction isolation level serializable;
Query OK, 0 rows affected (0.00 sec)

mysql> select @@global.tx_isolation;
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| READ-UNCOMMITTED      |
+-----------------------+
1 row in set, 1 warning (0.00 sec)

mysql> select @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| SERIALIZABLE   |
+----------------+
1 row in set, 1 warning (0.00 sec)

(二)事务的隔离级别

SQL标准定义了四种隔离级别:

  1. 读未提交
    允许事务读取其他事务未提交的数据,可能出现脏读、不可重复读和幻读的情况;
  2. 读已提交
    只允许读取已提交的数据,避免脏读,但存在不可重复读和幻读的情况;
  3. 可重复读
    确保同一事务多次读取同一数据结果的一致性,可能会出现幻读的情况;
  4. 串行化
    最高隔离的级别,事务完全串行化,可避免所有并发问题,但性能低。

(三)四种隔离级别详解

1、读未提交

在多个并行的会话中启动事务,一个事务在改动数据库哪怕没有commit提交,其他事务也是能够实时的看到它修改的数据。一个事务在执行中,读到另一个执行中事务的更新(或其他操作)但是未commit的数据,这种不合理的现象叫做脏读(dirty read)。

上图可以看出,即使左端事务没有进行提交操作,但右端事务仍可以看到表的操作,这就是脏读。

2、读已提交

事务A在commit提交事务之前,所做的修改是不会被其他事务看到的,一旦事务A发起commit之后,其他事务就能看到事务A对数据的修改。这就造成了其他事务在不同的时间点select查看数据库时,会查到不同的数据。这种现象叫不可重复读

上图可知,只要事务的操作被提交,那么其他事务可以查看到该事务的插入操作,这就会导致其他事务对同一表的查询结果可能会发生变化,这就是不可重复读。

3、可重复读

可重复读是MySQL默认的隔离级别。

上图可知,即使事务的操作被提交,其他事务仍然无法查看到该事务对表的操作,只要其他事务也提交以后才能查看到其他事务对表的操作。

4、串行化

串行化就是对所有事务进行加锁,事务的执行(一般对查询操作不进行加锁)全部挨个排队,这就导致了效率低下问题。

开启事务A和事务B,两个事务同时select读取将使用共享锁,不会串行化;事务A中有更新等操作,会阻塞A,直到事务B提交。如果事务A阻塞时间过长,将会由于锁等待超时退出当前事务。

相关推荐
泽韦德1 小时前
【Redis】笔记|第4节|Redis数据安全性分析
数据库·redis·笔记
eR3R31 小时前
我用Rust重新写了一遍ArangoDB
数据库
夕泠爱吃糖2 小时前
MySQL事务
android·数据库·mysql
robch2 小时前
mysql prepare statement
android·数据库·mysql
fengye2071612 小时前
板凳-------Mysql cookbook学习 (九--2)
学习·mysql·adb
运维老曾2 小时前
ubuntu mysql 8.0.42 基于二进制日志文件位置和GTID主从复制配置
数据库·mysql·dba
纪元A梦3 小时前
Redis最佳实践——购物车优化详解
数据库·redis·缓存
隐-梵3 小时前
Android studio进阶开发(七)---做一个完整的登录系统(前后端连接)
android·数据库·ide·spring·okhttp·android studio
水中加点糖3 小时前
各种数据库,行式、列式、文档型、KV、时序、向量、图究竟怎么选?
数据库·图数据库·向量数据库·选型·对比·行式存储·列式存储
南汐以墨4 小时前
Mybatis:灵活掌控SQL艺术
java·数据库·sql