龙空技术网

mysql高频面试难点之事务深入分析

梦幻毕加索 227

前言:

目前朋友们对“mysql手动事务”可能比较看重,同学们都需要分析一些“mysql手动事务”的相关内容。那么小编同时在网摘上汇集了一些有关“mysql手动事务””的相关资讯,希望你们能喜欢,兄弟们快快来了解一下吧!

事务是数据库操作的一个基本单位,它由一系列的数据库操作组成,这些操作要么全部成功执行,要么全部失败回滚。

一、事务隔离级别脏写(Dirty Write)

一个事务修改了另一个未提交事务修改过的数据

脏读(Dirty Read)

果一个事务读到了另一个未提交事务修改过的数据

不可重复读(Non-Repeatable Read)

一个事务只能读到另一个已经提交的事务修改过的数据,并且其他事务每对该数据进行一次修改并提交后,该事务都能查询得到最新值

幻读(Phantom)

一个事务先根据某些条件查询出一些记录,之后另一个事务又向表中插入了符合这些条件的记录,原先的事务再次按照该条件查询时,能把另一个事务插入的记录也读出来

二、事务隔离级别设置

MySQL 的默认隔离级别REPEATABLE READ ,我们可以手动修改一下事务的隔离级别。

我们可以通过下边的语句修改事务的隔离级别;

SET [GLOBAL|SESSION] TRANSACTION ISOLATION LEVEL level;

设置事务的隔离级别的语句中,在SET关键字后可以放置GLOBAL关键字、 SESSION关键字或者什么都不放,会对不同范围的事务产生不同的影响,具体如下:

2.1 GLOBAL关键字(在全局范围影响):

比方说这样:

SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE; 则:

只对执行完该语句之后产生的会话起作用。 当前已经存在的会话无效。

2.2 SESSION关键字(在会话范围影响):

比方说这样:

SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE; 则:

对当前会话的所有后续的事务有效 。该语句在已经开启的事务中间执行,但不会影响

如果在事务之间执行,则对后续的事务有效。

2.3 上述两个关键字都不用(只对执行语句后的下一个事务产生影响):

比方说这样:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; 则:

只对当前会话中下一个即将开启的事务有效。 下一个事务执行完后,后续事务将恢复到之前的隔离级别。 该语句不能在已经开启的事务中间执行,会报错的。

我们在服务器启动时想改变事务的默认隔离级别,可以修改启动参数 transaction-isolation 的值,比方说我们在启动服务器时指定了 --transaction-isolation=SERIALIZABLE ,那么事务的默认隔离级别就从原来的 REPEATABLE READ 变成了 SERIALIZABLE 。

三、不同隔离级别的问题READ UNCOMMITTED隔离级别下,可能发生脏读、不可重复读和幻读问题。READ COMMITTED隔离级别下,可能发生不可重复读和幻读问题,但是不可以发生脏读问题。REPEATABLE READ隔离级别下,可能发生幻读问题,但是不可以发生脏读和不可重复读的问题。SERIALIZABLE 隔离级别下,各种问题都不可以发生。四、事务难点之幻读分析

大家可能听到过很多说法是 RR 隔离级别无法解决幻读的问题,但事实上在InnoDB中,如果事务中只存在当前读或快照读,是不会出现幻读的问题,如果当前读和快照读一起存在,才会出现幻读问题。看下面示例:

4.1、当前读和快照读只有一种

在一个只有快照读的事务中多次读取同一个范围内的数据获取的结果集不同。比如 select count(*) from user where age>10,第一次读取10条数据,第二次读取 11 条数据,因为两次读取的时间差上有事务插入了数据,并提交了;这种情况在 innodb 可重复读隔离级别中不会发生

4.2、当前读和快照读都有

select 操作得到的结果集所表征的数据无法支撑后续的业务操作。比如:select 一条记录不存在,准备插入此记录,但执行 insert 的时候发现此记录已经存在,无法插入,如同产生了幻觉。再比如:select 一个范围的数据,发现有 2 条,更新的时候发现更新了 3 条,再次进行范围查询,发现有3 条数据。

MySQL InnoDB 引擎的默认隔离级别虽然是「可重复读」,它很大程度上避免幻读现象(并不是完全解决了),解决的方案有两种:

针对快照读(普通 select 语句),是通过 MVCC 方式解决了幻读,因为可重复读隔离级别下,事务执行过程中看到的数据,一直跟这个事务中第一次查询到的数据是一致的,即使中途有其他事务插入了一条数据,是查询不出来这条数据的,所以就很好了避免幻读问题。针对当前读(select ... for update 等语句),是通过 next-key lock(记录锁+间隙锁)方式解决了幻读,因为当执行 select ... for update 语句的时候,会加上 next-key lock,如果有其他事务在 next-key lock 锁范围内插入了一条记录,那么这个插入语句就会被阻塞,无法成功插入,所以就很好了避免幻读问题。

这两个解决方案是很大程度上解决了幻读现象,但是还是有个别的情况造成的幻读现象是无法解决的。

4.3、临键锁与幻读

官方文档有这么一条说明:By default, InnoDB operates in REPEATABLE READ transaction isolation level. In this case, InnoDB uses next-key locks for searches and index scans, which prevents phantom rows.

大意是:MySQL 默认隔离级别是RR,在这种级别下,如果你使用 select in share mode 或者 select for update 语句,那么InnoDB会使用临键锁(记录锁 + 间隙锁),因而可以防止幻读。所以,MySQL默认隔离级别下可以解决大部分的幻读。

4.4、快照读和当前读对比4.4.1、快照读生成时机

只有“快照读”的sql语句,才会生成快照,比如不加锁的select语句;而“当前读”的sql语句,是不会生成快照的,比如:

update

select … for update

select … lock in share mode等

4.4.2、readview生成时机RC隔离级别:每次读取数据前,都生成一个readview;RR隔离级别:在第一次读取数据前,生成一个readview;

所以快照读并不是在事务begin后就生成,而是在第一条“快照读”语句后才生成。

五、事务核心之sql分析5.1 select

select语法非常简单,本文就给大家说一下在事务处理中使用select应该注意的地方。

5.1.1隔离级别是读已提交

当前事务的更改,即便是未提交的,select也是可见的。除了当前事务之外,select只能看到已提交的事务所做出的更改。往往会出现这种情况:同一个事务的两个相同的select语句,但是执行顺序不同,也可能会返回不同的结果。两个select语句执行间隔的时间里,可能有其他的事务提交,数据被修改了。

5.1.2隔离级别是可重复读

事务只能看到它开始前已提交的数据。但是select能看到本事务未提交的数据,不能看到被其它事务更新的已提交的数据。

5.2 updata

讲完select,接下来介绍一下updata语句。

5.2.1 隔离级别是读已提交

如果事务要更新一条记录,而这条记录恰好被另一个运行中但未提交事务更改(被锁定或删除),则当前事务会阻塞,等待直到另一个事务提交或回滚后,再继续处理(First Updater Win Rule)。

如果另一个事务回滚了,那么当前事务可以继续执行,更新这条记录。

如果另一个事务提交了,要分两种情形。第一种,要是这条记录被删除了,那么忽略这条记录;第二种,这条记录被更新了,需要重新判断这条记录是否满足谓词条件(where语句),满足则更新,不满足则忽略这条记录。

5.2.2隔离级别是可重复读

事务只能看到事务开始前已提交的数据。所以,对于并发的更新操作,与读已提交是类似的。如果事务更新同一条记录,当前事务会阻塞直到另一个并发写的事务结束。如果另一个事务回滚,那么当前事务继续执行,更新这条记录。

如果另一个事务回滚了,那么当前事务可以继续执行,更新这条记录。

如果另一个事务提交了,要分两种情形。第一种,要是这条记录被删除了,那么忽略这条记录;第二种,这条记录被更新了,需要重新判断这条记录是否满足谓词条件(where语句),满足则更新,不满足则忽略这条记录。

六、事务关键之trxid分配6.1事务id分配策略

服务器会在内存中维护一个全局变量,每当需要为某个事务分配一个 事务id 时,就会把该变量的值当作 事务id 分配给该事务,并且把该变量自增1。每当这个变量的值为 256 的倍数时,就会将该变量的值刷新到系统表空间的页号为 5 的页面中一个称之为Max TrxID的属性处,这个属性占用 8 个字节的存储空间。当系统下一次重新启动时,会将上边提到的 Max Trx ID 属性加载到内存中,将该值加上256之后赋值给我们前边提到的全局变量(因为在上次关机时该全局变量的值可能大于 Max Trx ID 属性值)。

这样就可以保证整个系统中分配的 事务id 值是一个递增的数字。先被分配 id 的事务得到的是较小的 事务id ,后被分配 id 的事务得到的是较大的 事务id。

6.2 事务id分配时机

对于只读事务来说,只有在它第一次对某个用户创建的临时表执行增、删、改操作时才会为这个事务分配一个 事务id ,否则的话是不分配 事务id 的。

对于读写事务来说,只有在它第一次对某个表(包括用户创建的临时表)执行增、删、改操作时才会为这个事务分配一个 事务id ,否则的话也是不分配 事务id 的。

七、Mysql事务实现原理原子性:使用 undo log ,从而达到回滚持久性:使用 redo log,从而达到故障后恢复隔离性:使用锁以及MVCC,运用的优化思想有读写分离,读读并行,读写并行一致性:通过回滚,以及恢复,和在并发环境下的隔离做到一致性。

标签: #mysql手动事务