Mysql事务

事务定义

一个事务会涉及大量的cpu操做和IO操做,这些操做会被打包成一个执行单元,要么同时完成,要么同时都不完成。面试

事务是一组原子性的sql命令或者说是一个独立的工做单元,若是其中任何一条sql语句由于崩溃或者其余缘由执行失败,那么该组全部的sql语句都不会执行。若是没有显示启动事务,数据库会根据autocommit的值,默认每条sql操做都会自动提交。sql

事务的特性ACID

原子性(A)

一个事务中的全部操做,要么都完成,要么都不执行,对于一个事务来讲,不可能只执行其中的一部分。数据库

一致性(C)

数据库老是从一个一致性的状态转换到另外一个一致性的状态安全

隔离性(I)

一个事务所作的修改在最终提交以前,对其余事务是不可见的。多个事务之间的操做相互不影响。每下降一个事务的隔离级别都能提升数据库的并发,但同时不安全性就增长了。关于事务的隔离级别后续还要详细讨论。这里简单介绍一下:bash

  • 读未提交:其余事务未提交就能够读。
  • 读已提交:其余事务只有提交了才能读。
  • 可重复读:只管本身启动事务时候的状态,不接受其余事务的影响。
  • 串行化:按照顺序提交事务保证了数据的安全性,但没法实现并发。

持久性(D)

事务一旦提交,就要更新到数据库中,不能回滚。就算服务器宕机,仍然须要在下次启动的时候自动执行事务中的sql命令,体现到数据库中。服务器

数据库事务隔离界别

介绍

隔离界别 脏读 不可重复读 幻读
未提交读(Read uncommited) 可能 可能 可能
已提交读(Read commited) 不可能 可能 可能
可重复读(Repeatable read) 不可能 不可能 可能
串行化(Serializable) 不可能 不可能 不可能
  • 未提交读(Read uncommited):容许脏读,也就是可能读取到其余会话中未提交事务修改的数据。
  • 已提交读(Read commited):只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别。
  • 可重复读(Repeatable read):可重复读。在同一事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,改级别消除了不可重复读,但仍是存在幻象读,但InnoDB解决了幻读。
  • 串行化:彻底串行化的读,每次读都须要得到表级共享锁,读写相互都会阻塞。

准备数据

准备一个帐户表,表里三个字段,分别是主键id,帐户名和金额session

create table account(
	id int(11) not null auto_increment,
	customer_name varchar(255) not null,
	money decimal(10,2) not null,
	primary key(id),
	unique uni_name using btree (customer_name)
) ENGINE = 'InnoDB' AUTO_INCREMENT=10 COMMENT = '帐户表';
复制代码

验证RU(未提交读)

一、开启两个会话,设置隔离级别为read uncommited并发

set session transaction isolation level read uncommitted;

select @@session.tx_isolation;
复制代码


二、会话1开启事务

三、会话2开启事务并插入一条数据

四、会话1开始查询数据库,查到了会话2事务未提交的数据

五、会话2若是此时回滚了,会话1就查不到了

小结:在RU模式下,一个事务能够读取到另外一个未提交事务的数据,致使了脏读。若是另外一个事务回滚了,就会形成数据的不一致性。RU是事务隔离级别中最低的。

RC(读提交)

一、将会话1和会话2的隔离级别设置成读提交模式性能

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

二、会话1和会话2都开启事务,会话1查询当前数据

三、会话2更新数据但未提交,会话1查询当前数据仍是没改

四、可是会话2此时事务提交,会话1查询当前数据变了

小结:在RC模式下,咱们发现,当另外一个事务没有提交数据修改时,当前事务时读不到修改后的数据的,这就避免了读未提交模式的脏读。但有一个问题,在当前事务中,两次select的数据不同,这就存在了不可重复读的问题。PS:RC隔离级别是Oracle数据库默认的隔离级别。ui

RR(可重复读)

一、将会话1和会话2的隔离级别都设置成可重复读

set session transaction isolation level repeatable read;
复制代码

二、会话1开启事务并查询当前数据,会话2开启事务并更新数据

三、此时会话1查询数据仍是以前的数据

四、而后会话2提交事务,会话1查询数据仍是以前的数据,这表示会话2的更改并无影响当前的事务,能够重复读取。

五、此时会话1提交当前事务,并再次读取数据,发现其余事务改了数据

小结:在RR模式下,咱们解决了不可重复读的问题,即在这种隔离级别下,一个事务中咱们可以保证获取同样的数据(即便有其余事务正在改当前的数据行)。可是没法避免幻读, 幻读简单的解释就是在数据有新增的时候,也没法保证两次获得的数据不一致可是不一样数据库对不一样的RR级别有不一样的实现,有时候加上间隙锁来避免幻读。

InnoDB解决了幻读

结论

首先说结论:在RR的隔离级别下,InnoDB使用MVCC和next-key locks(间隙锁)解决幻读。MVCC解决的是普通读(快照读)的幻读,间隙锁解决的是当前读状况下的幻读。

幻读是什么

(首先声明:在RR模式下,引擎是InnoDB,没法产生如下效果。)
一、事务1先执行,但未提交

start transaction;
update account set account=1000 where id>10;
复制代码

结果为:OK row xx表名成功影响多少行 二、事务2后执行,而且提交

start transaction;
insert into account(customer_name, money) values('李云龙',10000);
commit;
复制代码

三、事务1再select一下,结果集为:

select * from account;
复制代码

··· 李云龙,10000
这时,事务1蒙了,不是已经将id>10的全部人的金额都更新成1000了吗,怎么多出一我的金额是10000?这是已提交事务2对事务1产生的影响,这个影响就叫作幻读。

幻读和不可重复读的区别

不可重复读:屡次读取一条记录,发现该记录中某些列值被修改过。
幻读:只要是说屡次读取一个范围内的记录(包括直接查询全部记录结果或者作聚合统计),发现结果不一致(通常指的是记录增多,记录的减小应该也算是幻读)。

怎么解决幻读

当前读
所谓当前读,指的是加锁的select(S锁或者X锁),update或者delete语句。在RR的事务隔离级别下,数据库会使用间隙锁来锁住本条记录以及索引区间。例如:
select * from account where id>10 for update;锁住的就是id=10的记录以及id>10的这个区间范围,避免范围间插入记录,以免产生幻影行记录。
普通读
由于普通读是不会加锁的读,因此解决幻读的手段是MVCC。MVCC会给每行元祖加一些辅助字段,记录建立版本号和删除版本号。在每个事务启动的时候,都有一个惟一的递增的版本号。每开启一个新事务,事务的版本号就会递增。MYSQL默认的隔离级别(可重复读Repeatable Read)下,增删改查就变成下面这样:

  • select:读取建立版本小于或等于当前事务版本号,而且删除版本为空或大于当前事务版本号的记录。这样能够保证在读取以前记录是存在的。
  • insert:将当前事务的版本号保存至行的建立版本号。
  • update:新插入一行,并以当前事务版本号做为新行的建立版本号,同时将原纪录行的删除版本号设置为当前事务版本号。
  • delete:将当前事务版本号保存至行的删除版本号。 有点绕,总结下来就是每行多了两个版本号,一个建立版本号和一个删除版本号,都是保存事务的版本号。下面举个例子理解一下: 例如我插入一条记录,事务id=1,那么记录以下:
id name createversion deleteversion
1 马云 1

若是我把name更新成刘强东,事务id=2,那么记录就变成

id name createversion deleteversion
1 马云 1 2
2 刘强东 2

=> 能够看出,原来的元祖的deleteversion为更新的这个事务的id,而且新增一条
若是删除的话,假设事务id=3,删除id=2的记录

id name createversion deleteversion
1 马云 1 2
2 刘强东 2 3

关键点来了,若是我如今读取的话,须要同时知足两个条件:
一、读取建立版本小于或等于当前事务的版本号,这意味着数据在这个事务以前被建立
二、删除版本为空或者大于当前事务版本号的记录。这意味着删除操做在这个事务以后发生
因此当事务1插入一条记录,事物2更新这条记录并删除这条记录以后,事务1再次查询这条记录,只有id=1的记录知足这两个条件,因此结果为:(避免了幻读)

id name createversion deleteversion
1 马云 1 2

串行化

全部事务串行执行,是最高的隔离级别,性能最差

总结

这篇文章就到这了,总的来讲数据库事务的四个特性ACID和四种隔离级别(RU,RC,RR,串行化)以及会产生的问题(脏读、不可重复读、幻读)是面试的高频问点,什么东西都须要积累,而后反复的温习,这样才能记忆和理解的更深入。

相关文章
相关标签/搜索