MySQL事务相关详解(一篇足够了)

前言

说到MySQL那么一定逃不过事务的相关知识,网上有很多文章都有介绍事务相关知识,但是个人感觉都比较散乱,下面记录一下自己的所学和感悟,做一个小结。

推荐博文:https://blog.csdn.net/ks2356/article/details/115257688

小知识

MySQL数据库中常用的存储引擎为InnoDB和MyISAM,而MyISAM不支持事务,故此我们说的事务都是指的InnoDB存储引擎中

1.什么是事务

事务(Transaction),一般是指要做的或所做的事情。在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元。事务由事务开始(begin transaction)和事务结束(end transaction)之间执行的全体操作组成。事务是恢复和并发控制的基本单位。

2.事务的特性ACID(敲黑板,重点)

原子性((Atomicity))

一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。

一致性(Consistency)

在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。

隔离性(Isolation)

数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。

持久性(Durability)

事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

CASE:以生活中去银行转账(A转账给B)为例,来熟悉事务的四大属性:

原子性:A转出的时候B同时也收到帐(过程不能分割开,不能A转出帐,但是B未收到帐)
一致性:A转出100那么B必须收到100(整个转账过程结束后A和B的总金额要保持一致)
隔离性:A转钱的过程中,别人不允许进行插队操作
持久性:A转出100元且B收到100元后,整个过程结束,并且永久生效

3.数据库中事务引起的常见问题

脏读 事务A读到了事务B未提交的数据
不可重复读: 事务A第一次查询得到一行记录row1,事务B提交修改后,事务A第二次查询得到row1,但列内容发生了变化。
幻读 事务A第一次查询得到一行记录row1,事务B提交修改后,事务A第二次查询得到两行记录row1和row2

4.数据库中常见的隔离级别

读未提交:A事务可以读取到其他事务未提交的数据

读已提交(Oracle默认隔离级别):A事务只能读取其他事务已经提交的数据

可重复读(MySQL默认隔离级别): A事务中前后2次读取某数据,都是一致

串行化:多个事务按照顺序执行,后访问的事务必须等前一个事务执行完成,才能继续执行

5.数据库隔离级别对事务问题的处理情况

隔离级别 脏读 不可重复读 幻读
读未提交 × × ×
读已提交 × ×
可重复读
串行化

以上隔离级别从上到下,解决的问题逐渐变多,但性能却越来越低

6.事务问题演示

6.1 演示前小知识

查询MySQL版本号:

select version(); // 此处我的演示版本为:5.7.33-log

查询当前数据库的隔离级别

select @@global.tx_isolation; // 适用于MySQL5.6 及其更早的版本
select @@global.transaction_isolation; // 适用于MySQL5.7 及更高版本

注意:
1、MySQL5.7 引入了 transaction_isolation 用来代替 tx_isolation,并在 MySQL8.0.3
去掉了 tx_isolation,在 MySQL5.7 及更高版本中建议使用 transaction_isolation
2、若要查看当前会话的事务隔离级别,可以去掉 global. 使用 SELECT
@@transaction_isolation。同理,若只想针对当前 session 设置事务隔离级别,可将 global 关键字替换为
session

设置当前数据库隔离级别

MySQL5.6 及其更早的版本
set global tx_isolation=‘read-uncommitted’;
set global tx_isolation=‘read-committed’;
set global tx_isolation=‘repeatable-read’;
set global tx_isolation=‘serializable’;
MySQL5.7 及更高版本
set global transaction_isolation=‘read-uncommitted’;
set global transaction_isolation=‘read-committed’;
set global transaction_isolation=‘repeatable-read’;
set global transaction_isolation=‘serializable’;

查看MySQL中是否开启自动提交事务

SHOW variables like “autocommit”; //ON为打开 OFF为关闭

全局关闭事务自动提交

SET GLOBAL autocommit = 0|1; // 0-代表关闭,1-代表打开 (去掉GLOBAL代表为会话级)

手动开启事务

begin或者start transaction

6.2 演示脏读情况

第一步:将数据库隔离级别设置为读未提交

set global transaction_isolation=‘read-uncommitted’;

第二步,开启事务A,新增一条数据但不提交
MySQL事务相关详解(一篇足够了)
第四步:在事务B中查询,发现出现了事务A未提交的数据(这就是脏读)
MySQL事务相关详解(一篇足够了)
第五步:回滚A事务
MySQL事务相关详解(一篇足够了)
第六步:在重新查询B事务,发现之前查询出的数据没了
MySQL事务相关详解(一篇足够了)

6.3 演示不可重复读情况

第一步:将数据库隔离级别设置为读未提交

set global transaction_isolation=‘read-uncommitted’;

第二步:执行事务A,并查询满足条件的数据
MySQL事务相关详解(一篇足够了)
第三步:执行事务B,修改其中数据内容并提交(将田七的生物成绩改成了100分)
MySQL事务相关详解(一篇足够了)
第四步:执行事务A,发现事务A中数据发生了改变,同一个事务中,2次读取数据不一致
MySQL事务相关详解(一篇足够了)

6.3 演示幻读情况

第一步:将数据库隔离级别设置为读未提交

set global transaction_isolation=‘read-uncommitted’;

第二步:开启事务A并执行查询
MySQL事务相关详解(一篇足够了)
第三步:开始事务B,插入新的数据并提交
MySQL事务相关详解(一篇足够了)
第四步: 重新查询事务A,发现同一事务中前后2次相同查询,数据变多了(这就是幻读)
MySQL事务相关详解(一篇足够了)

7.MySQL如何解决幻读

通过上面的讲解,大家都知道什么叫幻读了,MySQL的默认隔离级别就已经解决了幻读的情况,下面大家一起看看如何解决幻读的。
提示:首先我们需要了解MySQL中查询语句的种类,因为不同种类的查询语句的解决方式是不一样的哟!!!!

7.1 查询语句的分类

快照读

因为MySQL默认的隔离级别是可重复读,这种隔离级别下,我们普通的SELECT语句都是快照读,也就是在一个事务内,多次执行SELECT语句,查询到的数据都是事务开始时那个状态的数据(这样就不会受其他事务修改数据的影响),这样就解决了幻读的问题。
提示:简单的select操作,属于快照读,不加锁。((当然,也有例外)

当前读

如果说快照读总是读取事务开始时那个状态的数据,那么当前读就是查询时总是执行这个查询时数据库中的数据。
提示:特殊的读操作,插入/更新/删除操作,属于当前读,需要加锁。
例如:
select * from table where ? for update;
select * from table where ? lock in share mode;
insert into table values (…);
update table set ? where ?;
delete from table where ?;

7.2 不同种类查询如何避免

快照读:mysql通过mvcc来避免幻读。
当前读:mysql通过next-key锁来避免幻读

7.3 什么是MVCC

mvcc概念

mvcc全称是multi version concurrent control(多版本并发控制)。mysql把每个操作都定义成一个事务,每开启一个事务,系统的事务版本号自动递增。每行记录都有两个隐藏列:创建版本号和删除版本号。

不同语句中版本号的处理场景

select:事务每次只能读到创建版本号小于等于此次系统版本号的记录,同时行的删除版本号不存在或者大于当前事务的版本号。
update:插入一条新记录,并把当前系统版本号作为行记录的版本号,同时保存当前系统版本号 到原有的行作为删除版本号。
delete:把当前系统版本号作为行记录的删除版本号
insert:把当前系统版本号作为行记录的版本号

7.7 什么是next-key锁

next-key锁其实包含行锁和间隙锁:
行锁:InnoDB行锁是通过给索引上的索引项加锁来实现的,因此只有通过索引条件检索数据,InnoDB才使用行级锁
间隙锁:

上一篇:移动开发作业1


下一篇:loadrunner简单使用——HTTP,WebService,Socket压力测试脚本编写