首页 > 数据库 >MySQL进阶—事务

MySQL进阶—事务

时间:2023-03-02 16:03:31浏览次数:47  
标签:account transaction 窗口 进阶 money 事务 提交 MySQL

事务

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

事务的四大特性:

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

MySQL的事务默认是自动提交的,当执行一条DML语句,MySQL会隐式的提交事务。

默认自动提交事务可能会导致数据出错,例如,有如下数据表:


MySQL进阶—事务_数据库


接下来我们模拟收付款,同时执行如下代码:


update account set money=money - 1000 where name='买家';          # 买家付款
update account set dasa money=money + 1000 where name='商家'; # 商家收款

执行结果如下图所示:


MySQL进阶—事务_数据_02


由于第二条MySQL语句是错误的,MySQL自动提交事务错误,导致商家没有收到款。

事务操作

既然自动提交事务可能会导致数据错误,那么我们把自动提交事务改为手动提交事务。

手动提交事务

在修改事务提交方式之前,执行如下代码查看事务的提交方式:


select @@autocommit;

如下图所示:


MySQL进阶—事务_数据_03


其中1为自动提交事务,0为手动提交事务。

查看事务提交方式后,执行如下代码把事务提交方式改为手动提交:


set @@autocommit=0;

如下图所示:


MySQL进阶—事务_数据库_04


把事务提交方式改为手动提交后,需要执行如下代码提交事务和回滚事务:


commit;         #提交事务
rollback; #回滚事务

接下来我们还是模拟收付款来演示手动提交事务,同时执行如下代码:


update account set money=money - 1000 where name='买家';          # 买家付款
update account set money=money + 1000 where name='商家'; # 商家收款

发现数据没有发生改变,如下图所示:


MySQL进阶—事务_数据_05


这是因为还没提交事务,此时我们只需要执行如下代码数据会发生改变:


commit;

如下图所示:


MySQL进阶—事务_数据库_06


接下来我们同时执行如下代码演示事务执行出错的情况:


update account set money=money - 1000 where name='买家';          
update account set dasa money=money + 1000 where name='商家';
rollback; # 回滚事务

如下图所示:


MySQL进阶—事务_数据库_07


MySQL进阶—事务_提交事务_08


通过回滚事务把正确和错误的事务修改值恢复到修改前的值。

自动提交事务中手动提交

当我们不想把自动提交事务改为手动提交事务,而防止自动提交事务出错时,可以在自动提交事务中,手动开始事务,代码如下:


start transaction 或 begin;

示例代码如下:


set @@autocommit=1;         # 把事务提交方式改为自动提交
start transaction; # 开始事务
update account set money=money - 1000 where name='买家'; # 买家付款
update account set money=money + 1000 where name='商家'; # 商家收款

如下图所示:


MySQL进阶—事务_提交事务_09


MySQL进阶—事务_提交事务_10


发现数据没有发生改变,这时只需要执行如下代码数据就会发生改变:


commit;

并发事务问题

当有两个或两个以上的事务同时执行、提交事务时,可能会引发并发事务问题,并发事务常见的问题有:

  • 脏读:一个事务读取了另一个事务还没提交的数据;
  • 不可重复读:一个事务先后读取同一条记录,但两次读取的数据不同,这种称为不可重复读;
  • 幻读:一个事务按照条件查询数据时,没有对应的数据行,但在插入数据时,这行数据已经存在了,好像出现了“幻影”。

事务隔离级别

为了解决事务并发问题,我们设置事务的隔离级别,不同的隔离级别可以解决不同的事务并发问题,如下表:

隔离级别

脏读

不可重复读

幻读

Read uncommitted

不可解决

不可解决

不可解决

Read committed

可解决

不可解决

不可解决

Repeatable Read(默认)

可解决

可解决

不可解决

Serializable

可解决

可解决

可解决

在设置事务的隔离级别之前,我们首先执行如下代码看看事务的隔离级别:


select @@transaction_isolation;

设置事务隔离级别语法格式如下:


set [session | global] transaction isolation level {read uncommitted | read committed | repeatable read | serializable}

其中session是当前会话窗口有效,global是对当前客户端的所有会话窗口有效。

接下来我们来演示隔离级别解决事务并发问题。

Read uncommitted

首先依次执行如下代码查看和设置隔离级别:


select @@transaction_isolation;
set session transaction isolation level read uncommitted;

如下图所示:


MySQL进阶—事务_数据库_11


接下来通过两个cmd窗口通过start transaction手动开启两个事务,并依次执行如下代码:


# 窗口1
use mytest; # 选择mytest数据库
set session transaction isolation level read uncommitted; # 设置事务级别为read uncommitted
start transaction; # 开始事务
select * from account; # 查看数据

#窗口2
use mytest; # 选择mytest数据库
start transaction; # 开始事务
update account set money=money - 1000 where name='买家'; #更新数据

# 窗口1
select * from account; # 查看数据

如下图所示:


MySQL进阶—事务_数据库_12


在窗口2更新数据事务后,没有提交事务,但在窗口1查看数据发现数据已经更新了,这就属于脏读了。

Read committed

接下来我们通过设置Read committed事务隔离级别解决脏读问题,同样打开两个cmd窗口通过start transaction手动开启两个事务,并依次执行如下代码:


# 窗口1
use mytest; # 选择mytest数据库
set session transaction isolation level read committed; # 设置事务级别为read committed
start transaction; # 开始事务
select * from account; # 查看数据

#窗口2
use mytest; # 选择mytest数据库
start transaction; # 开始事务
update account set money=money - 1000 where name='买家'; #更新数据

# 窗口1
select * from account; # 查看数据

#窗口2
commit;

如下图所示:


MySQL进阶—事务_数据库_13


这样就成功解决了脏读问题。虽然Read committed隔离级别可以解决脏读问题,解决不了不可重复读和幻读。

接下来演示不可重复读,示例代码如下:


# 窗口1
use mytest; # 选择mytest数据库
set session transaction isolation level read committed; # 设置事务级别为read committed
start transaction; # 开始事务
select * from account; # 查看数据

#窗口2
use mytest; # 选择mytest数据库
start transaction; # 开始事务
update account set money=money - 1000 where name='买家'; #更新数据

# 窗口1
select * from account; # 查看数据

#窗口2
commit; # 提交事务

#窗口1
select * from account; # 查看数据

如下图所示:


MySQL进阶—事务_数据库_14


在窗口1中一次事务执行了3次查询数据表操作,前面两次结果相同,第三次查询的结果不同,这就叫不可重复读。

Repeatable Read

为了解决不可重复读,把事务隔离级别改为Repeatable Read,示例代码如下 :


# 窗口1
use mytest; # 选择mytest数据库
set session transaction isolation level repeatable read; # 设置事务级别为repeatable read
start transaction; # 开始事务
select * from account; # 查看数据

#窗口2
use mytest; # 选择mytest数据库
start transaction; # 开始事务
update account set money=money + 1000 where name='买家'; #更新数据
commit;

# 窗口1
select * from account; # 查看数据

# 窗口1
commit; # 提交事务
select * from account; # 查看数据

如下图所示:


MySQL进阶—事务_数据库_15


在窗口1开启一个事务并查询数据表的数据后,在窗口2中开启事务并更新提交事务,再返回窗口1中查询数据表发现查询结果和之前查询一样,这就叫可重复读,在窗口1中提交第一个事务后,再次查询数据表数据发现数据发生了改变。这就解决了并发问题的不可重复读。

Repeatable Read可以解决脏读和不可重复读,但解决不了幻读。

接下来我们演示幻读,示例代码如下:


# 窗口1
use mytest;
start transaction;
select * from account where id='3';

# 窗口2
use mytest;
start transaction;
insert into account(id,name,money) values(3,'中间商',2000);
commit;

# 窗口1
insert into account(id,name,money) values(3,'大中间商',2000);
select * from account where id='3';

如下图所示:


MySQL进阶—事务_数据库_16


在窗口1中开启事务并查询id为3的数据,发现数据表中没有id为3的数据,在窗口2中,开启事务并添加id为3的数据并提交事务后,在窗口1中添加id为3的数据,发现已经有id为3的数据了,但查询id为3的数据时,发现没有查到,这就是幻读。

Serializable

为了解决幻读并发事务问题,我们把事务隔离级别改为Serializable,示例代码如下:


# 窗口1
use mytest; # 选择mytest数据库
set session transaction isolation level Serializable; # 设置事务级别为Serializable
start transaction; # 开始事务
select * from account where id='5'; # 查看数据

# 窗口2
use mytest;
start transaction;
insert into account(id,name,money) values(5,'厂商',2000);

如下图所示:


MySQL进阶—事务_数据库_17


在窗口1查询id为5的数据,因为没有id为5的数据,所以没有查询到,在窗口2中插入id为5的数据,发现一直没有插入成功,这是因为在窗口1中已经开启了一个事务,需要窗口1开启的事务结束后,才能执行窗口2的事务,这样就解决了幻读问题。

标签:account,transaction,窗口,进阶,money,事务,提交,MySQL
From: https://blog.51cto.com/u_13146445/6093935

相关文章

  • MySQL学习笔记-多表查询(下)
    多表查询(下)一.联合查询联合查询:将多次查询结果合并,形成新的查询结果集select{字段列表}from{表A}...union[all]select{字段列表}from{表B}...;unio......
  • 存储过程块与事务控制
    KingbaseES的存储过程内的事务默认与外部调用者的事务是合并在一起的:存储过程内部的提交或回退会影响外部事务,而外部事务的提交或回退也会影响存储过程内部的事务。可以通......
  • mysql索引的面试常问问题
         ......
  • mysql sum 聚合计算后精度不准 出现多位小数点后的数
    解决办法原收款单money字段为decimal(28,8)经过层层计算用到了@total:=(beginning+@total+gather-verification)AS'balance',@num:=......
  • 转载 - 删除/清理 MySQL 的 binlog
    删除/清理MySQL的binlog 问题还处在数据清理和处理的流程中,突然发现程序脚本异常退出了。通过排查发现,1TB的磁盘,居然满了,定位到 /var/lib/mysql 路径下,发现了......
  • 美团2面:如何保障 MySQL 和 Redis 数据一致性?这样答,让面试官爱到 死去活来
    文章持续更新,建议收藏起来,慢慢读!疯狂创客圈总目录博客园版为您奉上珍贵的学习资源:免费赠送:《尼恩Java面试宝典》持续更新+史上最全+面试必备2000页+面试必备+......
  • MySQL数据库常用操作命令
    MySQL数据库常用操作命令登录mysql数据库:mysql-uuser-ppassword。user表示用户名、password表示登录密码。创建数据库:createdatabasecainiao;cainiao表示创建的数......
  • MySQL .ibd 文件很大清理空间
    如果你的MySQL.ibd文件很大,但是你的数据库文件大小不太,可能有几个原因:数据库文件和.ibd文件存储在不同的磁盘上。如果是这种情况,那么你需要清理.ibd文件所在的磁......
  • mysql知识点一
    1.mysql中造成索引失效的原因有哪些?如何分析和解决?原因:1.like以通配符%开头索引失效通常用的索引数据结构是B+树,而索引是有序排列的优化:一种是使......
  • mysql学习之join从句
    一、join从句共有5种类型内连接(innerjoin)全外连接(fullouterjoin)左外连接(leftouterjoin)右外连接(rightouterjoin)交叉连接(crossjoin)二、演示案例新建两张表user1表和us......