详解MySQL之事务篇

MySQL的组成结构

一、哪些引擎支持事务

MySQL是属于插件型的数据库,支持多种存储引擎,包括 MyISAM 、InnoDB、Memory、Infobright 等。
执行器通过调用存储引擎的相关接口,来达到操作数据存储的目的。也就是说,你也可以自己写个存储引擎,只要你能提供相对应的读写接口。

这么多引擎中,MyISAM 和 InnoDB 是最为常用的。
其中,目前只有 InnoDB 支持事务。因此 InnoDB 是目前最为常用的存储引擎。

二、事务的使用方式

2.1 启动方式
2.1.1 being 或者 start transaction
  • 在执行到他们之后的第一个操作innoDB 表的语句,事务才真正启动
2.1.2 start transaction with consistent snapshot
  • 立马启动事务

正常的业务中,我们一般使用 begin 比较多,但是我们还是要区分开这三种命令的区别。
being 或者 start transaction 实际上并没有区别,在执行这个语句的时候,实际上事务并没正式启动。只有当你接着执行第一个操作 innoDB 数据库表的时候,才是真正启动了。
而 start transaction with consistent snapshot 则不需要进行等待。

2.2 提交方式
2.2.1 主动提交

即我们熟悉的 commit 语句

2.2.2 自动提交

即 autocommit 参数

当 autocommit 为 on 时,InnoDB 会把你的每次执行都当做一个完整的事务,不需要你手动启动或者手动的提交。

当 autocommit为off 时,你每次操作执行,都是不会自动的提交事务。
我们举几个场景例子试试。

CREATE TABLE  `test_2022` (
  `id` int(11) NOT NULL,
  `name` varchar(20) NOT NULL
) ENGINE=InnoDB;
INSERT INTO `test_2022` VALUES (1, '1-name');
INSERT INTO `test_2022` VALUES (2, '2-name');
INSERT INTO `test_2022` VALUES (3, '3-name');
INSERT INTO `test_2022` VALUES (4, '4-name');

注:以下操作均在隔离级别为 “可重复读” 场景下。

假设 autocommit = on 时

mysql> show variables like "autocommit";
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | ON    |
+---------------+-------+
1 row in set (0.00 sec)
事务A事务B
select name from test_2022 where id=1;
begin;
update test_2022 set name=‘mclink’ where id=1;
select name from test_2022 where id=1;

此时,你会发现,事务A两次查询到的 name 都为 ‘1-name’
这是因为在 autocommit=on 的情况下,如果主动开启事务,如果没有主动 commit。数据库也不会自动帮你提交。此时,在可重复读的隔离级别上,没有提交的时候做的数据更改是不可见的(除非是update的当前读(for upadate)操作)
如果,你把事务B操作中的 begin;去掉。那么事务A第二次查到的 name就会变成 ‘mclink’
这是因为InnoDB 会把你的每次执行都当做一个完整的事务。

假设 autocommit = off 时。
设置当前会话 autocommit = off

mysql> set session autocommit=off;
事务A事务B
select name from test_2022 where id=2;
update test_2022 set name=‘mclink’ where id=2;
select name from test_2022 where id=2;
commit;
select name from test_2022 where id=2;

此时你会发现,事务A前两次查询的结果都是 ‘2-name’,第三次才是 ‘mclink’。
这是因为当我们把自动提交给关了,此时当我们执行语句的时候还是会自动开启一个事务,但是并不会自动的提交,除非你手动的去 commit。

2.3 回滚方式
rollback;

回滚事务只有这一条命令,当你在事务中执行了这条命令,事务中的所有操作(更新,插入,删除)都会全部取消。

2.4 查询目前正在执行的事务
select * from information_schema.INNODB_TRX;

三、事务的隔离级别

3.1 四种隔离级别
隔离级别概念严格程度
读未提交一个事务还没提交时,它做的变更就能被别的事务看到很不严格
读已提交一个事务提交后,它做的变更才会被其他事务看到有点严格
可重复读一个事务执行过程中看到的数据,总是跟这个事务启动时看到的数据是一致的挺严格的
串行化对同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务完成后,才能继续执行超级严格

innodb 目前支持四种隔离级别。
默认的隔离级别是可重复读。你可以通过执行这些命令看看你数据库的隔离级别是哪种。

-- 查询当前会话隔离级别
select @@tx_isolation;
-- 查看系统当前隔离级别
select @@global.tx_isolation;

我们简单举几个栗子说明一下几种方式的区别。

事务A事务B
select name from test_2022 where id=3
begin;
update test_2022 set name=‘mclink’ where id=3;
select name from test_2022 where id=3
commit;
select name from test_2022 where id=3;

当隔离级别为 【读未提交】时。 第一次 name 的值为 ‘3-name’, 后面两次的 name 都是 ‘mclink’。
当隔离级别为 【读已提交】时,第一次和第二次 name 的值都是 ‘3-name’, 第三次才是 ‘mclink’。
当隔离级别为【可重复读】时。我们改一下栗子。

事务A事务B
begin;
select name from test_2022 where id=3
begin;
update test_2022 set name=‘mclink’ where id=3;
select name from test_2022 where id=3
commit;
select name from test_2022 where id=3;
commit;

此时,由于【一致性视图】的原因,查到的三次 name 值都是 ‘3-name’;

而第四种串行化,相当于只要有一个事务占用了这条数据,其他的事务都需要等待。相当于一个原子锁。这种方式性能很差,一般都不会使用这种隔离级别。

3.2 脏读、幻读、当前读、快照读
3.2.1 什么是脏读

脏读在业务上是比较严重的事情,当你的隔离级别设置为【读未提交】时,就会出现脏读。
我们也举个例子。

事务A事务B
select name from test_2022 where id=4
begin;
update test_2022 set name=‘mclink’ where id=4;
select name from test_2022 where id=4
rollback;
select name from test_2022 where id=4

你会发现,在这种隔离级别下,第一次查询的 name 是 '4-name‘,第二次查询的值是 ‘mclink’。
第三次查询到的 name 值 为 ‘4-name’。

第二次查询到 name 值改变了,这种情况我们就叫做【脏读】。其实就是另一个事务更新了这个数据,但是并没有提交事务,此时却查询到了他的修改,后面他事务回滚了,我们查到的值又变回来了。
用生活中的栗子来讲,就是 老板答应给你加钱(更新数据),但是没有签合同(提交事务)。你就觉得老板给你加钱了(查询到变更的数据)。后面老板又说不加了(回滚事务),你的工资又变回去了(数据又变回去了),像是做梦一样。所以,请不要相信画饼。

3.2.2 什么是快照读

快照读的意思就是,事务启动后,事务内部的查询默认都是以事务启动时的一致性视图为准,这种情况下即使其他事务插入了数据也是不可见。这其实是 MVCC 的机制控制的,每个数据都有自己数据版本,版本对应的值是最新操作事务的ID,通过结合 undo log 就可以达到获取数据的前世今生了。

3.2.3 什么是当前读

我们经常会有增值的操作,比如说把某个数值做加减。
此时你会问,如果是快照读的话,那不是被加的值就不对了。
比如说,老板A说要给你加 20块钱,老板B说要给你加50块钱,你本来是100块的时薪水,然后老板B操作比较快,已经录入了工资系统(提交事务),这个时候老板A如果还是基于100去给你加钱,不就亏了。

因此,实际上,update的操作使用的并不是快照读,而是当前读。相当于更新的时候,取的是最新的数据。

select * from test_2022 where id=4 for update;

这个 for update 就是使用当前读的意思。

3.2.4 什么是幻读

在【可重复读】隔离级别下,普通的查询是【快照读】,是不会看到别的事务插入的数据的。
因此,幻读在“当前读”下才会出现。而且幻读仅专指“新插入的行”。
同样我们也举个例子

事务A事务B
begin;
select * from test_2022;
INSERT INTO test_2022 VALUES (5, ‘5-name’);
select * from test_2022 for update
select * from test_2022;
commit

事务A的第一次全表查询我们可以查到四条之前插入的记录。当事务A执行插入语句后。
第二次查询我们可以查到五条记录(因为使用的是当前读)
而第三次查询,查到的还是四条记录(快照读)

所以在当前读的情况下,我们是可以看到其他已提交的事务插入的数据的。但是不使用当前读的时候又查不到,这种情况下就叫做幻读。

四、小结

事务是经常使用的保障手段,当某个流程需要满足 “要么全部完成, 要么全部失败” 的场景时,就可以使用事务,本篇文章总结了事务的使用方式,隔离方式,以及 innodb 几种 “读” 的不同概念,其实事务和锁的联系十分紧密,后面我们聊到锁的时候,可以看看如何将他们打通来看。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MClink

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值