MySQL数据库 - 事务

目录

  • 1.事务的基本介绍和使用
    • [1.1 事例](#1.1 事例)
    • [1.2 事务的特性](#1.2 事务的特性)
  • 2.事务的隔离级别
    • [2.1 隔离级别的查询](#2.1 隔离级别的查询)
    • [2.2 隔离级别的设置](#2.2 隔离级别的设置)
  • 3.不同隔离级别引发的问题
    • [3.1 脏读问题](#3.1 脏读问题)
    • [3.2 不可重复读](#3.2 不可重复读)
    • [3.3 幻读](#3.3 幻读)
    • [3.4 不同隔离级别的比较](#3.4 不同隔离级别的比较)

1.事务的基本介绍和使用

事务简单理解就是将多条sql语句看作为一个整体,事务的执行有两种情况:

1.执行成功事务内的所有sql语句都执行完成

2.执行失败已经执行的sql语句会发生回滚恢复到事务执行前的状态

1.1 事例

例子:执行有一个转账的业务执行,由A账户给B账户转账500元,转账前的A账户有1000元,B账户有0元;转账后A账户的金额为:500元,B账户金额为:500元;

先创建一个account_info账户表格记录用户的信息,定义account_info_id(账号ID),account_info_name(账户名称),account_info_money(账户金额);

sql 复制代码
# 创建账户信息表格
create table account_info(
account_info_id varchar(20) primary key, # 账号ID,同时设置为主键索引
account_info_name varchar(20), # 账户名称
account_info_money bigint  # 账户金额
) charset = utf8mb4;# 设置编码集

初始化表格信息:添加账号A:"10011001","A",1000;账号B:"10011002","B",0;

sql 复制代码
insert into account_info values("10011001","A",1000),("10011002","B",0);

查找表格信息

sql 复制代码
select* from acount_info;

转账涉及两条sql语句,账号A转账给B,A的账号金额减少500,B的账号金额增加500;

sql 复制代码
update account_info set account_info_money = 500 where account_info_id = 10011001; # 更新账号A
update account_info set account_info_money = 500 where account_info_id = 10011002; # 更新账号B

查看表格数据

sql 复制代码
select account_info_id as "账号ID", account_info_name as "账号名称", account_info_money as "账号金额" from account_info;

以上执行的结果是正确的,但是执行的sql语句并不是一个整体,如果需要当作一个事务,可以修改为以下格式。

sql 复制代码
start transaction; # 开启事务

update account_info set account_info_money = 500 where account_info_id = 10011001; # 更新账号A
update account_info set account_info_money = 500 where account_info_id = 10011002; # 更新账号B

commit; # 事务执行完成,不会发生回滚

rollback; # 事务无法正常执行完,发生回滚,恢复到事务执行前的状态

start transaction: 表示开启一个新的事务;

commit:表示事务执行完成,不需要发生回滚;

rollback: 回滚,事务执行的结果与预期的结果不一致就会发生回滚操作。

1.2 事务的特性

(1)原子性:一个事务要么成功执行,要么执行失败,不会程序只执行部分sql语句的情况,因为只要有部分sql语句没有执行,就会发生回滚操作(rollback);

(2)一致性 :事务发生前后不会对数据库的完整性破坏,因为数据的执行必须满足预设的条件,包括精度,关联性,服务器崩溃后数据的恢复;

数据库的完整性包含:

1)实体完整性(确保每一个表都存在唯一主键,不能为null);

2)参照物完整性(例如外键:确保定义的外键需要在被引用的表中存在,或者为null);

3)域完整性(定义的列满足特定给数据格式:continue,default,指定类型varchar(20)等);

4)用户定义的完整性 (满足用户对某一列的要求,如:添加性别时只能设置为男或者女,年龄大 于等于18岁等);

5)数据的一致性和精确性(相关联的列在逻辑上或者业务上必须满足要求);

6)系统维护的完整性(系统发生奔溃时,根据提取备份的数据,进行数据库的恢复)。

事务执行满足的预设的添加:精度(域完整性),关联性(参照物完整性),服务器崩溃后的机制(系统维护和完整性),事务可以正常执行的条件满足数据库执行的条件,所以事务的执行不会破坏数据库的完整性。

简单理解就是数据库的完整性需要满足一系列约束规则(6个完整性约束),而事务的一致性则是保证这些约束规则不被破坏;

(3)数据可以持久化保存:事务执行后修改的数据是保存在硬盘的,可以持久化保存;

(4) 数据库可以支持多个事务同时进行,对数据进行交叉修改,通过事务的隔离级别确保事务执行后数据的准确性.

2.事务的隔离级别

事务总共包含4个隔离级别:1)read uncommited(读未提交);2)read commited(读已提交);

3)repeatable read(可重复读);4)serializable(串行化);

2.1 隔离级别的查询

查询当前系统设置的隔离级别:系统默认是repeatable read(可重复读)

sql 复制代码
select @@global.transaction_isolation; # 全局隔离级别
select @@session.transaction_isolation; # 局部隔离级别

全局的隔离级别对所有sql语句都生效,局部的隔离基本对当前文件生效;

2.2 隔离级别的设置

设置隔离级别的语法如下。

sql 复制代码
set [global/session] transaction isolation level [隔离级别]; 

设置的语句如下:

sql 复制代码
# 全局
set global transaction isolation level read uncommitted; # 读未提交

set global transaction isolation level read committed; # 读已提交

set global transaction isolation level  repeatable read; # 可重复读

set global transaction isolation level  serializable;# 序列化

# 局部
set session transaction isolation level read uncommitted;# 读未提交

set session transaction isolation level read committed; # 读已提交

set session transaction isolation level  repeatable read;# 可重复读

set session transaction isolation level  serializable;# 序列化

3.不同隔离级别引发的问题

3.1 脏读问题

脏读问题出现在隔离级别在读未提交的事务隔离级别,出现原因是一个事务B读取了另一个事务A未修改完成的数据,导致事务B获取的数据可能是错误的。

例如,创建一个test表,有字段test_id,test_value,添加数据(1,10);设置事务的隔离级别为read uncommitted,事务B在事务A提交前读取,事务B读取到的数据后执行相关的操作,可能执行的结果都是错误的。

sql 复制代码
# 创建test表格
create table test(
test_id int PRIMARY KEY AUTO_INCREMENT,
test_value int
)

#插入数据
insert into test(test_value) value(10);


在navicat中打开两个窗口。

设置事务的隔离级别

sql 复制代码
# 设置事务的隔离级别
set session transaction isolation level read uncommitted;

在第一个窗口中设置事务的隔离级别成功,在第二窗口查看事务的隔离级别为read uncommitted,但是此时生效只有第一个窗口,第二个窗口并没有生效。可以通过以下方法测试

1.执行事务A的修改,不提交。

2.执行事务B,并提交。

可以看到如果此时事务B是read uncommitted,应该读取到的数据为事务A修改后的数据test_val = 30,但是执行完整的事务B得到的结果为10(修改前的数据);此时需要断开事务B的窗口,重新连接才会生效。

重新连接只需要将第二个窗口关闭后重新打开就会生效。

此时再次执行以上操作,发现事务B执行后的结果为30,但是如果事务A未提交,而是发生回滚,此时test_val的值还是10,而事务B读取到的数据为30,读取错误的数据,执行后续相关操作也会出现问题。

如果需要解决脏读问题,可以更高的事务隔离级别:read committed

sql 复制代码
# 读已提交
set  session transction isolaction level ;

3.2 不可重复读

不可重复读出现在事务隔离级别为read committed ,表示同一个事务读取同一个数据出现的结果不同。

还是使用以上的的表格,先设置隔离级别为read committed

sql 复制代码
set session transaction isolation level read committed;

1.开启事务B,并查询test_id为1的值。

2.执行完整的事务A,将test_id为1的test_val修改为30.

3.执行事务B第二次查询操作。

可以发现,第一次执行获取到的test_val为10,第二次执行获取到的test_val为30,在同一个事务(B)中,读取同一个test_val值出现两个不一样的结果,此时就出现不可重复读问题。(可重复读:在一个事务中读取同一个字段获取到的值是一样的)

为了解决不可重复读问题,可以提高事务的隔离级别,设置为:repeatable read

sql 复制代码
set session transaction isolation level  repeatable read;# 可重复读

3.3 幻读

幻读问题出现在事务隔离级别为:repeatable read,同一个事务中查询多次同一个数据,查询的结果不唯一,就查询了幻读问题。

1.执行事务B,此时test表就一条数据。

2.执行事务A,在表格中加入一条新的数据。

3.执行事务B,再次执行查询操作并提交。

以上操作发现查询的结果是一样的,但是并不意味不存在幻读问题,之所以查询查询结果是一样的,是因为repeatable read 可以一定程度上解决幻读问题,这与其实现方式相关,使用到了间隙锁的机制(此处不对间隙锁展开介绍:可以参考这一篇博客:mysql中锁的介绍

还是按照以上的执行过程,但是将查询事务B第二次查询的操作修改为insert操作。

当执行第三步操作时,会报错:主键已经存在,不可重复添加。

在第一步查询时获取到表格数据为一条,但是第二次需要加入第二条数据时却发现数据已经存在,不可重复加入,此时就出现了幻读问题,统一事务查询同一个数据发现两次数据结果不一样。

为了解决幻读问题可以提升事务的隔离级别,也是事务隔离的最高级别:serializable;

sql 复制代码
set session transaction isolation level  serializable;# 序列化

3.4 不同隔离级别的比较

隔离级别 脏读 可重复读 幻读
read uncommitted 存在 存在 存在
read committed 不存在 存在 存在
repeatable read 不存在 不存在 存在
serializable 不存在 不存在 不存在
复制代码
            隔离级别越高,性能越低,安全系数更高;隔离级别越低,性能越高,安全系数越低

以上就是关于事务的相关介绍,如果有相关疑问欢迎留言讨论,我们下一篇文章再见!

相关推荐
木井巳2 小时前
【Java】深入理解Java语言的重要概念
java·开发语言
yangminlei2 小时前
MyBatis插件开发-实现SQL执行耗时监控
java·开发语言·tomcat
Mr Robot2 小时前
数据库概述
数据库·oracle
what丶k2 小时前
Java连接人大金仓数据库(KingbaseES)全指南:从环境搭建到实战优化
java·开发语言·数据库
kida_yuan2 小时前
【Oracle】Ubuntu 部署 Oracle 10g 的完整实战复盘
数据库·ubuntu·oracle
霖霖总总2 小时前
[小技巧44]MySQL Purge 线程详解:作用、机制与性能优化
数据库·mysql
沛沛老爹2 小时前
从Web到AI:多模态Agent Skills开发实战——JavaScript+Python全栈赋能视觉/语音能力
java·开发语言·javascript·人工智能·python·安全架构
菜鸡上道2 小时前
MySQL 查询优化全解析:从原理到实战
数据库·mysql
0x532 小时前
JAVA|智能仿真并发项目-进程与线程
java·开发语言·jvm